xref: /netbsd-src/sbin/swapctl/swapctl.c (revision aaf4ece63a859a04e37cf3a7229b5fab0157cc06)
1 /*	$NetBSD: swapctl.c,v 1.29 2005/06/12 16:24:20 christos 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.29 2005/06/12 16:24:20 christos 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(char *);
128 static	int  add_swap(char *, int);
129 static	int  delete_swap(char *);
130 static	void set_dumpdev(char *);
131 static	void get_dumpdev(void);
132 static	void do_fstab(int);
133 static	void usage(void);
134 static	void swapon_command(int, char **);
135 #if 0
136 static	void swapoff_command(int, char **);
137 #endif
138 
139 int
140 main(int argc, char *argv[])
141 {
142 	int	c;
143 
144 	if (strcmp(getprogname(), "swapon") == 0) {
145 		swapon_command(argc, argv);
146 		/* NOTREACHED */
147 	}
148 
149 #if 0
150 	if (strcmp(getprogname(), "swapoff") == 0) {
151 		swapoff_command(argc, argv);
152 		/* NOTREACHED */
153 	}
154 #endif
155 
156 	while ((c = getopt(argc, argv, "ADUacdghklmp:st:z")) != -1) {
157 		switch (c) {
158 		case 'A':
159 			SET_COMMAND(CMD_A);
160 			break;
161 
162 		case 'D':
163 			SET_COMMAND(CMD_D);
164 			break;
165 
166 		case 'U':
167 			SET_COMMAND(CMD_U);
168 			break;
169 
170 		case 'a':
171 			SET_COMMAND(CMD_a);
172 			break;
173 
174 		case 'c':
175 			SET_COMMAND(CMD_c);
176 			break;
177 
178 		case 'd':
179 			SET_COMMAND(CMD_d);
180 			break;
181 
182 		case 'g':
183 			kflag = 3; /* 1k ^ 3 */
184 			break;
185 
186 		case 'h':
187 			hflag = 1;
188 			break;
189 
190 		case 'k':
191 			kflag = 1;
192 			break;
193 
194 		case 'l':
195 			SET_COMMAND(CMD_l);
196 			break;
197 
198 		case 'm':
199 			kflag = 2; /* 1k ^ 2 */
200 			break;
201 
202 		case 'p':
203 			pflag = 1;
204 			/* XXX strtol() */
205 			pri = atoi(optarg);
206 			break;
207 
208 		case 's':
209 			SET_COMMAND(CMD_s);
210 			break;
211 
212 		case 't':
213 			if (tflag != NULL)
214 				usage();
215 			tflag = optarg;
216 			break;
217 
218 		case 'z':
219 			SET_COMMAND(CMD_z);
220 			break;
221 
222 		default:
223 			usage();
224 			/* NOTREACHED */
225 		}
226 	}
227 
228 	/* Did the user specify a command? */
229 	if (command == 0)
230 		usage();
231 
232 	argv += optind;
233 	argc -= optind;
234 
235 	switch (argc) {
236 	case 0:
237 		if (command & REQUIRE_PATH)
238 			usage();
239 		break;
240 
241 	case 1:
242 		if (command & REQUIRE_NOPATH)
243 			usage();
244 		break;
245 
246 	default:
247 		usage();
248 	}
249 
250 	/* To change priority, you have to specify one. */
251 	if ((command == CMD_c) && pflag == 0)
252 		usage();
253 
254 	/* Sanity-check -t */
255 	if (tflag != NULL) {
256 		if (command != CMD_A && command != CMD_U)
257 			usage();
258 		if (strcmp(tflag, "blk") != 0 &&
259 		    strcmp(tflag, "noblk") != 0)
260 			usage();
261 	}
262 
263 	/* Dispatch the command. */
264 	switch (command) {
265 	case CMD_l:
266 		list_swap(pri, kflag, pflag, 0, 1, hflag);
267 		break;
268 
269 	case CMD_s:
270 		list_swap(pri, kflag, pflag, 0, 0, hflag);
271 		break;
272 
273 	case CMD_c:
274 		change_priority(argv[0]);
275 		break;
276 
277 	case CMD_a:
278 		if (! add_swap(argv[0], pri))
279 			exit(1);
280 		break;
281 
282 	case CMD_d:
283 		if (! delete_swap(argv[0]))
284 			exit(1);
285 		break;
286 
287 	case CMD_A:
288 		do_fstab(1);
289 		break;
290 
291 	case CMD_D:
292 		set_dumpdev(argv[0]);
293 		break;
294 
295 	case CMD_z:
296 		get_dumpdev();
297 		break;
298 
299 	case CMD_U:
300 		do_fstab(0);
301 		break;
302 	}
303 
304 	exit(0);
305 }
306 
307 /*
308  * swapon_command: emulate the old swapon(8) program.
309  */
310 static void
311 swapon_command(int argc, char **argv)
312 {
313 	int ch, fiztab = 0;
314 
315 	while ((ch = getopt(argc, argv, "at:")) != -1) {
316 		switch (ch) {
317 		case 'a':
318 			fiztab = 1;
319 			break;
320 		case 't':
321 			if (tflag != NULL)
322 				usage();
323 			tflag = optarg;
324 			break;
325 		default:
326 			goto swapon_usage;
327 		}
328 	}
329 	argc -= optind;
330 	argv += optind;
331 
332 	if (fiztab) {
333 		if (argc)
334 			goto swapon_usage;
335 		/* Sanity-check -t */
336 		if (tflag != NULL) {
337 			if (strcmp(tflag, "blk") != 0 &&
338 			    strcmp(tflag, "noblk") != 0)
339 				usage();
340 		}
341 		do_fstab(1);
342 		exit(0);
343 	} else if (argc == 0 || tflag != NULL)
344 		goto swapon_usage;
345 
346 	while (argc) {
347 		if (! add_swap(argv[0], pri))
348 			exit(1);
349 		argc--;
350 		argv++;
351 	}
352 	exit(0);
353 	/* NOTREACHED */
354 
355  swapon_usage:
356 	fprintf(stderr, "usage: %s -a [-t blk|noblk]\n", getprogname());
357 	fprintf(stderr, "       %s <path> ...\n", getprogname());
358 	exit(1);
359 }
360 
361 /*
362  * change_priority:  change the priority of a swap device.
363  */
364 static void
365 change_priority(char *path)
366 {
367 
368 	if (swapctl(SWAP_CTL, path, pri) < 0)
369 		warn("%s", path);
370 }
371 
372 /*
373  * add_swap:  add the pathname to the list of swap devices.
374  */
375 static int
376 add_swap(char *path, int priority)
377 {
378 	struct stat sb;
379 
380 	if (stat(path, &sb) < 0)
381 		goto oops;
382 
383 	if (sb.st_mode & S_IROTH)
384 		warnx("WARNING: %s is readable by the world", path);
385 	if (sb.st_mode & S_IWOTH)
386 		warnx("WARNING: %s is writable by the world", path);
387 
388 	if (swapctl(SWAP_ON, path, priority) < 0) {
389 oops:
390 		warn("%s", path);
391 		return (0);
392 	}
393 	return (1);
394 }
395 
396 /*
397  * delete_swap:  remove the pathname to the list of swap devices.
398  */
399 static int
400 delete_swap(char *path)
401 {
402 
403 	if (swapctl(SWAP_OFF, path, pri) < 0) {
404 		warn("%s", path);
405 		return (0);
406 	}
407 	return (1);
408 }
409 
410 static void
411 set_dumpdev(char *path)
412 {
413 
414 	if (swapctl(SWAP_DUMPDEV, path, 0) == -1)
415 		warn("could not set dump device to %s", path);
416 	else
417 		printf("%s: setting dump device to %s\n", getprogname(), path);
418 }
419 
420 static void
421 get_dumpdev(void)
422 {
423 	dev_t	dev;
424 	char 	*name;
425 
426 	if (swapctl(SWAP_GETDUMPDEV, &dev, 0) == -1)
427 		warn("could not get dump device");
428 	else if (dev == NODEV)
429 		printf("no dump device set\n");
430 	else {
431 		name = devname(dev, S_IFBLK);
432 		printf("dump device is ");
433 		if (name)
434 			printf("%s\n", name);
435 		else
436 			printf("major %d minor %d\n", major(dev), minor(dev));
437 	}
438 }
439 
440 static void
441 do_fstab(int add)
442 {
443 	struct	fstab *fp;
444 	char	*s;
445 	long	priority;
446 	struct	stat st;
447 	int	isblk;
448 	int	gotone = 0;
449 
450 #ifdef RESCUEDIR
451 #define PATH_MOUNT	RESCUEDIR "/mount_nfs"
452 #define PATH_UMOUNT	RESCUEDIR "/umount"
453 #else
454 #define PATH_MOUNT	"/sbin/mount_nfs"
455 #define PATH_UMOUNT	"/sbin/umount"
456 #endif
457 
458 	char	cmd[2*PATH_MAX+sizeof(PATH_MOUNT)+2];
459 
460 #define PRIORITYEQ	"priority="
461 #define NFSMNTPT	"nfsmntpt="
462 	while ((fp = getfsent()) != NULL) {
463 		char *spec;
464 
465 		spec = fp->fs_spec;
466 		cmd[0] = '\0';
467 
468 		if (strcmp(fp->fs_type, "dp") == 0 && add) {
469 			set_dumpdev(spec);
470 			continue;
471 		}
472 
473 		if (strcmp(fp->fs_type, "sw") != 0)
474 			continue;
475 
476 		/* handle dp as mnt option */
477 		if (strstr(fp->fs_mntops, "dp") && add)
478 			set_dumpdev(spec);
479 
480 		isblk = 0;
481 
482 		if ((s = strstr(fp->fs_mntops, PRIORITYEQ)) != NULL) {
483 			s += sizeof(PRIORITYEQ) - 1;
484 			priority = atol(s);
485 		} else
486 			priority = pri;
487 
488 		if ((s = strstr(fp->fs_mntops, NFSMNTPT)) != NULL) {
489 			char *t;
490 
491 			/*
492 			 * Skip this song and dance if we're only
493 			 * doing block devices.
494 			 */
495 			if (tflag != NULL && strcmp(tflag, "blk") == 0)
496 				continue;
497 
498 			t = strpbrk(s, ",");
499 			if (t != 0)
500 				*t = '\0';
501 			spec = strdup(s + strlen(NFSMNTPT));
502 			if (t != 0)
503 				*t = ',';
504 
505 			if (spec == NULL)
506 				errx(1, "Out of memory");
507 
508 			if (strlen(spec) == 0) {
509 				warnx("empty mountpoint");
510 				free(spec);
511 				continue;
512 			}
513 			if (add) {
514 				snprintf(cmd, sizeof(cmd), "%s %s %s",
515 					PATH_MOUNT, fp->fs_spec, spec);
516 				if (system(cmd) != 0) {
517 					warnx("%s: mount failed", fp->fs_spec);
518 					continue;
519 				}
520 			} else {
521 				snprintf(cmd, sizeof(cmd), "%s %s",
522 					PATH_UMOUNT, fp->fs_spec);
523 			}
524 		} else {
525 			/*
526 			 * Determine blk-ness.
527 			 */
528 			if (stat(spec, &st) < 0) {
529 				warn("%s", spec);
530 				continue;
531 			}
532 			if (S_ISBLK(st.st_mode))
533 				isblk = 1;
534 		}
535 
536 		/*
537 		 * Skip this type if we're told to.
538 		 */
539 		if (tflag != NULL) {
540 			if (strcmp(tflag, "blk") == 0 && isblk == 0)
541 				continue;
542 			if (strcmp(tflag, "noblk") == 0 && isblk == 1)
543 				continue;
544 		}
545 
546 		if (add) {
547 			if (add_swap(spec, (int)priority)) {
548 				gotone = 1;
549 				printf(
550 			    	"%s: adding %s as swap device at priority %d\n",
551 				    getprogname(), fp->fs_spec, (int)priority);
552 			}
553 		} else {
554 			if (delete_swap(spec)) {
555 				gotone = 1;
556 				printf(
557 				    "%s: removing %s as swap device\n",
558 				    getprogname(), fp->fs_spec);
559 			}
560 			if (cmd[0]) {
561 				if (system(cmd) != 0) {
562 					warnx("%s: umount failed", fp->fs_spec);
563 					continue;
564 				}
565 			}
566 		}
567 
568 		if (spec != fp->fs_spec)
569 			free(spec);
570 	}
571 	if (gotone == 0)
572 		exit(1);
573 }
574 
575 static void
576 usage(void)
577 {
578 	const char *progname = getprogname();
579 
580 	fprintf(stderr, "usage: %s -A [-p priority] [-t blk|noblk]\n",
581 	    progname);
582 	fprintf(stderr, "       %s -D dumppath\n", progname);
583 	fprintf(stderr, "       %s -U [-t blk|noblk]\n", progname);
584 	fprintf(stderr, "       %s -a [-p priority] path\n", progname);
585 	fprintf(stderr, "       %s -c -p priority path\n", progname);
586 	fprintf(stderr, "       %s -d path\n", progname);
587 	fprintf(stderr, "       %s -l | -s [-k|-m|-g|-h]\n", progname);
588 	fprintf(stderr, "       %s -z\n", progname);
589 	exit(1);
590 }
591