xref: /netbsd-src/sbin/swapctl/swapctl.c (revision d710132b4b8ce7f7cccaaf660cb16aa16b4077a0)
1 /*	$NetBSD: swapctl.c,v 1.22 2003/06/23 11:53:44 agc 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.22 2003/06/23 11:53:44 agc 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, NULL) == -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, NULL) == -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