1*dbc3f941Srillig /* $NetBSD: main.c,v 1.79 2024/10/04 11:38:44 rillig Exp $ */ 20114e805Scgd 3108d8947Scgd /*- 4ccfa3742Smycroft * Copyright (c) 1980, 1991, 1993, 1994 5ccfa3742Smycroft * The Regents of the University of California. All rights reserved. 6108d8947Scgd * 7108d8947Scgd * Redistribution and use in source and binary forms, with or without 8108d8947Scgd * modification, are permitted provided that the following conditions 9108d8947Scgd * are met: 10108d8947Scgd * 1. Redistributions of source code must retain the above copyright 11108d8947Scgd * notice, this list of conditions and the following disclaimer. 12108d8947Scgd * 2. Redistributions in binary form must reproduce the above copyright 13108d8947Scgd * notice, this list of conditions and the following disclaimer in the 14108d8947Scgd * documentation and/or other materials provided with the distribution. 15bf07c871Sagc * 3. Neither the name of the University nor the names of its contributors 16108d8947Scgd * may be used to endorse or promote products derived from this software 17108d8947Scgd * without specific prior written permission. 18108d8947Scgd * 19108d8947Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20108d8947Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21108d8947Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22108d8947Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23108d8947Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24108d8947Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25108d8947Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26108d8947Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27108d8947Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28108d8947Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29108d8947Scgd * SUCH DAMAGE. 30108d8947Scgd */ 31108d8947Scgd 32a84bab53Slukem #include <sys/cdefs.h> 33108d8947Scgd #ifndef lint 346543a91fSlukem __COPYRIGHT("@(#) Copyright (c) 1980, 1991, 1993, 1994\ 356543a91fSlukem The Regents of the University of California. All rights reserved."); 36108d8947Scgd #endif /* not lint */ 37108d8947Scgd 38108d8947Scgd #ifndef lint 390114e805Scgd #if 0 40919c9246Slukem static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/1/95"; 410114e805Scgd #else 42*dbc3f941Srillig __RCSID("$NetBSD: main.c,v 1.79 2024/10/04 11:38:44 rillig Exp $"); 430114e805Scgd #endif 44108d8947Scgd #endif /* not lint */ 45108d8947Scgd 46108d8947Scgd #include <sys/param.h> 47108d8947Scgd #include <sys/time.h> 484c54f5b7Slukem #include <sys/stat.h> 494c54f5b7Slukem #include <sys/mount.h> 50262ce6cbSbouyer #include <sys/sysctl.h> 514c54f5b7Slukem 52919c9246Slukem #include <ufs/ffs/fs.h> 5334ccbd43Sbouyer #include <ufs/ffs/ffs_extern.h> 54ccfa3742Smycroft 55ccfa3742Smycroft #include <ctype.h> 56dd6c22edSmycroft #include <err.h> 57ccfa3742Smycroft #include <errno.h> 58108d8947Scgd #include <fcntl.h> 59108d8947Scgd #include <fstab.h> 60ccfa3742Smycroft #include <signal.h> 61108d8947Scgd #include <stdio.h> 62108d8947Scgd #include <stdlib.h> 63108d8947Scgd #include <string.h> 64578deb45Slukem #include <time.h> 65ccfa3742Smycroft #include <unistd.h> 665727faddSchristos #include <util.h> 67ccfa3742Smycroft 68108d8947Scgd #include "dump.h" 69108d8947Scgd #include "pathnames.h" 701f51c280Shannken #include "snapshot.h" 71108d8947Scgd 726ce4f404Sjoerg union u_spcl u_spcl; 736ce4f404Sjoerg struct ufsi *ufsib; 746ce4f404Sjoerg int mapsize; 756ce4f404Sjoerg char *usedinomap; 766ce4f404Sjoerg char *dumpdirmap; 776ce4f404Sjoerg char *dumpinomap; 786ce4f404Sjoerg char *disk; 796ce4f404Sjoerg char *disk_dev; 806ce4f404Sjoerg const char *tape; 816ce4f404Sjoerg const char *dumpdates; 826ce4f404Sjoerg const char *temp; 836ce4f404Sjoerg char lastlevel; 846ce4f404Sjoerg char level; 856ce4f404Sjoerg int uflag; 866ce4f404Sjoerg const char *dumpdev; 876ce4f404Sjoerg int eflag; 886ce4f404Sjoerg int lflag; 896ce4f404Sjoerg int diskfd; 906ce4f404Sjoerg int tapefd; 916ce4f404Sjoerg int pipeout; 926ce4f404Sjoerg int trueinc; 936ce4f404Sjoerg ino_t curino; 946ce4f404Sjoerg int newtape; 956ce4f404Sjoerg u_int64_t tapesize; 966ce4f404Sjoerg long tsize; 976ce4f404Sjoerg long asize; 986ce4f404Sjoerg int etapes; 996ce4f404Sjoerg int nonodump; 1006ce4f404Sjoerg int unlimited; 1016ce4f404Sjoerg time_t tstart_writing; 1026ce4f404Sjoerg time_t tstart_volume; 1036ce4f404Sjoerg int xferrate; 1046ce4f404Sjoerg char sblock_buf[MAXBSIZE]; 1056ce4f404Sjoerg int dev_bshift; 1066ce4f404Sjoerg int tp_bshift; 1076ce4f404Sjoerg int needswap; 1086ce4f404Sjoerg 109a152a6a9Sblymn int timestamp; /* print message timestamps */ 110e89e2124Sscw int notify; /* notify operator flag */ 111b2fc8900Skre u_int64_t blockswritten; /* number of blocks written on current tape */ 112e89e2124Sscw int tapeno; /* current tape number */ 113e89e2124Sscw int density; /* density in bytes/0.1" */ 114108d8947Scgd int ntrec = NTREC; /* # tape blocks in each tape record */ 115e89e2124Sscw int cartridge; /* Assume non-cartridge tape */ 116108d8947Scgd long dev_bsize = 1; /* recalculated below */ 117e89e2124Sscw long blocksperfile; /* output blocks per file */ 11864747563Schristos const char *host; /* remote host (if any) */ 119491d912fSbouyer int readcache = -1; /* read cache size (in readblksize blks) */ 120262ce6cbSbouyer int readblksize = -1; /* read block size */ 121a152a6a9Sblymn char default_time_string[] = "%T %Z"; /* default timestamp string */ 122a152a6a9Sblymn char *time_string = default_time_string; /* timestamp string */ 123108d8947Scgd 12464747563Schristos static long numarg(const char *, long, long); 12599feaf5bSlukem static void obsolete(int *, char **[]); 12699feaf5bSlukem static void usage(void); 127ccfa3742Smycroft 128ccfa3742Smycroft int 12999feaf5bSlukem main(int argc, char *argv[]) 130108d8947Scgd { 13193da79deSlukem ino_t ino; 13293da79deSlukem int dirty; 13342614ed3Sfvdl union dinode *dp; 13493da79deSlukem struct fstab *dt; 1356bd1d6d4Schristos struct statvfs *mntinfo, fsbuf; 13664747563Schristos char *map, *cp; 13793da79deSlukem int ch; 138b1a3e114Slukem int i, anydirskipped, bflag = 0, Tflag = 0, Fflag = 0, honorlevel = 1; 1391f51c280Shannken int snap_internal = 0; 140108d8947Scgd ino_t maxino; 14134ccbd43Sbouyer time_t tnow, date; 1429af1692eSlukem int dirc; 1439af1692eSlukem char *mountpoint; 144865fe390Slukem int just_estimate = 0; 1452faa2d1fSlukem char labelstr[LBLSIZE]; 146ca11e1e6Schristos char buf[MAXPATHLEN], rbuf[MAXPATHLEN]; 147a152a6a9Sblymn char *new_time_format; 1481f51c280Shannken char *snap_backup = NULL; 149108d8947Scgd 150108d8947Scgd spcl.c_date = 0; 151b73cca7bSlukem (void)time(&tnow); 152b73cca7bSlukem spcl.c_date = tnow; 153a152a6a9Sblymn tzset(); /* set up timezone for strftime */ 154a152a6a9Sblymn if ((new_time_format = getenv("TIMEFORMAT")) != NULL) 155a152a6a9Sblymn time_string = new_time_format; 156108d8947Scgd 157108d8947Scgd tsize = 0; /* Default later, based on 'c' option for cart tapes */ 1582b5cf470Smrg if ((tape = getenv("TAPE")) == NULL) 159108d8947Scgd tape = _PATH_DEFTAPE; 160108d8947Scgd dumpdates = _PATH_DUMPDATES; 161108d8947Scgd temp = _PATH_DTMP; 1622faa2d1fSlukem strcpy(labelstr, "none"); /* XXX safe strcpy. */ 163108d8947Scgd if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0) 164c8d11eb8Schristos quit("TP_BSIZE must be a multiple of DEV_BSIZE"); 165108d8947Scgd level = '0'; 166a152a6a9Sblymn timestamp = 0; 167108d8947Scgd 168dd6c22edSmycroft if (argc < 2) 169dd6c22edSmycroft usage(); 170108d8947Scgd 171dd6c22edSmycroft obsolete(&argc, &argv); 172865fe390Slukem while ((ch = getopt(argc, argv, 1737aae2902Sperseant "0123456789aB:b:cd:D:eFf:h:ik:l:L:nr:s:StT:uU:Wwx:X")) != -1) 174dd6c22edSmycroft switch (ch) { 175dd6c22edSmycroft /* dump level */ 176dd6c22edSmycroft case '0': case '1': case '2': case '3': case '4': 177dd6c22edSmycroft case '5': case '6': case '7': case '8': case '9': 178dd6c22edSmycroft level = ch; 179108d8947Scgd break; 180108d8947Scgd 1819dcaec9cSlukem case 'a': /* `auto-size', Write to EOM. */ 1829dcaec9cSlukem unlimited = 1; 1839dcaec9cSlukem break; 1849dcaec9cSlukem 185108d8947Scgd case 'B': /* blocks per output file */ 186dd6c22edSmycroft blocksperfile = numarg("blocks per file", 1L, 0L); 187dd6c22edSmycroft break; 188dd6c22edSmycroft 189dd6c22edSmycroft case 'b': /* blocks per tape write */ 190dd6c22edSmycroft ntrec = numarg("blocks per write", 1L, 1000L); 191b31dfde1Smikel bflag = 1; 192108d8947Scgd break; 193108d8947Scgd 194108d8947Scgd case 'c': /* Tape is cart. not 9-track */ 195ccfa3742Smycroft cartridge = 1; 196ccfa3742Smycroft break; 197108d8947Scgd 198dd6c22edSmycroft case 'd': /* density, in bits per inch */ 199dd6c22edSmycroft density = numarg("density", 10L, 327670L) / 10; 200dd6c22edSmycroft if (density >= 625 && !bflag) 201dd6c22edSmycroft ntrec = HIGHDENSITYTREC; 202ccfa3742Smycroft break; 203108d8947Scgd 2047aae2902Sperseant case 'D': /* specify alt. dumpdates file */ 2057aae2902Sperseant dumpdates = optarg; 2067aae2902Sperseant break; 2077aae2902Sperseant 208d6be44d3Stron case 'e': /* eject full tapes */ 209d6be44d3Stron eflag = 1; 210d6be44d3Stron break; 211d6be44d3Stron 212b1a3e114Slukem case 'F': /* files-to-dump is an fs image */ 213b1a3e114Slukem Fflag = 1; 214b1a3e114Slukem break; 215b1a3e114Slukem 216dd6c22edSmycroft case 'f': /* output file */ 217dd6c22edSmycroft tape = optarg; 218dd6c22edSmycroft break; 219dd6c22edSmycroft 220dd6c22edSmycroft case 'h': 221dd6c22edSmycroft honorlevel = numarg("honor level", 0L, 10L); 222ccfa3742Smycroft break; 223108d8947Scgd 22420ebd2a4Schristos case 'i': /* "true incremental" regardless level */ 22520ebd2a4Schristos level = 'i'; 226c40eb1e7Schristos trueinc = 1; 227c40eb1e7Schristos break; 228c40eb1e7Schristos 229491d912fSbouyer case 'k': 230491d912fSbouyer readblksize = numarg("read block size", 0, 64) * 1024; 231491d912fSbouyer break; 232491d912fSbouyer 233740dbf0dSbouyer case 'l': /* autoload after eject full tapes */ 234740dbf0dSbouyer eflag = 1; 235cf398818Sbouyer lflag = numarg("timeout (in seconds)", 1, 0); 236740dbf0dSbouyer break; 237740dbf0dSbouyer 2382faa2d1fSlukem case 'L': 2392faa2d1fSlukem /* 2402faa2d1fSlukem * Note that although there are LBLSIZE characters, 2412faa2d1fSlukem * the last must be '\0', so the limit on strlen() 2422faa2d1fSlukem * is really LBLSIZE-1. 2432faa2d1fSlukem */ 24494ccb14aSitojun if (strlcpy(labelstr, optarg, sizeof(labelstr)) 24594ccb14aSitojun >= sizeof(labelstr)) { 2462faa2d1fSlukem msg( 247bc0b1329Sitojun "WARNING Label `%s' is larger than limit of %lu characters.\n", 248bc0b1329Sitojun optarg, 249bc0b1329Sitojun (unsigned long)sizeof(labelstr) - 1); 2502faa2d1fSlukem msg("WARNING: Using truncated label `%s'.\n", 2512faa2d1fSlukem labelstr); 2522faa2d1fSlukem } 2532faa2d1fSlukem break; 254108d8947Scgd case 'n': /* notify operators */ 255ccfa3742Smycroft notify = 1; 256ccfa3742Smycroft break; 257ccfa3742Smycroft 258491d912fSbouyer case 'r': /* read cache size */ 259491d912fSbouyer readcache = numarg("read cache size", 0, 512); 260491d912fSbouyer break; 261491d912fSbouyer 262dd6c22edSmycroft case 's': /* tape size, feet */ 263dd6c22edSmycroft tsize = numarg("tape size", 1L, 0L) * 12 * 10; 264ccfa3742Smycroft break; 265108d8947Scgd 266865fe390Slukem case 'S': /* exit after estimating # of tapes */ 267865fe390Slukem just_estimate = 1; 268865fe390Slukem break; 269865fe390Slukem 270a152a6a9Sblymn case 't': 271a152a6a9Sblymn timestamp = 1; 272a152a6a9Sblymn break; 273a152a6a9Sblymn 274dd6c22edSmycroft case 'T': /* time of last dump */ 275dd6c22edSmycroft spcl.c_ddate = unctime(optarg); 276dd6c22edSmycroft if (spcl.c_ddate < 0) { 277dd6c22edSmycroft (void)fprintf(stderr, "bad time \"%s\"\n", 278dd6c22edSmycroft optarg); 2798975510aSlukem exit(X_STARTUP); 280108d8947Scgd } 281dd6c22edSmycroft Tflag = 1; 282dd6c22edSmycroft lastlevel = '?'; 283dd6c22edSmycroft break; 284dd6c22edSmycroft 285dd6c22edSmycroft case 'u': /* update /etc/dumpdates */ 286dd6c22edSmycroft uflag = 1; 287dd6c22edSmycroft break; 288dd6c22edSmycroft 289eeb96f8cSmanu case 'U': /* dump device in /etc/dumpdates */ 290eeb96f8cSmanu dumpdev = optarg; 291eeb96f8cSmanu break; 292eeb96f8cSmanu 293dd6c22edSmycroft case 'W': /* what to do */ 294dd6c22edSmycroft case 'w': 295dd6c22edSmycroft lastdump(ch); 2968975510aSlukem exit(X_FINOK); /* do nothing else */ 297dd6c22edSmycroft 2981f51c280Shannken case 'x': 2991f51c280Shannken snap_backup = optarg; 3001f51c280Shannken break; 3011f51c280Shannken 3021f51c280Shannken case 'X': 3031f51c280Shannken snap_internal = 1; 3041f51c280Shannken break; 3051f51c280Shannken 306dd6c22edSmycroft default: 307dd6c22edSmycroft usage(); 308108d8947Scgd } 309dd6c22edSmycroft argc -= optind; 310dd6c22edSmycroft argv += optind; 311dd6c22edSmycroft 312108d8947Scgd if (argc < 1) { 313b1a3e114Slukem (void)fprintf(stderr, 314b1a3e114Slukem "Must specify disk or image, or file list\n"); 3158975510aSlukem exit(X_STARTUP); 316ccfa3742Smycroft } 3174c54f5b7Slukem 3189af1692eSlukem 3194c54f5b7Slukem /* 3204c54f5b7Slukem * determine if disk is a subdirectory, and setup appropriately 3214c54f5b7Slukem */ 3229af1692eSlukem getfstab(); /* /etc/fstab snarfed */ 3239af1692eSlukem disk = NULL; 324fcd5ec48Shannken disk_dev = NULL; 3259af1692eSlukem mountpoint = NULL; 3269af1692eSlukem dirc = 0; 3274c54f5b7Slukem for (i = 0; i < argc; i++) { 3284c54f5b7Slukem struct stat sb; 3296fa7c341Smlelstv int error; 3304c54f5b7Slukem 3316fa7c341Smlelstv error = lstat(argv[i], &sb); 3326fa7c341Smlelstv if (Fflag || (!error && (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)))) { 3336fa7c341Smlelstv if (error) 334c8d11eb8Schristos quite(errno, "can't stat %s", argv[i]); 3359af1692eSlukem disk = argv[i]; 3369af1692eSlukem multicheck: 3379af1692eSlukem if (dirc != 0) 338c8d11eb8Schristos quit("can't dump a disk or image at the same" 339c8d11eb8Schristos " time as a file list"); 3404c54f5b7Slukem break; 3414c54f5b7Slukem } 3429af1692eSlukem if ((dt = fstabsearch(argv[i])) != NULL) { 3436fa7c341Smlelstv disk = argv[i]; 344de85f7e4Shannken mountpoint = xstrdup(dt->fs_file); 3459af1692eSlukem goto multicheck; 3464c54f5b7Slukem } 3476bd1d6d4Schristos if (statvfs(argv[i], &fsbuf) == -1) 348c8d11eb8Schristos quite(errno, "can't statvfs %s", argv[i]); 3499af1692eSlukem disk = fsbuf.f_mntfromname; 3509af1692eSlukem if (strcmp(argv[i], fsbuf.f_mntonname) == 0) 3519af1692eSlukem goto multicheck; 3529af1692eSlukem if (mountpoint == NULL) { 3539af1692eSlukem mountpoint = xstrdup(fsbuf.f_mntonname); 3544c54f5b7Slukem if (uflag) { 3554c54f5b7Slukem msg("Ignoring u flag for subdir dump\n"); 3564c54f5b7Slukem uflag = 0; 3574c54f5b7Slukem } 3584c54f5b7Slukem if (level > '0') { 3594c54f5b7Slukem msg("Subdir dump is done at level 0\n"); 3604c54f5b7Slukem level = '0'; 3614c54f5b7Slukem } 3629af1692eSlukem msg("Dumping sub files/directories from %s\n", 3639af1692eSlukem mountpoint); 3644c54f5b7Slukem } else { 3659af1692eSlukem if (strcmp(mountpoint, fsbuf.f_mntonname) != 0) 366c8d11eb8Schristos quit("%s is not on %s", argv[i], mountpoint); 3674c54f5b7Slukem } 3684c54f5b7Slukem msg("Dumping file/directory %s\n", argv[i]); 3699af1692eSlukem dirc++; 3704c54f5b7Slukem } 3719af1692eSlukem if (mountpoint) 3729af1692eSlukem free(mountpoint); 3739af1692eSlukem 3749af1692eSlukem if (dirc == 0) { 3759af1692eSlukem argv++; 3764c54f5b7Slukem if (argc != 1) { 3774c54f5b7Slukem (void)fprintf(stderr, "Excess arguments to dump:"); 3784c54f5b7Slukem while (--argc) 379108d8947Scgd (void)fprintf(stderr, " %s", *argv++); 380108d8947Scgd (void)fprintf(stderr, "\n"); 3818975510aSlukem exit(X_STARTUP); 382108d8947Scgd } 3834c54f5b7Slukem } 384108d8947Scgd if (Tflag && uflag) { 385108d8947Scgd (void)fprintf(stderr, 386108d8947Scgd "You cannot use the T and u flags together.\n"); 3878975510aSlukem exit(X_STARTUP); 388108d8947Scgd } 389108d8947Scgd if (strcmp(tape, "-") == 0) { 390108d8947Scgd pipeout++; 391108d8947Scgd tape = "standard output"; 392108d8947Scgd } 393108d8947Scgd 394108d8947Scgd if (blocksperfile) 395108d8947Scgd blocksperfile = blocksperfile / ntrec * ntrec; /* round down */ 3969dcaec9cSlukem else if (!unlimited) { 397108d8947Scgd /* 398108d8947Scgd * Determine how to default tape size and density 399108d8947Scgd * 400108d8947Scgd * density tape size 401108d8947Scgd * 9-track 1600 bpi (160 bytes/.1") 2300 ft. 402108d8947Scgd * 9-track 6250 bpi (625 bytes/.1") 2300 ft. 403108d8947Scgd * cartridge 8000 bpi (100 bytes/.1") 1700 ft. 404108d8947Scgd * (450*4 - slop) 405108d8947Scgd */ 406108d8947Scgd if (density == 0) 407108d8947Scgd density = cartridge ? 100 : 160; 408108d8947Scgd if (tsize == 0) 409108d8947Scgd tsize = cartridge ? 1700L*120L : 2300L*120L; 410108d8947Scgd } 411108d8947Scgd 412*dbc3f941Srillig /* LINTED 346 "call to 'strchr' effectively discards 'const'" */ 41364747563Schristos if ((cp = strchr(tape, ':')) != NULL) { 414108d8947Scgd host = tape; 41564747563Schristos /* This is fine, because all the const strings don't have : */ 41664747563Schristos *cp++ = '\0'; 41764747563Schristos tape = cp; 418108d8947Scgd #ifdef RDUMP 419108d8947Scgd if (rmthost(host) == 0) 4208975510aSlukem exit(X_STARTUP); 421108d8947Scgd #else 422108d8947Scgd (void)fprintf(stderr, "remote dump not enabled\n"); 4238975510aSlukem exit(X_STARTUP); 424108d8947Scgd #endif 425108d8947Scgd } 426108d8947Scgd 427108d8947Scgd if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 428108d8947Scgd signal(SIGHUP, sig); 429108d8947Scgd if (signal(SIGTRAP, SIG_IGN) != SIG_IGN) 430108d8947Scgd signal(SIGTRAP, sig); 431108d8947Scgd if (signal(SIGFPE, SIG_IGN) != SIG_IGN) 432108d8947Scgd signal(SIGFPE, sig); 433108d8947Scgd if (signal(SIGBUS, SIG_IGN) != SIG_IGN) 434108d8947Scgd signal(SIGBUS, sig); 43542614ed3Sfvdl #if 0 436108d8947Scgd if (signal(SIGSEGV, SIG_IGN) != SIG_IGN) 437108d8947Scgd signal(SIGSEGV, sig); 43842614ed3Sfvdl #endif 439108d8947Scgd if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 440108d8947Scgd signal(SIGTERM, sig); 441108d8947Scgd if (signal(SIGINT, interrupt) == SIG_IGN) 442108d8947Scgd signal(SIGINT, SIG_IGN); 443108d8947Scgd 444108d8947Scgd /* 4459af1692eSlukem * disk can be either the full special file name, or 4469af1692eSlukem * the file system name. 447108d8947Scgd */ 4489af1692eSlukem mountpoint = NULL; 4491f51c280Shannken mntinfo = mntinfosearch(disk); 4509af1692eSlukem if ((dt = fstabsearch(disk)) != NULL) { 4515727faddSchristos if (getfsspecname(buf, sizeof(buf), dt->fs_spec) == NULL) 452c8d11eb8Schristos quite(errno, "can't resolve mount %s (%s)", dt->fs_spec, 453c8d11eb8Schristos buf); 454ca11e1e6Schristos if (getdiskrawname(rbuf, sizeof(rbuf), buf) == NULL) 455c8d11eb8Schristos quite(errno, "can't get disk raw name for %s", buf); 456ca11e1e6Schristos disk = rbuf; 4579af1692eSlukem mountpoint = dt->fs_file; 4589af1692eSlukem msg("Found %s on %s in %s\n", disk, mountpoint, _PATH_FSTAB); 4591f51c280Shannken } else if (mntinfo != NULL) { 460ca11e1e6Schristos if (getdiskrawname(rbuf, sizeof(rbuf), mntinfo->f_mntfromname) 461ca11e1e6Schristos == NULL) 462c8d11eb8Schristos quite(errno, "can't get disk raw name for %s", 463c8d11eb8Schristos mntinfo->f_mntfromname); 4646fa7c341Smlelstv disk = rbuf; 4659af1692eSlukem mountpoint = mntinfo->f_mntonname; 4669af1692eSlukem msg("Found %s on %s in mount table\n", disk, mountpoint); 4679af1692eSlukem } 4689af1692eSlukem if (mountpoint != NULL) { 4699af1692eSlukem if (dirc != 0) 47094ccb14aSitojun (void)snprintf(spcl.c_filesys, sizeof(spcl.c_filesys), 4719af1692eSlukem "a subset of %s", mountpoint); 4724c54f5b7Slukem else 47394ccb14aSitojun (void)strlcpy(spcl.c_filesys, mountpoint, 47494ccb14aSitojun sizeof(spcl.c_filesys)); 475b1a3e114Slukem } else if (Fflag) { 47694ccb14aSitojun (void)strlcpy(spcl.c_filesys, "a file system image", 47794ccb14aSitojun sizeof(spcl.c_filesys)); 478108d8947Scgd } else { 47994ccb14aSitojun (void)strlcpy(spcl.c_filesys, "an unlisted file system", 48094ccb14aSitojun sizeof(spcl.c_filesys)); 481108d8947Scgd } 48294ccb14aSitojun (void)strlcpy(spcl.c_dev, disk, sizeof(spcl.c_dev)); 48394ccb14aSitojun (void)strlcpy(spcl.c_label, labelstr, sizeof(spcl.c_label)); 48494ccb14aSitojun (void)gethostname(spcl.c_host, sizeof(spcl.c_host)); 4852beab49aSmrg spcl.c_host[sizeof(spcl.c_host) - 1] = '\0'; 486108d8947Scgd 4871f51c280Shannken if ((snap_backup != NULL || snap_internal) && mntinfo == NULL) { 4881f51c280Shannken msg("WARNING: Cannot use -x or -X on unmounted file system.\n"); 4891f51c280Shannken snap_backup = NULL; 4901f51c280Shannken snap_internal = 0; 4911f51c280Shannken } 4921c57171fSperseant 4931c57171fSperseant #ifdef DUMP_LFS 4941c57171fSperseant sync(); 4951c57171fSperseant if (snap_backup != NULL || snap_internal) { 4961c57171fSperseant if (lfs_wrap_stop(mountpoint) < 0) { 4971c57171fSperseant msg("Cannot stop writing on %s\n", mountpoint); 4981c57171fSperseant exit(X_STARTUP); 4991c57171fSperseant } 5001c57171fSperseant } 5011c57171fSperseant if ((diskfd = open(disk, O_RDONLY)) < 0) { 5021c57171fSperseant msg("Cannot open %s\n", disk); 5031c57171fSperseant exit(X_STARTUP); 5041c57171fSperseant } 505fcd5ec48Shannken disk_dev = disk; 5061c57171fSperseant #else /* ! DUMP_LFS */ 5071f51c280Shannken if (snap_backup != NULL || snap_internal) { 508fcd5ec48Shannken diskfd = snap_open(mntinfo->f_mntonname, snap_backup, 509fcd5ec48Shannken &tnow, &disk_dev); 5101f51c280Shannken if (diskfd < 0) { 5111f51c280Shannken msg("Cannot open snapshot of %s\n", 5121f51c280Shannken mntinfo->f_mntonname); 5131f51c280Shannken exit(X_STARTUP); 5141f51c280Shannken } 5151f51c280Shannken spcl.c_date = tnow; 5161f51c280Shannken } else { 517108d8947Scgd if ((diskfd = open(disk, O_RDONLY)) < 0) { 518108d8947Scgd msg("Cannot open %s\n", disk); 5198975510aSlukem exit(X_STARTUP); 520108d8947Scgd } 521fcd5ec48Shannken disk_dev = disk; 5221f51c280Shannken } 523108d8947Scgd sync(); 5241c57171fSperseant #endif /* ! DUMP_LFS */ 5252763cc19Sperseant 5262763cc19Sperseant needswap = fs_read_sblock(sblock_buf); 52734ccbd43Sbouyer 52820ebd2a4Schristos /* true incremental is always a level 10 dump */ 52920ebd2a4Schristos spcl.c_level = trueinc? iswap32(10): iswap32(level - '0'); 53034ccbd43Sbouyer spcl.c_type = iswap32(TS_TAPE); 53134ccbd43Sbouyer spcl.c_date = iswap32(spcl.c_date); 53234ccbd43Sbouyer spcl.c_ddate = iswap32(spcl.c_ddate); 53334ccbd43Sbouyer if (!Tflag) 53434ccbd43Sbouyer getdumptime(); /* /etc/dumpdates snarfed */ 53534ccbd43Sbouyer 53634ccbd43Sbouyer date = iswap32(spcl.c_date); 53734ccbd43Sbouyer msg("Date of this level %c dump: %s", level, 53834ccbd43Sbouyer spcl.c_date == 0 ? "the epoch\n" : ctime(&date)); 53934ccbd43Sbouyer date = iswap32(spcl.c_ddate); 54034ccbd43Sbouyer msg("Date of last level %c dump: %s", lastlevel, 54134ccbd43Sbouyer spcl.c_ddate == 0 ? "the epoch\n" : ctime(&date)); 54234ccbd43Sbouyer msg("Dumping "); 543f4729837Shannken if (snap_backup != NULL || snap_internal) 5441f51c280Shannken msgtail("a snapshot of "); 5459af1692eSlukem if (dirc != 0) 54634ccbd43Sbouyer msgtail("a subset of "); 54734ccbd43Sbouyer msgtail("%s (%s) ", disk, spcl.c_filesys); 54834ccbd43Sbouyer if (host) 54934ccbd43Sbouyer msgtail("to %s on host %s\n", tape, host); 55034ccbd43Sbouyer else 55134ccbd43Sbouyer msgtail("to %s\n", tape); 5522faa2d1fSlukem msg("Label: %s\n", labelstr); 55334ccbd43Sbouyer 5542763cc19Sperseant ufsib = fs_parametrize(); 5552763cc19Sperseant 556108d8947Scgd dev_bshift = ffs(dev_bsize) - 1; 557108d8947Scgd if (dev_bsize != (1 << dev_bshift)) 5581e8e167aSbriggs quit("dev_bsize (%ld) is not a power of 2", dev_bsize); 559108d8947Scgd tp_bshift = ffs(TP_BSIZE) - 1; 560108d8947Scgd if (TP_BSIZE != (1 << tp_bshift)) 561108d8947Scgd quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE); 5622763cc19Sperseant maxino = fs_maxino(); 563ccfa3742Smycroft mapsize = roundup(howmany(maxino, NBBY), TP_BSIZE); 5649af1692eSlukem usedinomap = (char *)xcalloc((unsigned) mapsize, sizeof(char)); 5659af1692eSlukem dumpdirmap = (char *)xcalloc((unsigned) mapsize, sizeof(char)); 5669af1692eSlukem dumpinomap = (char *)xcalloc((unsigned) mapsize, sizeof(char)); 567108d8947Scgd tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); 568108d8947Scgd 56934ccbd43Sbouyer nonodump = iswap32(spcl.c_level) < honorlevel; 570ccfa3742Smycroft 571491d912fSbouyer initcache(readcache, readblksize); 572491d912fSbouyer 5734c54f5b7Slukem (void)signal(SIGINFO, statussig); 5744c54f5b7Slukem 575108d8947Scgd msg("mapping (Pass I) [regular files]\n"); 5769af1692eSlukem anydirskipped = mapfiles(maxino, &tapesize, mountpoint, 5779af1692eSlukem (dirc ? argv : NULL)); 578108d8947Scgd 579108d8947Scgd msg("mapping (Pass II) [directories]\n"); 580108d8947Scgd while (anydirskipped) { 581108d8947Scgd anydirskipped = mapdirs(maxino, &tapesize); 582108d8947Scgd } 583108d8947Scgd 5849dcaec9cSlukem if (pipeout || unlimited) { 585108d8947Scgd tapesize += 10; /* 10 trailer blocks */ 58642614ed3Sfvdl msg("estimated %llu tape blocks.\n", 58742614ed3Sfvdl (unsigned long long)tapesize); 588ccfa3742Smycroft } else { 589ccfa3742Smycroft double fetapes; 590ccfa3742Smycroft 591108d8947Scgd if (blocksperfile) 592ccfa3742Smycroft fetapes = (double) tapesize / blocksperfile; 593108d8947Scgd else if (cartridge) { 594108d8947Scgd /* Estimate number of tapes, assuming streaming stops at 595108d8947Scgd the end of each block written, and not in mid-block. 596108d8947Scgd Assume no erroneous blocks; this can be compensated 597108d8947Scgd for with an artificially low tape size. */ 598108d8947Scgd fetapes = 5998975510aSlukem ( (double) tapesize /* blocks */ 600108d8947Scgd * TP_BSIZE /* bytes/block */ 601108d8947Scgd * (1.0/density) /* 0.1" / byte */ 602108d8947Scgd + 6038975510aSlukem (double) tapesize /* blocks */ 604108d8947Scgd * (1.0/ntrec) /* streaming-stops per block */ 605108d8947Scgd * 15.48 /* 0.1" / streaming-stop */ 606108d8947Scgd ) * (1.0 / tsize ); /* tape / 0.1" */ 607108d8947Scgd } else { 608108d8947Scgd /* Estimate number of tapes, for old fashioned 9-track 609108d8947Scgd tape */ 610108d8947Scgd int tenthsperirg = (density == 625) ? 3 : 7; 611108d8947Scgd fetapes = 612108d8947Scgd ( tapesize /* blocks */ 613108d8947Scgd * TP_BSIZE /* bytes / block */ 614108d8947Scgd * (1.0/density) /* 0.1" / byte */ 615108d8947Scgd + 616108d8947Scgd tapesize /* blocks */ 617108d8947Scgd * (1.0/ntrec) /* IRG's / block */ 618108d8947Scgd * tenthsperirg /* 0.1" / IRG */ 619108d8947Scgd ) * (1.0 / tsize ); /* tape / 0.1" */ 620108d8947Scgd } 621108d8947Scgd etapes = fetapes; /* truncating assignment */ 622108d8947Scgd etapes++; 623108d8947Scgd /* count the dumped inodes map on each additional tape */ 624108d8947Scgd tapesize += (etapes - 1) * 625108d8947Scgd (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); 626108d8947Scgd tapesize += etapes + 10; /* headers + 10 trailer blks */ 62742614ed3Sfvdl msg("estimated %llu tape blocks on %3.2f tape(s).\n", 62842614ed3Sfvdl (unsigned long long)tapesize, fetapes); 629ccfa3742Smycroft } 630865fe390Slukem /* 631865fe390Slukem * If the user only wants an estimate of the number of 632865fe390Slukem * tapes, exit now. 633865fe390Slukem */ 634865fe390Slukem if (just_estimate) 6358975510aSlukem exit(X_FINOK); 636108d8947Scgd 637108d8947Scgd /* 638ccfa3742Smycroft * Allocate tape buffer. 639108d8947Scgd */ 640108d8947Scgd if (!alloctape()) 641c8d11eb8Schristos quit("can't allocate tape buffers - try a smaller" 642c8d11eb8Schristos " blocking factor."); 643108d8947Scgd 644108d8947Scgd startnewtape(1); 645108d8947Scgd (void)time((time_t *)&(tstart_writing)); 646578deb45Slukem xferrate = 0; 647ccfa3742Smycroft dumpmap(usedinomap, TS_CLRI, maxino - 1); 648108d8947Scgd 649108d8947Scgd msg("dumping (Pass III) [directories]\n"); 650ccfa3742Smycroft dirty = 0; /* XXX just to get gcc to shut up */ 651ccfa3742Smycroft for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { 652ccfa3742Smycroft if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ 653108d8947Scgd dirty = *map++; 654108d8947Scgd else 655108d8947Scgd dirty >>= 1; 656108d8947Scgd if ((dirty & 1) == 0) 657108d8947Scgd continue; 658108d8947Scgd /* 659108d8947Scgd * Skip directory inodes deleted and maybe reallocated 660108d8947Scgd */ 661108d8947Scgd dp = getino(ino); 66242614ed3Sfvdl if ((DIP(dp, mode) & IFMT) != IFDIR) 663108d8947Scgd continue; 664108d8947Scgd (void)dumpino(dp, ino); 665108d8947Scgd } 666108d8947Scgd 667108d8947Scgd msg("dumping (Pass IV) [regular files]\n"); 668ccfa3742Smycroft for (map = dumpinomap, ino = 1; ino < maxino; ino++) { 669ccfa3742Smycroft int mode; 670ccfa3742Smycroft 671ccfa3742Smycroft if (((ino - 1) % NBBY) == 0) /* map is offset by 1 */ 672108d8947Scgd dirty = *map++; 673108d8947Scgd else 674108d8947Scgd dirty >>= 1; 675108d8947Scgd if ((dirty & 1) == 0) 676108d8947Scgd continue; 677108d8947Scgd /* 678108d8947Scgd * Skip inodes deleted and reallocated as directories. 679108d8947Scgd */ 680108d8947Scgd dp = getino(ino); 68142614ed3Sfvdl mode = DIP(dp, mode) & IFMT; 682ccfa3742Smycroft if (mode == IFDIR) 683108d8947Scgd continue; 684108d8947Scgd (void)dumpino(dp, ino); 685108d8947Scgd } 686108d8947Scgd 68734ccbd43Sbouyer spcl.c_type = iswap32(TS_END); 688108d8947Scgd for (i = 0; i < ntrec; i++) 689ccfa3742Smycroft writeheader(maxino - 1); 690108d8947Scgd if (pipeout) 6912266c911Sbouyer msg("%lld tape blocks\n",(long long)iswap64(spcl.c_tapea)); 692108d8947Scgd else 6932266c911Sbouyer msg("%lld tape blocks on %d volume%s\n", 6942266c911Sbouyer (long long)iswap64(spcl.c_tapea), iswap32(spcl.c_volume), 69534ccbd43Sbouyer (iswap32(spcl.c_volume) == 1) ? "" : "s"); 696578deb45Slukem tnow = do_stats(); 69734ccbd43Sbouyer date = iswap32(spcl.c_date); 698578deb45Slukem msg("Date of this level %c dump: %s", level, 69934ccbd43Sbouyer spcl.c_date == 0 ? "the epoch\n" : ctime(&date)); 700578deb45Slukem msg("Date this dump completed: %s", ctime(&tnow)); 7011e8e167aSbriggs msg("Average transfer rate: %d KB/s\n", xferrate / tapeno); 702108d8947Scgd putdumptime(); 703b72a1f53Stron trewind(0); 7048975510aSlukem broadcast("DUMP IS DONE!\a\a\n"); 7051c57171fSperseant #ifdef DUMP_LFS 7061c57171fSperseant lfs_wrap_go(); 7071c57171fSperseant #endif /* DUMP_LFS */ 708108d8947Scgd msg("DUMP IS DONE\n"); 709108d8947Scgd Exit(X_FINOK); 710108d8947Scgd /* NOTREACHED */ 711a84bab53Slukem exit(X_FINOK); /* XXX: to satisfy gcc */ 712108d8947Scgd } 713108d8947Scgd 714dd6c22edSmycroft static void 71599feaf5bSlukem usage(void) 716dd6c22edSmycroft { 7179dcaec9cSlukem const char *prog = getprogname(); 718dd6c22edSmycroft 7199dcaec9cSlukem (void)fprintf(stderr, 720fd6f00f4Schristos "usage: %s [-0123456789aceFinStuX] [-B records] [-b blocksize]\n" 7219dcaec9cSlukem " [-d density] [-f file] [-h level] [-k read-blocksize]\n" 722fd6f00f4Schristos " [-L label] [-l timeout] [-r cachesize] [-s feet]\n" 723eeb96f8cSmanu " [-T date] [-U dumpdev] [-x snap-backup] files-to-dump\n" 7249dcaec9cSlukem " %s [-W | -w]\n", prog, prog); 7258975510aSlukem exit(X_STARTUP); 726dd6c22edSmycroft } 727dd6c22edSmycroft 728ccfa3742Smycroft /* 729ccfa3742Smycroft * Pick up a numeric argument. It must be nonnegative and in the given 730ccfa3742Smycroft * range (except that a vmax of 0 means unlimited). 731ccfa3742Smycroft */ 732ccfa3742Smycroft static long 73364747563Schristos numarg(const char *meaning, long vmin, long vmax) 734ccfa3742Smycroft { 735dd6c22edSmycroft char *p; 736ccfa3742Smycroft long val; 737ccfa3742Smycroft 738dd6c22edSmycroft val = strtol(optarg, &p, 10); 739dd6c22edSmycroft if (*p) 7408975510aSlukem errx(X_STARTUP, "illegal %s -- %s", meaning, optarg); 741ccfa3742Smycroft if (val < vmin || (vmax && val > vmax)) 7428975510aSlukem errx(X_STARTUP, "%s must be between %ld and %ld", 7438975510aSlukem meaning, vmin, vmax); 744ccfa3742Smycroft return (val); 745ccfa3742Smycroft } 746ccfa3742Smycroft 747108d8947Scgd void 748713ffa0eSlukem sig(int signo) 749108d8947Scgd { 750713ffa0eSlukem 751108d8947Scgd switch(signo) { 752108d8947Scgd case SIGALRM: 753108d8947Scgd case SIGBUS: 754108d8947Scgd case SIGFPE: 755108d8947Scgd case SIGHUP: 756108d8947Scgd case SIGTERM: 757108d8947Scgd case SIGTRAP: 758108d8947Scgd if (pipeout) 759c8d11eb8Schristos quit("Signal on pipe: cannot recover"); 7605c897ed9Smrg msg("Rewriting attempted as response to signal %s.\n", sys_siglist[signo]); 761108d8947Scgd (void)fflush(stderr); 762108d8947Scgd (void)fflush(stdout); 763108d8947Scgd close_rewind(); 764ccfa3742Smycroft exit(X_REWRITE); 765108d8947Scgd /* NOTREACHED */ 766108d8947Scgd case SIGSEGV: 767108d8947Scgd msg("SIGSEGV: ABORTING!\n"); 768108d8947Scgd (void)signal(SIGSEGV, SIG_DFL); 769108d8947Scgd (void)kill(0, SIGSEGV); 770108d8947Scgd /* NOTREACHED */ 771108d8947Scgd } 772108d8947Scgd } 773108d8947Scgd 774dd6c22edSmycroft /* 775dd6c22edSmycroft * obsolete -- 776dd6c22edSmycroft * Change set of key letters and ordered arguments into something 777dd6c22edSmycroft * getopt(3) will like. 778dd6c22edSmycroft */ 779dd6c22edSmycroft static void 780713ffa0eSlukem obsolete(int *argcp, char **argvp[]) 781108d8947Scgd { 782dd6c22edSmycroft int argc, flags; 783dd6c22edSmycroft char *ap, **argv, *flagsp, **nargv, *p; 784108d8947Scgd 785dd6c22edSmycroft /* Setup. */ 786dd6c22edSmycroft argv = *argvp; 787dd6c22edSmycroft argc = *argcp; 788dd6c22edSmycroft 789dd6c22edSmycroft /* Return if no arguments or first argument has leading dash. */ 790dd6c22edSmycroft ap = argv[1]; 791dd6c22edSmycroft if (argc == 1 || *ap == '-') 792dd6c22edSmycroft return; 793dd6c22edSmycroft 794dd6c22edSmycroft /* Allocate space for new arguments. */ 7959af1692eSlukem *argvp = nargv = xmalloc((argc + 1) * sizeof(char *)); 7969af1692eSlukem p = flagsp = xmalloc(strlen(ap) + 2); 797dd6c22edSmycroft 798dd6c22edSmycroft *nargv++ = *argv; 799dd6c22edSmycroft argv += 2; 800dd6c22edSmycroft 801dd6c22edSmycroft for (flags = 0; *ap; ++ap) { 802dd6c22edSmycroft switch (*ap) { 803dd6c22edSmycroft case 'B': 804dd6c22edSmycroft case 'b': 805dd6c22edSmycroft case 'd': 806dd6c22edSmycroft case 'f': 807dd6c22edSmycroft case 'h': 808dd6c22edSmycroft case 's': 809dd6c22edSmycroft case 'T': 8101f51c280Shannken case 'x': 811dd6c22edSmycroft if (*argv == NULL) { 812dd6c22edSmycroft warnx("option requires an argument -- %c", *ap); 813dd6c22edSmycroft usage(); 814108d8947Scgd } 8159af1692eSlukem nargv[0] = xmalloc(strlen(*argv) + 2 + 1); 816dd6c22edSmycroft nargv[0][0] = '-'; 817dd6c22edSmycroft nargv[0][1] = *ap; 818865fe390Slukem (void)strcpy(&nargv[0][2], *argv); /* XXX safe strcpy */ 819dd6c22edSmycroft ++argv; 820dd6c22edSmycroft ++nargv; 821dd6c22edSmycroft break; 822dd6c22edSmycroft default: 823dd6c22edSmycroft if (!flags) { 824dd6c22edSmycroft *p++ = '-'; 825dd6c22edSmycroft flags = 1; 826dd6c22edSmycroft } 827dd6c22edSmycroft *p++ = *ap; 828dd6c22edSmycroft break; 829dd6c22edSmycroft } 830dd6c22edSmycroft } 831dd6c22edSmycroft 832dd6c22edSmycroft /* Terminate flags. */ 833dd6c22edSmycroft if (flags) { 834dd6c22edSmycroft *p = '\0'; 835dd6c22edSmycroft *nargv++ = flagsp; 8366edcc275Schristos } else 8376edcc275Schristos free(flagsp); 838dd6c22edSmycroft 839dd6c22edSmycroft /* Copy remaining arguments. */ 8404c54f5b7Slukem while ((*nargv++ = *argv++) != NULL) 8414c54f5b7Slukem ; 842dd6c22edSmycroft 843dd6c22edSmycroft /* Update argument count. */ 844dd6c22edSmycroft *argcp = nargv - *argvp - 1; 845dd6c22edSmycroft } 8469af1692eSlukem 8479af1692eSlukem 8489af1692eSlukem void * 8499af1692eSlukem xcalloc(size_t number, size_t size) 8509af1692eSlukem { 8519af1692eSlukem void *p; 8529af1692eSlukem 8539af1692eSlukem p = calloc(number, size); 8549af1692eSlukem if (p == NULL) 855c8d11eb8Schristos quite(errno, "Can't allocate %zu bytes", size * number); 8569af1692eSlukem return (p); 8579af1692eSlukem } 8589af1692eSlukem 8599af1692eSlukem void * 8609af1692eSlukem xmalloc(size_t size) 8619af1692eSlukem { 8629af1692eSlukem void *p; 8639af1692eSlukem 8649af1692eSlukem p = malloc(size); 8659af1692eSlukem if (p == NULL) 866c8d11eb8Schristos quite(errno, "Can't allocate %zu bytes", size); 8679af1692eSlukem return (p); 8689af1692eSlukem } 8699af1692eSlukem 8709af1692eSlukem char * 8719af1692eSlukem xstrdup(const char *str) 8729af1692eSlukem { 8739af1692eSlukem char *p; 8749af1692eSlukem 8759af1692eSlukem p = strdup(str); 8769af1692eSlukem if (p == NULL) 877c8d11eb8Schristos quite(errno, "Can't copy %s", str); 8789af1692eSlukem return (p); 8799af1692eSlukem } 880