blob: 75487267ba8d2a99266361097b73762699c46a5c [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
Eric Andersen87590061999-10-18 21:22:59 +00002/*
3 * Mini swapon/swapoff implementation for busybox
4 *
Eric Andersenc7bda1c2004-03-15 08:29:22 +00005 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
Eric Andersen87590061999-10-18 21:22:59 +00006 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02007 * Licensed under GPLv2, see file LICENSE in this source tree.
Eric Andersen87590061999-10-18 21:22:59 +00008 */
9
Pere Orga5bc8c002011-04-11 03:29:49 +020010//usage:#define swapon_trivial_usage
Matt Whitlock0a53b202014-03-22 19:21:01 -040011//usage: "[-a]" IF_FEATURE_SWAPON_DISCARD(" [-d[POL]]") IF_FEATURE_SWAPON_PRI(" [-p PRI]") " [DEVICE]"
Pere Orga5bc8c002011-04-11 03:29:49 +020012//usage:#define swapon_full_usage "\n\n"
13//usage: "Start swapping on DEVICE\n"
Pere Orga5bc8c002011-04-11 03:29:49 +020014//usage: "\n -a Start swapping on all swap devices"
Matt Whitlock0a53b202014-03-22 19:21:01 -040015//usage: IF_FEATURE_SWAPON_DISCARD(
16//usage: "\n -d[POL] Discard blocks at swapon (POL=once),"
17//usage: "\n as freed (POL=pages), or both (POL omitted)"
18//usage: )
Pere Orga5bc8c002011-04-11 03:29:49 +020019//usage: IF_FEATURE_SWAPON_PRI(
20//usage: "\n -p PRI Set swap device priority"
21//usage: )
22//usage:
23//usage:#define swapoff_trivial_usage
24//usage: "[-a] [DEVICE]"
25//usage:#define swapoff_full_usage "\n\n"
26//usage: "Stop swapping on DEVICE\n"
Pere Orga5bc8c002011-04-11 03:29:49 +020027//usage: "\n -a Stop swapping on all swap devices"
28
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000029#include "libbb.h"
Eric Andersen87590061999-10-18 21:22:59 +000030#include <mntent.h>
Denys Vlasenko14bd16a2011-07-08 08:49:40 +020031#ifndef __BIONIC__
32# include <sys/swap.h>
33#endif
Eric Andersene76c3b02001-04-05 03:14:39 +000034
Natanael Copa9aff2992009-09-20 04:28:22 +020035#if ENABLE_FEATURE_MOUNT_LABEL
36# include "volume_id.h"
37#else
38# define resolve_mount_spec(fsname) ((void)0)
39#endif
40
Denys Vlasenko14bd16a2011-07-08 08:49:40 +020041#ifndef MNTTYPE_SWAP
42# define MNTTYPE_SWAP "swap"
43#endif
44
Matt Whitlock0a53b202014-03-22 19:21:01 -040045#if ENABLE_FEATURE_SWAPON_DISCARD
46#ifndef SWAP_FLAG_DISCARD
47#define SWAP_FLAG_DISCARD 0x10000
48#endif
49#ifndef SWAP_FLAG_DISCARD_ONCE
50#define SWAP_FLAG_DISCARD_ONCE 0x20000
51#endif
52#ifndef SWAP_FLAG_DISCARD_PAGES
53#define SWAP_FLAG_DISCARD_PAGES 0x40000
54#endif
55#define SWAP_FLAG_DISCARD_MASK \
56 (SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE | SWAP_FLAG_DISCARD_PAGES)
57#endif
58
59
60#if ENABLE_FEATURE_SWAPON_DISCARD || ENABLE_FEATURE_SWAPON_PRI
Denis Vlasenkoee56e012008-05-18 23:05:34 +000061struct globals {
62 int flags;
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +010063} FIX_ALIASING;
Denis Vlasenkoee56e012008-05-18 23:05:34 +000064#define G (*(struct globals*)&bb_common_bufsiz1)
65#define g_flags (G.flags)
Tito Ragusaa3f326c2014-03-31 16:39:26 +020066#define save_g_flags() int save_g_flags = g_flags
67#define restore_g_flags() g_flags = save_g_flags
Denis Vlasenkoee56e012008-05-18 23:05:34 +000068#else
69#define g_flags 0
Tito Ragusaa3f326c2014-03-31 16:39:26 +020070#define save_g_flags() ((void)0)
71#define restore_g_flags() ((void)0)
Denis Vlasenkoee56e012008-05-18 23:05:34 +000072#endif
Denys Vlasenko16714242011-09-21 01:59:15 +020073#define INIT_G() do { } while (0)
Denis Vlasenkoee56e012008-05-18 23:05:34 +000074
Tito Ragusaa3f326c2014-03-31 16:39:26 +020075#define do_swapoff (applet_name[5] == 'f')
76
77/* Command line options */
78enum {
79 OPTBIT_a, /* -a all */
80 IF_FEATURE_SWAPON_DISCARD( OPTBIT_d ,) /* -d discard */
81 IF_FEATURE_SWAPON_PRI ( OPTBIT_p ,) /* -p priority */
82 OPT_a = 1 << OPTBIT_a,
83 OPT_d = IF_FEATURE_SWAPON_DISCARD((1 << OPTBIT_d)) + 0,
84 OPT_p = IF_FEATURE_SWAPON_PRI ((1 << OPTBIT_p)) + 0,
85};
86
87#define OPT_ALL (option_mask32 & OPT_a)
88#define OPT_DISCARD (option_mask32 & OPT_d)
89#define OPT_PRIO (option_mask32 & OPT_p)
90
Rob Landley20cc6d52006-09-12 21:42:17 +000091static int swap_enable_disable(char *device)
Eric Andersen87590061999-10-18 21:22:59 +000092{
Tito Ragusaa3f326c2014-03-31 16:39:26 +020093 int err = 0;
94 int quiet = 0;
Eric Andersen97b141a2002-11-03 00:25:23 +000095 struct stat st;
96
Natanael Copa9aff2992009-09-20 04:28:22 +020097 resolve_mount_spec(&device);
Eric Andersen97b141a2002-11-03 00:25:23 +000098
Tito Ragusaa3f326c2014-03-31 16:39:26 +020099 if (do_swapoff) {
100 err = swapoff(device);
101 /* Don't complain on OPT_ALL if not a swap device or if it doesn't exist */
102 quiet = (OPT_ALL && (errno == EINVAL || errno == ENOENT));
103 } else {
104 /* swapon */
105 err = stat(device, &st);
106 if (!err) {
107 if (ENABLE_DESKTOP && S_ISREG(st.st_mode)) {
108 if (st.st_blocks * (off_t)512 < st.st_size) {
109 bb_error_msg("%s: file has holes", device);
110 return 1;
111 }
112 }
113 err = swapon(device, g_flags);
114 /* Don't complain on swapon -a if device is already in use */
115 quiet = (OPT_ALL && errno == EBUSY);
116 }
Eric Andersendb1df5e2002-10-26 10:27:42 +0000117 }
Mike Frysinger6943a942005-09-13 02:29:39 +0000118
Tito Ragusac9a67132014-04-01 09:51:27 +0200119 if (err && !quiet) {
120 bb_simple_perror_msg(device);
Tito Ragusaa3f326c2014-03-31 16:39:26 +0200121 return 1;
122 }
Mike Frysinger2d5e4f62005-09-16 04:41:20 +0000123 return 0;
Eric Andersen87590061999-10-18 21:22:59 +0000124}
125
Tito Ragusaa3f326c2014-03-31 16:39:26 +0200126#if ENABLE_FEATURE_SWAPON_DISCARD
127static void set_discard_flag(char *s)
Eric Andersen87590061999-10-18 21:22:59 +0000128{
Tito Ragusaa3f326c2014-03-31 16:39:26 +0200129 /* Unset the flag first to allow fstab options to override */
130 /* options set on the command line */
131 g_flags = (g_flags & ~SWAP_FLAG_DISCARD_MASK) | SWAP_FLAG_DISCARD;
132
133 if (!s) /* No optional policy value on the commandline */
134 return;
135 /* Skip prepended '=' */
136 if (*s == '=')
137 s++;
138 /* For fstab parsing: remove other appended options */
139 *strchrnul(s, ',') = '\0';
140
141 if (strcmp(s, "once") == 0)
142 g_flags |= SWAP_FLAG_DISCARD_ONCE;
143 if (strcmp(s, "pages") == 0)
144 g_flags |= SWAP_FLAG_DISCARD_PAGES;
145}
146#else
147#define set_discard_flag(s) ((void)0)
Matt Whitlockb9bbd4d2014-03-22 19:10:08 -0400148#endif
Eric Andersen87590061999-10-18 21:22:59 +0000149
Tito Ragusaa3f326c2014-03-31 16:39:26 +0200150#if ENABLE_FEATURE_SWAPON_PRI
151static void set_priority_flag(char *s)
152{
153 unsigned prio;
Mike Frysinger6943a942005-09-13 02:29:39 +0000154
Tito Ragusaa3f326c2014-03-31 16:39:26 +0200155 /* For fstab parsing: remove other appended options */
156 *strchrnul(s, ',') = '\0';
157 /* Max allowed 32767 (== SWAP_FLAG_PRIO_MASK) */
158 prio = bb_strtou(s, NULL, 10);
159 if (!errno) {
160 /* Unset the flag first to allow fstab options to override */
161 /* options set on the command line */
162 g_flags = (g_flags & ~SWAP_FLAG_PRIO_MASK) | SWAP_FLAG_PREFER |
163 MIN(prio, SWAP_FLAG_PRIO_MASK);
164 }
165}
166#else
167#define set_priority_flag(s) ((void)0)
168#endif
169
170static int do_em_all_in_fstab(void)
171{
172 struct mntent *m;
173 int err = 0;
174 FILE *f = xfopen_for_read("/etc/fstab");
175
Lauri Kasanend2844fc2010-04-29 22:20:57 +0200176 while ((m = getmntent(f)) != NULL) {
177 if (strcmp(m->mnt_type, MNTTYPE_SWAP) == 0) {
178 /* swapon -a should ignore entries with noauto,
Tito Ragusaa3f326c2014-03-31 16:39:26 +0200179 * but swapoff -a should process them
180 */
181 if (do_swapoff || hasmntopt(m, MNTOPT_NOAUTO) == NULL) {
182 /* each swap space might have different flags */
183 /* save global flags for the next round */
184 save_g_flags();
185 if (ENABLE_FEATURE_SWAPON_DISCARD) {
186 char *p = hasmntopt(m, "discard");
187 if (p) {
188 /* move to '=' or to end of string */
189 p += 7;
190 set_discard_flag(p);
Tito Ragusa8c7fcbd2013-08-08 10:21:27 +0200191 }
192 }
Tito Ragusaa3f326c2014-03-31 16:39:26 +0200193 if (ENABLE_FEATURE_SWAPON_PRI) {
194 char *p = hasmntopt(m, "pri");
195 if (p) {
196 set_priority_flag(p + 4);
197 }
198 }
199 err |= swap_enable_disable(m->mnt_fsname);
200 restore_g_flags();
Lauri Kasanend2844fc2010-04-29 22:20:57 +0200201 }
202 }
203 }
Mike Frysinger6943a942005-09-13 02:29:39 +0000204
Lauri Kasanend2844fc2010-04-29 22:20:57 +0200205 if (ENABLE_FEATURE_CLEAN_UP)
206 endmntent(f);
Mike Frysinger6943a942005-09-13 02:29:39 +0000207
Eric Andersendb1df5e2002-10-26 10:27:42 +0000208 return err;
Eric Andersen87590061999-10-18 21:22:59 +0000209}
210
Tito Ragusaa3f326c2014-03-31 16:39:26 +0200211static int do_all_in_proc_swaps(void)
212{
213 char *line;
214 int err = 0;
215 FILE *f = fopen_for_read("/proc/swaps");
216 /* Don't complain if missing */
217 if (f) {
218 while ((line = xmalloc_fgetline(f)) != NULL) {
219 if (line[0] == '/') {
220 *strchrnul(line, ' ') = '\0';
221 err |= swap_enable_disable(line);
222 }
223 free(line);
224 }
225 if (ENABLE_FEATURE_CLEAN_UP)
226 fclose(f);
227 }
228
229 return err;
230}
231
232#define OPTSTR_SWAPON "a" \
233 IF_FEATURE_SWAPON_DISCARD("d::") \
234 IF_FEATURE_SWAPON_PRI("p:")
235
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000236int swap_on_off_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000237int swap_on_off_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen87590061999-10-18 21:22:59 +0000238{
Tito Ragusaa3f326c2014-03-31 16:39:26 +0200239 IF_FEATURE_SWAPON_PRI(char *prio;)
240 IF_FEATURE_SWAPON_DISCARD(char *discard = NULL;)
241 int ret = 0;
Mike Frysinger6943a942005-09-13 02:29:39 +0000242
Denys Vlasenko16714242011-09-21 01:59:15 +0200243 INIT_G();
244
Tito Ragusaa3f326c2014-03-31 16:39:26 +0200245 getopt32(argv, do_swapoff ? "a" : OPTSTR_SWAPON
246 IF_FEATURE_SWAPON_DISCARD(, &discard)
247 IF_FEATURE_SWAPON_PRI(, &prio)
248 );
Mike Frysinger6943a942005-09-13 02:29:39 +0000249
Denis Vlasenkoee56e012008-05-18 23:05:34 +0000250 argv += optind;
Tito Ragusaa3f326c2014-03-31 16:39:26 +0200251
252 if (OPT_DISCARD) {
253 set_discard_flag(discard);
254 }
255 if (OPT_PRIO) {
256 set_priority_flag(prio);
257 }
258
259 if (OPT_ALL) {
260 /* swapoff -a does also /proc/swaps */
261 if (do_swapoff)
262 ret = do_all_in_proc_swaps();
263 ret |= do_em_all_in_fstab();
264 } else if (!*argv) {
265 /* if not -a we need at least one arg */
Denis Vlasenkoee56e012008-05-18 23:05:34 +0000266 bb_show_usage();
Tito Ragusaa3f326c2014-03-31 16:39:26 +0200267 }
268 /* Unset -a now to allow for more messages in swap_enable_disable */
269 option_mask32 = option_mask32 & ~OPT_a;
270 /* Now process devices on the commandline if any */
271 while (*argv) {
272 ret |= swap_enable_disable(*argv++);
273 }
Mike Frysinger2d5e4f62005-09-16 04:41:20 +0000274 return ret;
Eric Andersen87590061999-10-18 21:22:59 +0000275}