swapctl.c revision 1.13 1 /* $NetBSD: swapctl.c,v 1.13 2000/03/13 22:59:22 soren Exp $ */
2
3 /*
4 * Copyright (c) 1996, 1997, 1999 Matthew R. Green
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 /*
32 * swapctl command:
33 * -A add all devices listed as `sw' in /etc/fstab (also
34 * (sets the dump device, if listed in fstab)
35 * -D <dev> set dumpdev to <dev>
36 * -U remove all devices listed as `sw' in /etc/fstab.
37 * -t [blk|noblk] if -A or -U , add (remove) either all block device
38 * or all non-block devices
39 * -a <dev> add this device
40 * -d <dev> remove this swap device (not supported yet)
41 * -l list swap devices
42 * -s short listing of swap devices
43 * -k use kilobytes
44 * -p <pri> use this priority
45 * -c change priority
46 *
47 * or, if invoked as "swapon" (compatibility mode):
48 *
49 * -a all devices listed as `sw' in /etc/fstab
50 * -t same as -t above (feature not present in old
51 * swapon(8) command)
52 * <dev> add this device
53 */
54
55 #include <sys/param.h>
56 #include <sys/stat.h>
57
58 #include <vm/vm_swap.h>
59
60 #include <unistd.h>
61 #include <err.h>
62 #include <errno.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <fstab.h>
67
68 #include "swapctl.h"
69
70 int command;
71
72 /*
73 * Commands for swapctl(8). These are mutually exclusive.
74 */
75 #define CMD_A 0x01 /* process /etc/fstab for adding */
76 #define CMD_D 0x02 /* set dumpdev */
77 #define CMD_U 0x04 /* process /etc/fstab for removing */
78 #define CMD_a 0x08 /* add a swap file/device */
79 #define CMD_c 0x10 /* change priority of a swap file/device */
80 #define CMD_d 0x20 /* delete a swap file/device */
81 #define CMD_l 0x40 /* list swap files/devices */
82 #define CMD_s 0x80 /* summary of swap files/devices */
83
84 #define SET_COMMAND(cmd) \
85 do { \
86 if (command) \
87 usage(); \
88 command = (cmd); \
89 } while (0)
90
91 /*
92 * Commands that require a "path" argument at the end of the command
93 * line, and the ones which require that none exist.
94 */
95 #define REQUIRE_PATH (CMD_D | CMD_a | CMD_c | CMD_d)
96 #define REQUIRE_NOPATH (CMD_A | CMD_U | CMD_l | CMD_s)
97
98 /*
99 * Option flags, and the commands with which they are valid.
100 */
101 int kflag; /* display in 1K blocks */
102 #define KFLAG_CMDS (CMD_l | CMD_s)
103
104 int pflag; /* priority was specified */
105 #define PFLAG_CMDS (CMD_A | CMD_a | CMD_c)
106
107 char *tflag; /* swap device type (blk or noblk) */
108 #define TFLAG_CMDS (CMD_A | CMD_U )
109
110 int pri; /* uses 0 as default pri */
111
112 static void change_priority __P((const char *));
113 static int add_swap __P((const char *, int));
114 static int delete_swap __P((const char *));
115 static void set_dumpdev __P((const char *));
116 int main __P((int, char *[]));
117 static void do_fstab __P((int));
118 static void usage __P((void));
119 static void swapon_command __P((int, char **));
120 #if 0
121 static void swapoff_command __P((int, char **));
122 #endif
123
124 extern char *__progname; /* from crt0.o */
125
126 int
127 main(argc, argv)
128 int argc;
129 char *argv[];
130 {
131 int c;
132
133 if (strcmp(__progname, "swapon") == 0) {
134 swapon_command(argc, argv);
135 /* NOTREACHED */
136 }
137
138 #if 0
139 if (strcmp(__progname, "swapoff") == 0) {
140 swapoff_command(argc, argv);
141 /* NOTREACHED */
142 }
143 #endif
144
145 while ((c = getopt(argc, argv, "ADUacdlkp:st:")) != -1) {
146 switch (c) {
147 case 'A':
148 SET_COMMAND(CMD_A);
149 break;
150
151 case 'D':
152 SET_COMMAND(CMD_D);
153 break;
154
155 case 'U':
156 SET_COMMAND(CMD_U);
157 break;
158
159 case 'a':
160 SET_COMMAND(CMD_a);
161 break;
162
163 case 'c':
164 SET_COMMAND(CMD_c);
165 break;
166
167 case 'd':
168 SET_COMMAND(CMD_d);
169 break;
170
171 case 'l':
172 SET_COMMAND(CMD_l);
173 break;
174
175 case 'k':
176 kflag = 1;
177 break;
178
179 case 'p':
180 pflag = 1;
181 /* XXX strtol() */
182 pri = atoi(optarg);
183 break;
184
185 case 's':
186 SET_COMMAND(CMD_s);
187 break;
188
189 case 't':
190 if (tflag != NULL)
191 usage();
192 tflag = optarg;
193 break;
194
195 default:
196 usage();
197 /* NOTREACHED */
198 }
199 }
200
201 /* Did the user specify a command? */
202 if (command == 0)
203 usage();
204
205 argv += optind;
206 argc -= optind;
207
208 switch (argc) {
209 case 0:
210 if (command & REQUIRE_PATH)
211 usage();
212 break;
213
214 case 1:
215 if (command & REQUIRE_NOPATH)
216 usage();
217 break;
218
219 default:
220 usage();
221 }
222
223 /* To change priority, you have to specify one. */
224 if ((command == CMD_c) && pflag == 0)
225 usage();
226
227 /* Sanity-check -t */
228 if (tflag != NULL) {
229 if (command != CMD_A && command != CMD_U)
230 usage();
231 if (strcmp(tflag, "blk") != 0 &&
232 strcmp(tflag, "noblk") != 0)
233 usage();
234 }
235
236 /* Dispatch the command. */
237 switch (command) {
238 case CMD_l:
239 list_swap(pri, kflag, pflag, 0, 1);
240 break;
241
242 case CMD_s:
243 list_swap(pri, kflag, pflag, 0, 0);
244 break;
245
246 case CMD_c:
247 change_priority(argv[0]);
248 break;
249
250 case CMD_a:
251 if (! add_swap(argv[0], pri))
252 exit(1);
253 break;
254
255 case CMD_d:
256 if (! delete_swap(argv[0]))
257 exit(1);
258 break;
259
260 case CMD_A:
261 do_fstab(1);
262 break;
263
264 case CMD_D:
265 set_dumpdev(argv[0]);
266 break;
267
268 case CMD_U:
269 do_fstab(0);
270 break;
271 }
272
273 exit(0);
274 }
275
276 /*
277 * swapon_command: emulate the old swapon(8) program.
278 */
279 static void
280 swapon_command(argc, argv)
281 int argc;
282 char **argv;
283 {
284 int ch, fiztab = 0;
285
286 while ((ch = getopt(argc, argv, "at:")) != -1) {
287 switch (ch) {
288 case 'a':
289 fiztab = 1;
290 break;
291 case 't':
292 if (tflag != NULL)
293 usage();
294 tflag = optarg;
295 break;
296 default:
297 goto swapon_usage;
298 }
299 }
300 argc -= optind;
301 argv += optind;
302
303 if (fiztab) {
304 if (argc)
305 goto swapon_usage;
306 /* Sanity-check -t */
307 if (tflag != NULL) {
308 if (strcmp(tflag, "blk") != 0 &&
309 strcmp(tflag, "noblk") != 0)
310 usage();
311 }
312 do_fstab(1);
313 exit(0);
314 } else if (argc == 0 || tflag != NULL)
315 goto swapon_usage;
316
317 while (argc) {
318 if (! add_swap(argv[0], pri))
319 exit(1);
320 argc--;
321 argv++;
322 }
323 exit(0);
324 /* NOTREACHED */
325
326 swapon_usage:
327 fprintf(stderr, "usage: %s -a [-t blk|noblk]\n", __progname);
328 fprintf(stderr, " %s <path> ...\n", __progname);
329 exit(1);
330 }
331
332 /*
333 * change_priority: change the priority of a swap device.
334 */
335 static void
336 change_priority(path)
337 const char *path;
338 {
339
340 if (swapctl(SWAP_CTL, path, pri) < 0)
341 warn("%s", path);
342 }
343
344 /*
345 * add_swap: add the pathname to the list of swap devices.
346 */
347 static int
348 add_swap(path, priority)
349 const char *path;
350 int priority;
351 {
352 struct stat sb;
353
354 if (stat(path, &sb) < 0)
355 goto oops;
356
357 if (sb.st_mode & S_IROTH)
358 warnx("%s is readable by the world", path);
359 if (sb.st_mode & S_IWOTH)
360 warnx("%s is writable by the world", path);
361
362 if (swapctl(SWAP_ON, path, priority) < 0) {
363 oops:
364 warn("%s", path);
365 return (0);
366 }
367 return (1);
368 }
369
370 /*
371 * delete_swap: remove the pathname to the list of swap devices.
372 */
373 static int
374 delete_swap(path)
375 const char *path;
376 {
377
378 if (swapctl(SWAP_OFF, path, pri) < 0) {
379 warn("%s", path);
380 return (0);
381 }
382 return (1);
383 }
384
385 static void
386 set_dumpdev(path)
387 const char *path;
388 {
389
390 if (swapctl(SWAP_DUMPDEV, path, NULL) == -1)
391 warn("could not set dump device to %s", path);
392 else
393 printf("%s: setting dump device to %s\n", __progname, path);
394 }
395
396 static void
397 do_fstab(add)
398 int add;
399 {
400 struct fstab *fp;
401 char *s;
402 long priority;
403 struct stat st;
404 int isblk;
405 int gotone = 0;
406 #define PATH_MOUNT "/sbin/mount_nfs"
407 #define PATH_UMOUNT "/sbin/umount"
408 char cmd[2*PATH_MAX+sizeof(PATH_MOUNT)+2];
409
410 #define PRIORITYEQ "priority="
411 #define NFSMNTPT "nfsmntpt="
412 while ((fp = getfsent()) != NULL) {
413 const char *spec;
414
415 spec = fp->fs_spec;
416 cmd[0] = '\0';
417
418 if (strcmp(fp->fs_type, "dp") == 0 && add) {
419 set_dumpdev(spec);
420 continue;
421 }
422
423 if (strcmp(fp->fs_type, "sw") != 0)
424 continue;
425 isblk = 0;
426
427 if ((s = strstr(fp->fs_mntops, PRIORITYEQ)) != NULL) {
428 s += sizeof(PRIORITYEQ) - 1;
429 priority = atol(s);
430 } else
431 priority = pri;
432
433 if ((s = strstr(fp->fs_mntops, NFSMNTPT)) != NULL) {
434 char *t;
435
436 /*
437 * Skip this song and dance if we're only
438 * doing block devices.
439 */
440 if (tflag != NULL && strcmp(tflag, "blk") == 0)
441 continue;
442
443 t = strpbrk(s, ",");
444 if (t != 0)
445 *t = '\0';
446 spec = strdup(s + strlen(NFSMNTPT));
447 if (t != 0)
448 *t = ',';
449
450 if (spec == NULL)
451 errx(1, "Out of memory");
452
453 if (strlen(spec) == 0) {
454 warnx("empty mountpoint");
455 free((char *)spec);
456 continue;
457 }
458 if (add) {
459 snprintf(cmd, sizeof(cmd), "%s %s %s",
460 PATH_MOUNT, fp->fs_spec, spec);
461 if (system(cmd) != 0) {
462 warnx("%s: mount failed", fp->fs_spec);
463 continue;
464 }
465 } else {
466 snprintf(cmd, sizeof(cmd), "%s %s",
467 PATH_UMOUNT, fp->fs_spec);
468 }
469 } else {
470 /*
471 * Determine blk-ness.
472 */
473 if (stat(spec, &st) < 0) {
474 warn(spec);
475 continue;
476 }
477 if (S_ISBLK(st.st_mode))
478 isblk = 1;
479 }
480
481 /*
482 * Skip this type if we're told to.
483 */
484 if (tflag != NULL) {
485 if (strcmp(tflag, "blk") == 0 && isblk == 0)
486 continue;
487 if (strcmp(tflag, "noblk") == 0 && isblk == 1)
488 continue;
489 }
490
491 if (add) {
492 if (add_swap(spec, (int)priority)) {
493 gotone = 1;
494 printf(
495 "%s: adding %s as swap device at priority %d\n",
496 __progname, fp->fs_spec, (int)priority);
497 }
498 } else {
499 if (delete_swap(spec)) {
500 gotone = 1;
501 printf(
502 "%s: removing %s as swap device\n",
503 __progname, fp->fs_spec);
504 }
505 if (cmd[0]) {
506 if (system(cmd) != 0) {
507 warnx("%s: umount failed", fp->fs_spec);
508 continue;
509 }
510 }
511 }
512
513 if (spec != fp->fs_spec)
514 free((char *)spec);
515 }
516 if (gotone == 0)
517 exit(1);
518 }
519
520 static void
521 usage()
522 {
523
524 fprintf(stderr, "usage: %s -A [-p priority] [-t blk|noblk]\n",
525 __progname);
526 fprintf(stderr, " %s -D dumppath\n", __progname);
527 fprintf(stderr, " %s -U [-t blk|noblk]\n", __progname);
528 fprintf(stderr, " %s -a [-p priority] path\n", __progname);
529 fprintf(stderr, " %s -c -p priority path\n", __progname);
530 fprintf(stderr, " %s -d path\n", __progname);
531 fprintf(stderr, " %s -l | -s [-k]\n", __progname);
532 exit(1);
533 }
534