swapctl.c revision 1.26 1 /* $NetBSD: swapctl.c,v 1.26 2004/06/08 08:13:16 cjep 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 * -z show dumpdev
37 * -U remove all devices listed as `sw' in /etc/fstab.
38 * -t [blk|noblk] if -A or -U , add (remove) either all block device
39 * or all non-block devices
40 * -a <dev> add this device
41 * -d <dev> remove this swap device
42 * -g use gigabytes
43 * -h use humanize_number(3) for listing
44 * -l list swap devices
45 * -m use megabytes
46 * -s short listing of swap devices
47 * -k use kilobytes
48 * -p <pri> use this priority
49 * -c change priority
50 *
51 * or, if invoked as "swapon" (compatibility mode):
52 *
53 * -a all devices listed as `sw' in /etc/fstab
54 * -t same as -t above (feature not present in old
55 * swapon(8) command)
56 * <dev> add this device
57 */
58 #include <sys/cdefs.h>
59
60 #ifndef lint
61 __RCSID("$NetBSD: swapctl.c,v 1.26 2004/06/08 08:13:16 cjep Exp $");
62 #endif
63
64
65 #include <sys/param.h>
66 #include <sys/stat.h>
67 #include <sys/swap.h>
68
69 #include <unistd.h>
70 #include <err.h>
71 #include <errno.h>
72 #include <stdio.h>
73 #include <stdlib.h>
74 #include <string.h>
75 #include <fstab.h>
76
77 #include "swapctl.h"
78
79 int command;
80
81 /*
82 * Commands for swapctl(8). These are mutually exclusive.
83 */
84 #define CMD_A 0x01 /* process /etc/fstab for adding */
85 #define CMD_D 0x02 /* set dumpdev */
86 #define CMD_U 0x04 /* process /etc/fstab for removing */
87 #define CMD_a 0x08 /* add a swap file/device */
88 #define CMD_c 0x10 /* change priority of a swap file/device */
89 #define CMD_d 0x20 /* delete a swap file/device */
90 #define CMD_l 0x40 /* list swap files/devices */
91 #define CMD_s 0x80 /* summary of swap files/devices */
92 #define CMD_z 0x100 /* show dump device */
93
94 #define SET_COMMAND(cmd) \
95 do { \
96 if (command) \
97 usage(); \
98 command = (cmd); \
99 } while (0)
100
101 /*
102 * Commands that require a "path" argument at the end of the command
103 * line, and the ones which require that none exist.
104 */
105 #define REQUIRE_PATH (CMD_D | CMD_a | CMD_c | CMD_d)
106 #define REQUIRE_NOPATH (CMD_A | CMD_U | CMD_l | CMD_s | CMD_z)
107
108 /*
109 * Option flags, and the commands with which they are valid.
110 */
111 int kflag; /* display in 1K^x blocks */
112 #define KFLAG_CMDS (CMD_l | CMD_s)
113 #define MFLAG_CMDS (CMD_l | CMD_s)
114 #define GFLAG_CMDS (CMD_l | CMD_s)
115
116 int hflag; /* display with humanize_number */
117 #define HFLAG_CMDS (CMD_l | CMD_s)
118
119 int pflag; /* priority was specified */
120 #define PFLAG_CMDS (CMD_A | CMD_a | CMD_c)
121
122 char *tflag; /* swap device type (blk or noblk) */
123 #define TFLAG_CMDS (CMD_A | CMD_U)
124
125 int pri; /* uses 0 as default pri */
126
127 static void change_priority __P((const char *));
128 static int add_swap __P((const char *, int));
129 static int delete_swap __P((const char *));
130 static void set_dumpdev __P((const char *));
131 static void get_dumpdev __P((void));
132 int main __P((int, char *[]));
133 static void do_fstab __P((int));
134 static void usage __P((void));
135 static void swapon_command __P((int, char **));
136 #if 0
137 static void swapoff_command __P((int, char **));
138 #endif
139
140 int
141 main(argc, argv)
142 int argc;
143 char *argv[];
144 {
145 int c;
146
147 if (strcmp(getprogname(), "swapon") == 0) {
148 swapon_command(argc, argv);
149 /* NOTREACHED */
150 }
151
152 #if 0
153 if (strcmp(getprogname(), "swapoff") == 0) {
154 swapoff_command(argc, argv);
155 /* NOTREACHED */
156 }
157 #endif
158
159 while ((c = getopt(argc, argv, "ADUacdghklmp:st:z")) != -1) {
160 switch (c) {
161 case 'A':
162 SET_COMMAND(CMD_A);
163 break;
164
165 case 'D':
166 SET_COMMAND(CMD_D);
167 break;
168
169 case 'U':
170 SET_COMMAND(CMD_U);
171 break;
172
173 case 'a':
174 SET_COMMAND(CMD_a);
175 break;
176
177 case 'c':
178 SET_COMMAND(CMD_c);
179 break;
180
181 case 'd':
182 SET_COMMAND(CMD_d);
183 break;
184
185 case 'g':
186 kflag = 3; /* 1k ^ 3 */
187 break;
188
189 case 'h':
190 hflag = 1;
191 break;
192
193 case 'k':
194 kflag = 1;
195 break;
196
197 case 'l':
198 SET_COMMAND(CMD_l);
199 break;
200
201 case 'm':
202 kflag = 2; /* 1k ^ 2 */
203 break;
204
205 case 'p':
206 pflag = 1;
207 /* XXX strtol() */
208 pri = atoi(optarg);
209 break;
210
211 case 's':
212 SET_COMMAND(CMD_s);
213 break;
214
215 case 't':
216 if (tflag != NULL)
217 usage();
218 tflag = optarg;
219 break;
220
221 case 'z':
222 SET_COMMAND(CMD_z);
223 break;
224
225 default:
226 usage();
227 /* NOTREACHED */
228 }
229 }
230
231 /* Did the user specify a command? */
232 if (command == 0)
233 usage();
234
235 argv += optind;
236 argc -= optind;
237
238 switch (argc) {
239 case 0:
240 if (command & REQUIRE_PATH)
241 usage();
242 break;
243
244 case 1:
245 if (command & REQUIRE_NOPATH)
246 usage();
247 break;
248
249 default:
250 usage();
251 }
252
253 /* To change priority, you have to specify one. */
254 if ((command == CMD_c) && pflag == 0)
255 usage();
256
257 /* Sanity-check -t */
258 if (tflag != NULL) {
259 if (command != CMD_A && command != CMD_U)
260 usage();
261 if (strcmp(tflag, "blk") != 0 &&
262 strcmp(tflag, "noblk") != 0)
263 usage();
264 }
265
266 /* Dispatch the command. */
267 switch (command) {
268 case CMD_l:
269 list_swap(pri, kflag, pflag, 0, 1, hflag);
270 break;
271
272 case CMD_s:
273 list_swap(pri, kflag, pflag, 0, 0, hflag);
274 break;
275
276 case CMD_c:
277 change_priority(argv[0]);
278 break;
279
280 case CMD_a:
281 if (! add_swap(argv[0], pri))
282 exit(1);
283 break;
284
285 case CMD_d:
286 if (! delete_swap(argv[0]))
287 exit(1);
288 break;
289
290 case CMD_A:
291 do_fstab(1);
292 break;
293
294 case CMD_D:
295 set_dumpdev(argv[0]);
296 break;
297
298 case CMD_z:
299 get_dumpdev();
300 break;
301
302 case CMD_U:
303 do_fstab(0);
304 break;
305 }
306
307 exit(0);
308 }
309
310 /*
311 * swapon_command: emulate the old swapon(8) program.
312 */
313 static void
314 swapon_command(argc, argv)
315 int argc;
316 char **argv;
317 {
318 int ch, fiztab = 0;
319
320 while ((ch = getopt(argc, argv, "at:")) != -1) {
321 switch (ch) {
322 case 'a':
323 fiztab = 1;
324 break;
325 case 't':
326 if (tflag != NULL)
327 usage();
328 tflag = optarg;
329 break;
330 default:
331 goto swapon_usage;
332 }
333 }
334 argc -= optind;
335 argv += optind;
336
337 if (fiztab) {
338 if (argc)
339 goto swapon_usage;
340 /* Sanity-check -t */
341 if (tflag != NULL) {
342 if (strcmp(tflag, "blk") != 0 &&
343 strcmp(tflag, "noblk") != 0)
344 usage();
345 }
346 do_fstab(1);
347 exit(0);
348 } else if (argc == 0 || tflag != NULL)
349 goto swapon_usage;
350
351 while (argc) {
352 if (! add_swap(argv[0], pri))
353 exit(1);
354 argc--;
355 argv++;
356 }
357 exit(0);
358 /* NOTREACHED */
359
360 swapon_usage:
361 fprintf(stderr, "usage: %s -a [-t blk|noblk]\n", getprogname());
362 fprintf(stderr, " %s <path> ...\n", getprogname());
363 exit(1);
364 }
365
366 /*
367 * change_priority: change the priority of a swap device.
368 */
369 static void
370 change_priority(path)
371 const char *path;
372 {
373
374 if (swapctl(SWAP_CTL, path, pri) < 0)
375 warn("%s", path);
376 }
377
378 /*
379 * add_swap: add the pathname to the list of swap devices.
380 */
381 static int
382 add_swap(path, priority)
383 const char *path;
384 int priority;
385 {
386 struct stat sb;
387
388 if (stat(path, &sb) < 0)
389 goto oops;
390
391 if (sb.st_mode & S_IROTH)
392 warnx("WARNING: %s is readable by the world", path);
393 if (sb.st_mode & S_IWOTH)
394 warnx("WARNING: %s is writable by the world", path);
395
396 if (swapctl(SWAP_ON, path, priority) < 0) {
397 oops:
398 warn("%s", path);
399 return (0);
400 }
401 return (1);
402 }
403
404 /*
405 * delete_swap: remove the pathname to the list of swap devices.
406 */
407 static int
408 delete_swap(path)
409 const char *path;
410 {
411
412 if (swapctl(SWAP_OFF, path, pri) < 0) {
413 warn("%s", path);
414 return (0);
415 }
416 return (1);
417 }
418
419 static void
420 set_dumpdev(path)
421 const char *path;
422 {
423
424 if (swapctl(SWAP_DUMPDEV, path, 0) == -1)
425 warn("could not set dump device to %s", path);
426 else
427 printf("%s: setting dump device to %s\n", getprogname(), path);
428 }
429
430 static void
431 get_dumpdev()
432 {
433 dev_t dev;
434 char *name;
435
436 if (swapctl(SWAP_GETDUMPDEV, &dev, 0) == -1)
437 warn("could not get dump device");
438 else if (dev == NODEV)
439 printf("no dump device set\n");
440 else {
441 name = devname(dev, S_IFBLK);
442 printf("dump device is ");
443 if (name)
444 printf("%s\n", name);
445 else
446 printf("major %d minor %d\n", major(dev), minor(dev));
447 }
448 }
449
450 static void
451 do_fstab(add)
452 int add;
453 {
454 struct fstab *fp;
455 char *s;
456 long priority;
457 struct stat st;
458 int isblk;
459 int gotone = 0;
460 #define PATH_MOUNT "/sbin/mount_nfs"
461 #define PATH_UMOUNT "/sbin/umount"
462 char cmd[2*PATH_MAX+sizeof(PATH_MOUNT)+2];
463
464 #define PRIORITYEQ "priority="
465 #define NFSMNTPT "nfsmntpt="
466 while ((fp = getfsent()) != NULL) {
467 const char *spec;
468
469 spec = fp->fs_spec;
470 cmd[0] = '\0';
471
472 if (strcmp(fp->fs_type, "dp") == 0 && add) {
473 set_dumpdev(spec);
474 continue;
475 }
476
477 if (strcmp(fp->fs_type, "sw") != 0)
478 continue;
479
480 /* handle dp as mnt option */
481 if (strstr(fp->fs_mntops, "dp") && add)
482 set_dumpdev(spec);
483
484 isblk = 0;
485
486 if ((s = strstr(fp->fs_mntops, PRIORITYEQ)) != NULL) {
487 s += sizeof(PRIORITYEQ) - 1;
488 priority = atol(s);
489 } else
490 priority = pri;
491
492 if ((s = strstr(fp->fs_mntops, NFSMNTPT)) != NULL) {
493 char *t;
494
495 /*
496 * Skip this song and dance if we're only
497 * doing block devices.
498 */
499 if (tflag != NULL && strcmp(tflag, "blk") == 0)
500 continue;
501
502 t = strpbrk(s, ",");
503 if (t != 0)
504 *t = '\0';
505 spec = strdup(s + strlen(NFSMNTPT));
506 if (t != 0)
507 *t = ',';
508
509 if (spec == NULL)
510 errx(1, "Out of memory");
511
512 if (strlen(spec) == 0) {
513 warnx("empty mountpoint");
514 free((char *)spec);
515 continue;
516 }
517 if (add) {
518 snprintf(cmd, sizeof(cmd), "%s %s %s",
519 PATH_MOUNT, fp->fs_spec, spec);
520 if (system(cmd) != 0) {
521 warnx("%s: mount failed", fp->fs_spec);
522 continue;
523 }
524 } else {
525 snprintf(cmd, sizeof(cmd), "%s %s",
526 PATH_UMOUNT, fp->fs_spec);
527 }
528 } else {
529 /*
530 * Determine blk-ness.
531 */
532 if (stat(spec, &st) < 0) {
533 warn("%s", spec);
534 continue;
535 }
536 if (S_ISBLK(st.st_mode))
537 isblk = 1;
538 }
539
540 /*
541 * Skip this type if we're told to.
542 */
543 if (tflag != NULL) {
544 if (strcmp(tflag, "blk") == 0 && isblk == 0)
545 continue;
546 if (strcmp(tflag, "noblk") == 0 && isblk == 1)
547 continue;
548 }
549
550 if (add) {
551 if (add_swap(spec, (int)priority)) {
552 gotone = 1;
553 printf(
554 "%s: adding %s as swap device at priority %d\n",
555 getprogname(), fp->fs_spec, (int)priority);
556 }
557 } else {
558 if (delete_swap(spec)) {
559 gotone = 1;
560 printf(
561 "%s: removing %s as swap device\n",
562 getprogname(), fp->fs_spec);
563 }
564 if (cmd[0]) {
565 if (system(cmd) != 0) {
566 warnx("%s: umount failed", fp->fs_spec);
567 continue;
568 }
569 }
570 }
571
572 if (spec != fp->fs_spec)
573 free((char *)spec);
574 }
575 if (gotone == 0)
576 exit(1);
577 }
578
579 static void
580 usage()
581 {
582 const char *progname = getprogname();
583
584 fprintf(stderr, "usage: %s -A [-p priority] [-t blk|noblk]\n",
585 progname);
586 fprintf(stderr, " %s -D dumppath\n", progname);
587 fprintf(stderr, " %s -U [-t blk|noblk]\n", progname);
588 fprintf(stderr, " %s -a [-p priority] path\n", progname);
589 fprintf(stderr, " %s -c -p priority path\n", progname);
590 fprintf(stderr, " %s -d path\n", progname);
591 fprintf(stderr, " %s -l | -s [-k|-m|-g|-h]\n", progname);
592 fprintf(stderr, " %s -z\n", progname);
593 exit(1);
594 }
595