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