xref: /netbsd-src/sbin/dump/main.c (revision 27578b9aac214cc7796ead81dcc5427e79d5f2a0)
1 /*	$NetBSD: main.c,v 1.38 2001/08/14 06:51:37 lukem Exp $	*/
2 
3 /*-
4  * Copyright (c) 1980, 1991, 1993, 1994
5  *	The Regents of the University of California.  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. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT("@(#) Copyright (c) 1980, 1991, 1993, 1994\n\
39 	The Regents of the University of California.  All rights reserved.\n");
40 #endif /* not lint */
41 
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)main.c	8.6 (Berkeley) 5/1/95";
45 #else
46 __RCSID("$NetBSD: main.c,v 1.38 2001/08/14 06:51:37 lukem Exp $");
47 #endif
48 #endif /* not lint */
49 
50 #include <sys/param.h>
51 #include <sys/time.h>
52 #include <sys/stat.h>
53 #include <sys/mount.h>
54 
55 #ifdef sunos
56 #include <sys/vnode.h>
57 
58 #include <ufs/inode.h>
59 #include <ufs/fs.h>
60 #else
61 #include <ufs/ufs/dinode.h>
62 #include <ufs/ffs/fs.h>
63 #include <ufs/ffs/ffs_extern.h>
64 #endif
65 
66 #include <protocols/dumprestore.h>
67 
68 #include <ctype.h>
69 #include <err.h>
70 #include <errno.h>
71 #include <fcntl.h>
72 #include <fstab.h>
73 #include <signal.h>
74 #include <stdio.h>
75 #include <stdlib.h>
76 #include <string.h>
77 #include <time.h>
78 #include <unistd.h>
79 
80 #include "dump.h"
81 #include "pathnames.h"
82 
83 gid_t	egid;			/* Retain tty privs for notification */
84 int	notify;			/* notify operator flag */
85 int	blockswritten;		/* number of blocks written on current tape */
86 int	tapeno;			/* current tape number */
87 int	density;		/* density in bytes/0.1" */
88 int	ntrec = NTREC;		/* # tape blocks in each tape record */
89 int	cartridge;		/* Assume non-cartridge tape */
90 long	dev_bsize = 1;		/* recalculated below */
91 long	blocksperfile;		/* output blocks per file */
92 char	*host;			/* remote host (if any) */
93 int	readcache = -1;		/* read cache size (in readblksize blks) */
94 int	readblksize = 32 * 1024; /* read block size */
95 
96 int	main(int, char *[]);
97 static long numarg(char *, long, long);
98 static void obsolete(int *, char **[]);
99 static void usage(void);
100 
101 int
102 main(int argc, char *argv[])
103 {
104 	ino_t ino;
105 	int dirty;
106 	struct dinode *dp;
107 	struct fstab *dt;
108 	struct statfs *mntinfo, fsbuf;
109 	char *map;
110 	int ch;
111 	int i, anydirskipped, bflag = 0, Tflag = 0, Fflag = 0, honorlevel = 1;
112 	ino_t maxino;
113 	time_t tnow, date;
114 	int dirc;
115 	char *mountpoint;
116 	int just_estimate = 0;
117 	char labelstr[LBLSIZE];
118 
119 	spcl.c_date = 0;
120 	(void)time((time_t *)&spcl.c_date);
121 
122 	/* Save setgid bit for use later */
123 	egid = getegid();
124 	setegid(getgid());
125 
126 	tsize = 0;	/* Default later, based on 'c' option for cart tapes */
127 	if ((tape = getenv("TAPE")) == NULL)
128 		tape = _PATH_DEFTAPE;
129 	dumpdates = _PATH_DUMPDATES;
130 	temp = _PATH_DTMP;
131 	strcpy(labelstr, "none");	/* XXX safe strcpy. */
132 	if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0)
133 		quit("TP_BSIZE must be a multiple of DEV_BSIZE\n");
134 	level = '0';
135 
136 	if (argc < 2)
137 		usage();
138 
139 	obsolete(&argc, &argv);
140 	while ((ch = getopt(argc, argv,
141 	    "0123456789B:b:cd:eFf:h:k:L:nr:s:ST:uWw")) != -1)
142 		switch (ch) {
143 		/* dump level */
144 		case '0': case '1': case '2': case '3': case '4':
145 		case '5': case '6': case '7': case '8': case '9':
146 			level = ch;
147 			break;
148 
149 		case 'B':		/* blocks per output file */
150 			blocksperfile = numarg("blocks per file", 1L, 0L);
151 			break;
152 
153 		case 'b':		/* blocks per tape write */
154 			ntrec = numarg("blocks per write", 1L, 1000L);
155 			bflag = 1;
156 			break;
157 
158 		case 'c':		/* Tape is cart. not 9-track */
159 			cartridge = 1;
160 			break;
161 
162 		case 'd':		/* density, in bits per inch */
163 			density = numarg("density", 10L, 327670L) / 10;
164 			if (density >= 625 && !bflag)
165 				ntrec = HIGHDENSITYTREC;
166 			break;
167 
168 		case 'e':		/* eject full tapes */
169 			eflag = 1;
170 			break;
171 
172 		case 'F':		/* files-to-dump is an fs image */
173 			Fflag = 1;
174 			break;
175 
176 		case 'f':		/* output file */
177 			tape = optarg;
178 			break;
179 
180 		case 'h':
181 			honorlevel = numarg("honor level", 0L, 10L);
182 			break;
183 
184 		case 'k':
185 			readblksize = numarg("read block size", 0, 64) * 1024;
186 			break;
187 
188 		case 'L':
189 			/*
190 			 * Note that although there are LBLSIZE characters,
191 			 * the last must be '\0', so the limit on strlen()
192 			 * is really LBLSIZE-1.
193 			 */
194 			strncpy(labelstr, optarg, LBLSIZE);
195 			labelstr[LBLSIZE-1] = '\0';
196 			if (strlen(optarg) > LBLSIZE-1) {
197 				msg(
198 		"WARNING Label `%s' is larger than limit of %d characters.\n",
199 				    optarg, LBLSIZE-1);
200 				msg("WARNING: Using truncated label `%s'.\n",
201 				    labelstr);
202 			}
203 			break;
204 		case 'n':		/* notify operators */
205 			notify = 1;
206 			break;
207 
208 		case 'r':		/* read cache size */
209 			readcache = numarg("read cache size", 0, 512);
210 			break;
211 
212 		case 's':		/* tape size, feet */
213 			tsize = numarg("tape size", 1L, 0L) * 12 * 10;
214 			break;
215 
216 		case 'S':		/* exit after estimating # of tapes */
217 			just_estimate = 1;
218 			break;
219 
220 		case 'T':		/* time of last dump */
221 			spcl.c_ddate = unctime(optarg);
222 			if (spcl.c_ddate < 0) {
223 				(void)fprintf(stderr, "bad time \"%s\"\n",
224 				    optarg);
225 				exit(X_ABORT);
226 			}
227 			Tflag = 1;
228 			lastlevel = '?';
229 			break;
230 
231 		case 'u':		/* update /etc/dumpdates */
232 			uflag = 1;
233 			break;
234 
235 		case 'W':		/* what to do */
236 		case 'w':
237 			lastdump(ch);
238 			exit(0);	/* do nothing else */
239 
240 		default:
241 			usage();
242 		}
243 	argc -= optind;
244 	argv += optind;
245 
246 	if (argc < 1) {
247 		(void)fprintf(stderr,
248 		    "Must specify disk or image, or file list\n");
249 		exit(X_ABORT);
250 	}
251 
252 
253 	/*
254 	 *	determine if disk is a subdirectory, and setup appropriately
255 	 */
256 	getfstab();		/* /etc/fstab snarfed */
257 	disk = NULL;
258 	mountpoint = NULL;
259 	dirc = 0;
260 	for (i = 0; i < argc; i++) {
261 		struct stat sb;
262 
263 		if (lstat(argv[i], &sb) == -1)
264 			quit("Cannot stat %s: %s\n", argv[i], strerror(errno));
265 		if (Fflag || S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) {
266 			disk = argv[i];
267 			if (Fflag && !S_ISREG(sb.st_mode))
268 				quit("%s is not a regular file", disk);
269  multicheck:
270 			if (dirc != 0)
271 				quit(
272 	"Can't dump a disk or image at the same time as a file list\n");
273 			break;
274 		}
275 		if ((dt = fstabsearch(argv[i])) != NULL) {
276 			disk = dt->fs_spec;
277 			mountpoint = xstrdup(dt->fs_file);
278 			goto multicheck;
279 		}
280 		if (statfs(argv[i], &fsbuf) == -1)
281 			quit("Cannot statfs %s: %s\n", argv[i],
282 			    strerror(errno));
283 		disk = fsbuf.f_mntfromname;
284 		if (strcmp(argv[i], fsbuf.f_mntonname) == 0)
285 			goto multicheck;
286 		if (mountpoint == NULL) {
287 			mountpoint = xstrdup(fsbuf.f_mntonname);
288 			if (uflag) {
289 				msg("Ignoring u flag for subdir dump\n");
290 				uflag = 0;
291 			}
292 			if (level > '0') {
293 				msg("Subdir dump is done at level 0\n");
294 				level = '0';
295 			}
296 			msg("Dumping sub files/directories from %s\n",
297 			    mountpoint);
298 		} else {
299 			if (strcmp(mountpoint, fsbuf.f_mntonname) != 0)
300 				quit("%s is not on %s\n", argv[i], mountpoint);
301 		}
302 		msg("Dumping file/directory %s\n", argv[i]);
303 		dirc++;
304 	}
305 	if (mountpoint)
306 		free(mountpoint);
307 
308 	if (dirc == 0) {
309 		argv++;
310 		if (argc != 1) {
311 			(void)fprintf(stderr, "Excess arguments to dump:");
312 			while (--argc)
313 				(void)fprintf(stderr, " %s", *argv++);
314 			(void)fprintf(stderr, "\n");
315 			exit(X_ABORT);
316 		}
317 	}
318 	if (Tflag && uflag) {
319 	        (void)fprintf(stderr,
320 		    "You cannot use the T and u flags together.\n");
321 		exit(X_ABORT);
322 	}
323 	if (strcmp(tape, "-") == 0) {
324 		pipeout++;
325 		tape = "standard output";
326 	}
327 
328 	if (blocksperfile)
329 		blocksperfile = blocksperfile / ntrec * ntrec; /* round down */
330 	else {
331 		/*
332 		 * Determine how to default tape size and density
333 		 *
334 		 *         	density				tape size
335 		 * 9-track	1600 bpi (160 bytes/.1")	2300 ft.
336 		 * 9-track	6250 bpi (625 bytes/.1")	2300 ft.
337 		 * cartridge	8000 bpi (100 bytes/.1")	1700 ft.
338 		 *						(450*4 - slop)
339 		 */
340 		if (density == 0)
341 			density = cartridge ? 100 : 160;
342 		if (tsize == 0)
343 			tsize = cartridge ? 1700L*120L : 2300L*120L;
344 	}
345 
346 	if (strchr(tape, ':')) {
347 		host = tape;
348 		tape = strchr(host, ':');
349 		*tape++ = '\0';
350 #ifdef RDUMP
351 		if (rmthost(host) == 0)
352 			exit(X_ABORT);
353 #else
354 		(void)fprintf(stderr, "remote dump not enabled\n");
355 		exit(X_ABORT);
356 #endif
357 	}
358 
359 	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
360 		signal(SIGHUP, sig);
361 	if (signal(SIGTRAP, SIG_IGN) != SIG_IGN)
362 		signal(SIGTRAP, sig);
363 	if (signal(SIGFPE, SIG_IGN) != SIG_IGN)
364 		signal(SIGFPE, sig);
365 	if (signal(SIGBUS, SIG_IGN) != SIG_IGN)
366 		signal(SIGBUS, sig);
367 	if (signal(SIGSEGV, SIG_IGN) != SIG_IGN)
368 		signal(SIGSEGV, sig);
369 	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
370 		signal(SIGTERM, sig);
371 	if (signal(SIGINT, interrupt) == SIG_IGN)
372 		signal(SIGINT, SIG_IGN);
373 
374 	set_operators();	/* /etc/group snarfed */
375 
376 	/*
377 	 *	disk can be either the full special file name, or
378 	 *	the file system name.
379 	 */
380 	mountpoint = NULL;
381 	if ((dt = fstabsearch(disk)) != NULL) {
382 		disk = rawname(dt->fs_spec);
383 		mountpoint = dt->fs_file;
384 		msg("Found %s on %s in %s\n", disk, mountpoint, _PATH_FSTAB);
385 	} else if ((mntinfo = mntinfosearch(disk)) != NULL) {
386 		disk = rawname(mntinfo->f_mntfromname);
387 		mountpoint = mntinfo->f_mntonname;
388 		msg("Found %s on %s in mount table\n", disk, mountpoint);
389 	}
390 	if (mountpoint != NULL) {
391 		if (dirc != 0)
392 			(void)snprintf(spcl.c_filesys, NAMELEN,
393 			    "a subset of %s", mountpoint);
394 		else
395 			(void)strncpy(spcl.c_filesys, mountpoint, NAMELEN);
396 	} else if (Fflag) {
397 		(void)strncpy(spcl.c_filesys, "a file system image", NAMELEN);
398 	} else {
399 		(void)strncpy(spcl.c_filesys, "an unlisted file system",
400 		    NAMELEN);
401 	}
402 	(void)strncpy(spcl.c_dev, disk, NAMELEN);
403 	(void)strncpy(spcl.c_label, labelstr, sizeof(spcl.c_label) - 1);
404 	(void)gethostname(spcl.c_host, NAMELEN);
405 	spcl.c_host[sizeof(spcl.c_host) - 1] = '\0';
406 
407 	if ((diskfd = open(disk, O_RDONLY)) < 0) {
408 		msg("Cannot open %s\n", disk);
409 		exit(X_ABORT);
410 	}
411 	sync();
412 
413 	needswap = fs_read_sblock(sblock_buf);
414 
415 	spcl.c_level = iswap32(level - '0');
416 	spcl.c_type = iswap32(TS_TAPE);
417 	spcl.c_date = iswap32(spcl.c_date);
418 	spcl.c_ddate = iswap32(spcl.c_ddate);
419 	if (!Tflag)
420 	        getdumptime();		/* /etc/dumpdates snarfed */
421 
422 	date = iswap32(spcl.c_date);
423 	msg("Date of this level %c dump: %s", level,
424 		spcl.c_date == 0 ? "the epoch\n" : ctime(&date));
425 	date = iswap32(spcl.c_ddate);
426  	msg("Date of last level %c dump: %s", lastlevel,
427 		spcl.c_ddate == 0 ? "the epoch\n" : ctime(&date));
428 	msg("Dumping ");
429 	if (dirc != 0)
430 		msgtail("a subset of ");
431 	msgtail("%s (%s) ", disk, spcl.c_filesys);
432 	if (host)
433 		msgtail("to %s on host %s\n", tape, host);
434 	else
435 		msgtail("to %s\n", tape);
436 	msg("Label: %s\n", labelstr);
437 
438 	ufsib = fs_parametrize();
439 
440 	dev_bshift = ffs(dev_bsize) - 1;
441 	if (dev_bsize != (1 << dev_bshift))
442 		quit("dev_bsize (%ld) is not a power of 2", dev_bsize);
443 	tp_bshift = ffs(TP_BSIZE) - 1;
444 	if (TP_BSIZE != (1 << tp_bshift))
445 		quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE);
446 	maxino = fs_maxino();
447 	mapsize = roundup(howmany(maxino, NBBY), TP_BSIZE);
448 	usedinomap = (char *)xcalloc((unsigned) mapsize, sizeof(char));
449 	dumpdirmap = (char *)xcalloc((unsigned) mapsize, sizeof(char));
450 	dumpinomap = (char *)xcalloc((unsigned) mapsize, sizeof(char));
451 	tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1);
452 
453 	nonodump = iswap32(spcl.c_level) < honorlevel;
454 
455 	initcache(readcache, readblksize);
456 
457 	(void)signal(SIGINFO, statussig);
458 
459 	msg("mapping (Pass I) [regular files]\n");
460 	anydirskipped = mapfiles(maxino, &tapesize, mountpoint,
461 	    (dirc ? argv : NULL));
462 
463 	msg("mapping (Pass II) [directories]\n");
464 	while (anydirskipped) {
465 		anydirskipped = mapdirs(maxino, &tapesize);
466 	}
467 
468 	if (pipeout) {
469 		tapesize += 10;	/* 10 trailer blocks */
470 		msg("estimated %ld tape blocks.\n", tapesize);
471 	} else {
472 		double fetapes;
473 
474 		if (blocksperfile)
475 			fetapes = (double) tapesize / blocksperfile;
476 		else if (cartridge) {
477 			/* Estimate number of tapes, assuming streaming stops at
478 			   the end of each block written, and not in mid-block.
479 			   Assume no erroneous blocks; this can be compensated
480 			   for with an artificially low tape size. */
481 			fetapes =
482 			(	  tapesize	/* blocks */
483 				* TP_BSIZE	/* bytes/block */
484 				* (1.0/density)	/* 0.1" / byte */
485 			  +
486 				  tapesize	/* blocks */
487 				* (1.0/ntrec)	/* streaming-stops per block */
488 				* 15.48		/* 0.1" / streaming-stop */
489 			) * (1.0 / tsize );	/* tape / 0.1" */
490 		} else {
491 			/* Estimate number of tapes, for old fashioned 9-track
492 			   tape */
493 			int tenthsperirg = (density == 625) ? 3 : 7;
494 			fetapes =
495 			(	  tapesize	/* blocks */
496 				* TP_BSIZE	/* bytes / block */
497 				* (1.0/density)	/* 0.1" / byte */
498 			  +
499 				  tapesize	/* blocks */
500 				* (1.0/ntrec)	/* IRG's / block */
501 				* tenthsperirg	/* 0.1" / IRG */
502 			) * (1.0 / tsize );	/* tape / 0.1" */
503 		}
504 		etapes = fetapes;		/* truncating assignment */
505 		etapes++;
506 		/* count the dumped inodes map on each additional tape */
507 		tapesize += (etapes - 1) *
508 			(howmany(mapsize * sizeof(char), TP_BSIZE) + 1);
509 		tapesize += etapes + 10;	/* headers + 10 trailer blks */
510 		msg("estimated %ld tape blocks on %3.2f tape(s).\n",
511 		    tapesize, fetapes);
512 	}
513 	/*
514 	 * If the user only wants an estimate of the number of
515 	 * tapes, exit now.
516 	 */
517 	if (just_estimate)
518 		exit(0);
519 
520 	/*
521 	 * Allocate tape buffer.
522 	 */
523 	if (!alloctape())
524 		quit("can't allocate tape buffers - try a smaller blocking factor.\n");
525 
526 	startnewtape(1);
527 	(void)time((time_t *)&(tstart_writing));
528 	xferrate = 0;
529 	dumpmap(usedinomap, TS_CLRI, maxino - 1);
530 
531 	msg("dumping (Pass III) [directories]\n");
532 	dirty = 0;		/* XXX just to get gcc to shut up */
533 	for (map = dumpdirmap, ino = 1; ino < maxino; ino++) {
534 		if (((ino - 1) % NBBY) == 0)	/* map is offset by 1 */
535 			dirty = *map++;
536 		else
537 			dirty >>= 1;
538 		if ((dirty & 1) == 0)
539 			continue;
540 		/*
541 		 * Skip directory inodes deleted and maybe reallocated
542 		 */
543 		dp = getino(ino);
544 		if ((dp->di_mode & IFMT) != IFDIR)
545 			continue;
546 		(void)dumpino(dp, ino);
547 	}
548 
549 	msg("dumping (Pass IV) [regular files]\n");
550 	for (map = dumpinomap, ino = 1; ino < maxino; ino++) {
551 		int mode;
552 
553 		if (((ino - 1) % NBBY) == 0)	/* map is offset by 1 */
554 			dirty = *map++;
555 		else
556 			dirty >>= 1;
557 		if ((dirty & 1) == 0)
558 			continue;
559 		/*
560 		 * Skip inodes deleted and reallocated as directories.
561 		 */
562 		dp = getino(ino);
563 		mode = dp->di_mode & IFMT;
564 		if (mode == IFDIR)
565 			continue;
566 		(void)dumpino(dp, ino);
567 	}
568 
569 	spcl.c_type = iswap32(TS_END);
570 	for (i = 0; i < ntrec; i++)
571 		writeheader(maxino - 1);
572 	if (pipeout)
573 		msg("%d tape blocks\n",iswap32(spcl.c_tapea));
574 	else
575 		msg("%d tape blocks on %d volume%s\n",
576 		    iswap32(spcl.c_tapea), iswap32(spcl.c_volume),
577 		    (iswap32(spcl.c_volume) == 1) ? "" : "s");
578 	tnow = do_stats();
579 	date = iswap32(spcl.c_date);
580 	msg("Date of this level %c dump: %s", level,
581 		spcl.c_date == 0 ? "the epoch\n" : ctime(&date));
582 	msg("Date this dump completed:  %s", ctime(&tnow));
583 	msg("Average transfer rate: %d KB/s\n", xferrate / tapeno);
584 	putdumptime();
585 	trewind(0);
586 	broadcast("DUMP IS DONE!\7\7\n");
587 	msg("DUMP IS DONE\n");
588 	Exit(X_FINOK);
589 	/* NOTREACHED */
590 	exit(X_FINOK);		/* XXX: to satisfy gcc */
591 }
592 
593 static void
594 usage(void)
595 {
596 
597 	(void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
598 "usage: dump [-0123456789ceFnu] [-B records] [-b blocksize] [-d density]",
599 "            [-f file] [-h level] [-k read block size] [-L label]",
600 "            [-r read cache size] [-s feet] [-T date] file system",
601 "       dump [-W | -w]");
602 	exit(1);
603 }
604 
605 /*
606  * Pick up a numeric argument.  It must be nonnegative and in the given
607  * range (except that a vmax of 0 means unlimited).
608  */
609 static long
610 numarg(char *meaning, long vmin, long vmax)
611 {
612 	char *p;
613 	long val;
614 
615 	val = strtol(optarg, &p, 10);
616 	if (*p)
617 		errx(1, "illegal %s -- %s", meaning, optarg);
618 	if (val < vmin || (vmax && val > vmax))
619 		errx(1, "%s must be between %ld and %ld", meaning, vmin, vmax);
620 	return (val);
621 }
622 
623 void
624 sig(int signo)
625 {
626 
627 	switch(signo) {
628 	case SIGALRM:
629 	case SIGBUS:
630 	case SIGFPE:
631 	case SIGHUP:
632 	case SIGTERM:
633 	case SIGTRAP:
634 		if (pipeout)
635 			quit("Signal on pipe: cannot recover\n");
636 		msg("Rewriting attempted as response to signal %s.\n", sys_siglist[signo]);
637 		(void)fflush(stderr);
638 		(void)fflush(stdout);
639 		close_rewind();
640 		exit(X_REWRITE);
641 		/* NOTREACHED */
642 	case SIGSEGV:
643 		msg("SIGSEGV: ABORTING!\n");
644 		(void)signal(SIGSEGV, SIG_DFL);
645 		(void)kill(0, SIGSEGV);
646 		/* NOTREACHED */
647 	}
648 }
649 
650 char *
651 rawname(char *cp)
652 {
653 	static char rawbuf[MAXPATHLEN];
654 	char *dp = strrchr(cp, '/');
655 
656 	if (dp == NULL)
657 		return (NULL);
658 	*dp = '\0';
659 	(void)snprintf(rawbuf, sizeof rawbuf, "%s/r%s", cp, dp + 1);
660 	*dp = '/';
661 	return (rawbuf);
662 }
663 
664 /*
665  * obsolete --
666  *	Change set of key letters and ordered arguments into something
667  *	getopt(3) will like.
668  */
669 static void
670 obsolete(int *argcp, char **argvp[])
671 {
672 	int argc, flags;
673 	char *ap, **argv, *flagsp, **nargv, *p;
674 
675 	/* Setup. */
676 	argv = *argvp;
677 	argc = *argcp;
678 
679 	/* Return if no arguments or first argument has leading dash. */
680 	ap = argv[1];
681 	if (argc == 1 || *ap == '-')
682 		return;
683 
684 	/* Allocate space for new arguments. */
685 	*argvp = nargv = xmalloc((argc + 1) * sizeof(char *));
686 	p = flagsp = xmalloc(strlen(ap) + 2);
687 
688 	*nargv++ = *argv;
689 	argv += 2;
690 
691 	for (flags = 0; *ap; ++ap) {
692 		switch (*ap) {
693 		case 'B':
694 		case 'b':
695 		case 'd':
696 		case 'f':
697 		case 'h':
698 		case 's':
699 		case 'T':
700 			if (*argv == NULL) {
701 				warnx("option requires an argument -- %c", *ap);
702 				usage();
703 			}
704 			nargv[0] = xmalloc(strlen(*argv) + 2 + 1);
705 			nargv[0][0] = '-';
706 			nargv[0][1] = *ap;
707 			(void)strcpy(&nargv[0][2], *argv); /* XXX safe strcpy */
708 			++argv;
709 			++nargv;
710 			break;
711 		default:
712 			if (!flags) {
713 				*p++ = '-';
714 				flags = 1;
715 			}
716 			*p++ = *ap;
717 			break;
718 		}
719 	}
720 
721 	/* Terminate flags. */
722 	if (flags) {
723 		*p = '\0';
724 		*nargv++ = flagsp;
725 	}
726 
727 	/* Copy remaining arguments. */
728 	while ((*nargv++ = *argv++) != NULL)
729 		;
730 
731 	/* Update argument count. */
732 	*argcp = nargv - *argvp - 1;
733 }
734 
735 
736 void *
737 xcalloc(size_t number, size_t size)
738 {
739 	void *p;
740 
741 	p = calloc(number, size);
742 	if (p == NULL)
743 		quit("%s\n", strerror(errno));
744 	return (p);
745 }
746 
747 void *
748 xmalloc(size_t size)
749 {
750 	void *p;
751 
752 	p = malloc(size);
753 	if (p == NULL)
754 		quit("%s\n", strerror(errno));
755 	return (p);
756 }
757 
758 char *
759 xstrdup(const char *str)
760 {
761 	char *p;
762 
763 	p = strdup(str);
764 	if (p == NULL)
765 		quit("%s\n", strerror(errno));
766 	return (p);
767 }
768