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