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