xref: /netbsd-src/sbin/swapctl/swapctl.c (revision de1dfb1250df962f1ff3a011772cf58e605aed11)
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