blob: 2a9b74dc02bb1b18f743563d66aaf294d6bff7d7
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * Mini swapon/swapoff implementation for busybox |
4 | * |
5 | * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> |
6 | * |
7 | * Licensed under GPLv2, see file LICENSE in this source tree. |
8 | */ |
9 | //config:config SWAPON |
10 | //config: bool "swapon" |
11 | //config: default y |
12 | //config: select PLATFORM_LINUX |
13 | //config: help |
14 | //config: This option enables the 'swapon' utility. |
15 | //config: Once you have created some swap space using 'mkswap', you also need |
16 | //config: to enable your swap space with the 'swapon' utility. The 'swapoff' |
17 | //config: utility is used, typically at system shutdown, to disable any swap |
18 | //config: space. If you are not using any swap space, you can leave this |
19 | //config: option disabled. |
20 | //config: |
21 | //config:config FEATURE_SWAPON_DISCARD |
22 | //config: bool "Support discard option -d" |
23 | //config: default y |
24 | //config: depends on SWAPON |
25 | //config: help |
26 | //config: Enable support for discarding swap area blocks at swapon and/or as |
27 | //config: the kernel frees them. This option enables both the -d option on |
28 | //config: 'swapon' and the 'discard' option for swap entries in /etc/fstab. |
29 | //config: |
30 | //config:config FEATURE_SWAPON_PRI |
31 | //config: bool "Support priority option -p" |
32 | //config: default y |
33 | //config: depends on SWAPON |
34 | //config: help |
35 | //config: Enable support for setting swap device priority in swapon. |
36 | //config: |
37 | //config:config SWAPOFF |
38 | //config: bool "swapoff" |
39 | //config: default y |
40 | //config: select PLATFORM_LINUX |
41 | //config: help |
42 | //config: This option enables the 'swapoff' utility. |
43 | |
44 | //applet:IF_SWAPON(APPLET_ODDNAME(swapon, swap_on_off, BB_DIR_SBIN, BB_SUID_DROP, swapon)) |
45 | //applet:IF_SWAPOFF(APPLET_ODDNAME(swapoff, swap_on_off, BB_DIR_SBIN, BB_SUID_DROP, swapoff)) |
46 | |
47 | //kbuild:lib-$(CONFIG_SWAPON) += swaponoff.o |
48 | //kbuild:lib-$(CONFIG_SWAPOFF) += swaponoff.o |
49 | |
50 | //usage:#define swapon_trivial_usage |
51 | //usage: "[-a] [-e]" IF_FEATURE_SWAPON_DISCARD(" [-d[POL]]") IF_FEATURE_SWAPON_PRI(" [-p PRI]") " [DEVICE]" |
52 | //usage:#define swapon_full_usage "\n\n" |
53 | //usage: "Start swapping on DEVICE\n" |
54 | //usage: "\n -a Start swapping on all swap devices" |
55 | //usage: IF_FEATURE_SWAPON_DISCARD( |
56 | //usage: "\n -d[POL] Discard blocks at swapon (POL=once)," |
57 | //usage: "\n as freed (POL=pages), or both (POL omitted)" |
58 | //usage: ) |
59 | //usage: "\n -e Silently skip devices that do not exist" |
60 | //usage: IF_FEATURE_SWAPON_PRI( |
61 | //usage: "\n -p PRI Set swap device priority" |
62 | //usage: ) |
63 | //usage: |
64 | //usage:#define swapoff_trivial_usage |
65 | //usage: "[-a] [DEVICE]" |
66 | //usage:#define swapoff_full_usage "\n\n" |
67 | //usage: "Stop swapping on DEVICE\n" |
68 | //usage: "\n -a Stop swapping on all swap devices" |
69 | |
70 | #include "libbb.h" |
71 | #include "common_bufsiz.h" |
72 | #include <mntent.h> |
73 | #include <android.h> |
74 | #ifndef __BIONIC__ |
75 | # include <sys/swap.h> |
76 | #endif |
77 | |
78 | #if ENABLE_FEATURE_MOUNT_LABEL |
79 | # include "volume_id.h" |
80 | #else |
81 | # define resolve_mount_spec(fsname) ((void)0) |
82 | #endif |
83 | |
84 | #ifndef MNTTYPE_SWAP |
85 | # define MNTTYPE_SWAP "swap" |
86 | #endif |
87 | |
88 | #if ENABLE_FEATURE_SWAPON_DISCARD |
89 | #ifndef SWAP_FLAG_DISCARD |
90 | #define SWAP_FLAG_DISCARD 0x10000 |
91 | #endif |
92 | #ifndef SWAP_FLAG_DISCARD_ONCE |
93 | #define SWAP_FLAG_DISCARD_ONCE 0x20000 |
94 | #endif |
95 | #ifndef SWAP_FLAG_DISCARD_PAGES |
96 | #define SWAP_FLAG_DISCARD_PAGES 0x40000 |
97 | #endif |
98 | #define SWAP_FLAG_DISCARD_MASK \ |
99 | (SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE | SWAP_FLAG_DISCARD_PAGES) |
100 | #endif |
101 | |
102 | |
103 | #if ENABLE_FEATURE_SWAPON_DISCARD || ENABLE_FEATURE_SWAPON_PRI |
104 | struct globals { |
105 | int flags; |
106 | } FIX_ALIASING; |
107 | #define G (*(struct globals*)bb_common_bufsiz1) |
108 | #define g_flags (G.flags) |
109 | #define save_g_flags() int save_g_flags = g_flags |
110 | #define restore_g_flags() g_flags = save_g_flags |
111 | #else |
112 | #define g_flags 0 |
113 | #define save_g_flags() ((void)0) |
114 | #define restore_g_flags() ((void)0) |
115 | #endif |
116 | #define INIT_G() do { setup_common_bufsiz(); } while (0) |
117 | |
118 | #if ENABLE_SWAPOFF |
119 | # if ENABLE_SWAPON |
120 | # define do_swapoff (applet_name[5] == 'f') |
121 | # else |
122 | # define do_swapoff 1 |
123 | # endif |
124 | #else |
125 | # define do_swapoff 0 |
126 | #endif |
127 | |
128 | /* Command line options */ |
129 | enum { |
130 | OPTBIT_a, /* -a all */ |
131 | OPTBIT_e, /* -e ifexists */ |
132 | IF_FEATURE_SWAPON_DISCARD( OPTBIT_d ,) /* -d discard */ |
133 | IF_FEATURE_SWAPON_PRI ( OPTBIT_p ,) /* -p priority */ |
134 | OPT_a = 1 << OPTBIT_a, |
135 | OPT_e = 1 << OPTBIT_e, |
136 | OPT_d = IF_FEATURE_SWAPON_DISCARD((1 << OPTBIT_d)) + 0, |
137 | OPT_p = IF_FEATURE_SWAPON_PRI ((1 << OPTBIT_p)) + 0, |
138 | }; |
139 | |
140 | #define OPT_ALL (option_mask32 & OPT_a) |
141 | #define OPT_DISCARD (option_mask32 & OPT_d) |
142 | #define OPT_IFEXISTS (option_mask32 & OPT_e) |
143 | #define OPT_PRIO (option_mask32 & OPT_p) |
144 | |
145 | static int swap_enable_disable(char *device) |
146 | { |
147 | int err = 0; |
148 | int quiet = 0; |
149 | |
150 | resolve_mount_spec(&device); |
151 | |
152 | if (do_swapoff) { |
153 | err = swapoff(device); |
154 | /* Don't complain on OPT_ALL if not a swap device or if it doesn't exist */ |
155 | quiet = (OPT_ALL && (errno == EINVAL || errno == ENOENT)); |
156 | } else { |
157 | /* swapon */ |
158 | struct stat st; |
159 | err = stat(device, &st); |
160 | if (!err) { |
161 | if (ENABLE_DESKTOP && S_ISREG(st.st_mode)) { |
162 | if (st.st_blocks * (off_t)512 < st.st_size) { |
163 | bb_error_msg("%s: file has holes", device); |
164 | return 1; |
165 | } |
166 | } |
167 | err = swapon(device, g_flags); |
168 | /* Don't complain on swapon -a if device is already in use */ |
169 | quiet = (OPT_ALL && errno == EBUSY); |
170 | } |
171 | /* Don't complain if file does not exist with -e option */ |
172 | if (err && OPT_IFEXISTS && errno == ENOENT) |
173 | err = 0; |
174 | } |
175 | |
176 | if (err && !quiet) { |
177 | bb_simple_perror_msg(device); |
178 | return 1; |
179 | } |
180 | return 0; |
181 | } |
182 | |
183 | #if ENABLE_FEATURE_SWAPON_DISCARD |
184 | static void set_discard_flag(char *s) |
185 | { |
186 | /* Unset the flag first to allow fstab options to override */ |
187 | /* options set on the command line */ |
188 | g_flags = (g_flags & ~SWAP_FLAG_DISCARD_MASK) | SWAP_FLAG_DISCARD; |
189 | |
190 | if (!s) /* No optional policy value on the commandline */ |
191 | return; |
192 | /* Skip prepended '=' */ |
193 | if (*s == '=') |
194 | s++; |
195 | /* For fstab parsing: remove other appended options */ |
196 | *strchrnul(s, ',') = '\0'; |
197 | |
198 | if (strcmp(s, "once") == 0) |
199 | g_flags |= SWAP_FLAG_DISCARD_ONCE; |
200 | if (strcmp(s, "pages") == 0) |
201 | g_flags |= SWAP_FLAG_DISCARD_PAGES; |
202 | } |
203 | #else |
204 | #define set_discard_flag(s) ((void)0) |
205 | #endif |
206 | |
207 | #if ENABLE_FEATURE_SWAPON_PRI |
208 | static void set_priority_flag(char *s) |
209 | { |
210 | unsigned prio; |
211 | |
212 | /* For fstab parsing: remove other appended options */ |
213 | *strchrnul(s, ',') = '\0'; |
214 | /* Max allowed 32767 (== SWAP_FLAG_PRIO_MASK) */ |
215 | prio = bb_strtou(s, NULL, 10); |
216 | if (!errno) { |
217 | /* Unset the flag first to allow fstab options to override */ |
218 | /* options set on the command line */ |
219 | g_flags = (g_flags & ~SWAP_FLAG_PRIO_MASK) | SWAP_FLAG_PREFER | |
220 | MIN(prio, SWAP_FLAG_PRIO_MASK); |
221 | } |
222 | } |
223 | #else |
224 | #define set_priority_flag(s) ((void)0) |
225 | #endif |
226 | |
227 | static int do_em_all_in_fstab(void) |
228 | { |
229 | struct mntent *m; |
230 | int err = 0; |
231 | FILE *f = xfopen_for_read("/etc/fstab"); |
232 | |
233 | while ((m = getmntent(f)) != NULL) { |
234 | if (strcmp(m->mnt_type, MNTTYPE_SWAP) == 0) { |
235 | /* swapon -a should ignore entries with noauto, |
236 | * but swapoff -a should process them |
237 | */ |
238 | if (do_swapoff || hasmntopt(m, MNTOPT_NOAUTO) == NULL) { |
239 | /* each swap space might have different flags */ |
240 | /* save global flags for the next round */ |
241 | save_g_flags(); |
242 | if (ENABLE_FEATURE_SWAPON_DISCARD) { |
243 | char *p = hasmntopt(m, "discard"); |
244 | if (p) { |
245 | /* move to '=' or to end of string */ |
246 | p += 7; |
247 | set_discard_flag(p); |
248 | } |
249 | } |
250 | if (ENABLE_FEATURE_SWAPON_PRI) { |
251 | char *p = hasmntopt(m, "pri"); |
252 | if (p) { |
253 | set_priority_flag(p + 4); |
254 | } |
255 | } |
256 | err |= swap_enable_disable(m->mnt_fsname); |
257 | restore_g_flags(); |
258 | } |
259 | } |
260 | } |
261 | |
262 | if (ENABLE_FEATURE_CLEAN_UP) |
263 | endmntent(f); |
264 | |
265 | return err; |
266 | } |
267 | |
268 | static int do_all_in_proc_swaps(void) |
269 | { |
270 | char *line; |
271 | int err = 0; |
272 | FILE *f = fopen_for_read("/proc/swaps"); |
273 | /* Don't complain if missing */ |
274 | if (f) { |
275 | while ((line = xmalloc_fgetline(f)) != NULL) { |
276 | if (line[0] == '/') { |
277 | *strchrnul(line, ' ') = '\0'; |
278 | err |= swap_enable_disable(line); |
279 | } |
280 | free(line); |
281 | } |
282 | if (ENABLE_FEATURE_CLEAN_UP) |
283 | fclose(f); |
284 | } |
285 | |
286 | return err; |
287 | } |
288 | |
289 | #define OPTSTR_SWAPON "ae" \ |
290 | IF_FEATURE_SWAPON_DISCARD("d::") \ |
291 | IF_FEATURE_SWAPON_PRI("p:") |
292 | |
293 | int swap_on_off_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
294 | int swap_on_off_main(int argc UNUSED_PARAM, char **argv) |
295 | { |
296 | IF_FEATURE_SWAPON_PRI(char *prio;) |
297 | IF_FEATURE_SWAPON_DISCARD(char *discard = NULL;) |
298 | int ret = 0; |
299 | |
300 | INIT_G(); |
301 | |
302 | getopt32(argv, do_swapoff ? "ae" : OPTSTR_SWAPON |
303 | IF_FEATURE_SWAPON_DISCARD(, &discard) |
304 | IF_FEATURE_SWAPON_PRI(, &prio) |
305 | ); |
306 | |
307 | argv += optind; |
308 | |
309 | if (OPT_DISCARD) { |
310 | set_discard_flag(discard); |
311 | } |
312 | if (OPT_PRIO) { |
313 | set_priority_flag(prio); |
314 | } |
315 | |
316 | if (OPT_ALL) { |
317 | /* swapoff -a does also /proc/swaps */ |
318 | if (do_swapoff) |
319 | ret = do_all_in_proc_swaps(); |
320 | ret |= do_em_all_in_fstab(); |
321 | } else if (!*argv) { |
322 | /* if not -a we need at least one arg */ |
323 | bb_show_usage(); |
324 | } |
325 | /* Unset -a now to allow for more messages in swap_enable_disable */ |
326 | option_mask32 = option_mask32 & ~OPT_a; |
327 | /* Now process devices on the commandline if any */ |
328 | while (*argv) { |
329 | ret |= swap_enable_disable(*argv++); |
330 | } |
331 | return ret; |
332 | } |
333 |