X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/5ff67f982f8b46da9fe5504bfef05a4254b89ab9..638231e6203d8d8447c87fcb959bb90da83f521f:/usr/src/sbin/savecore/savecore.c diff --git a/usr/src/sbin/savecore/savecore.c b/usr/src/sbin/savecore/savecore.c index 99151c49e4..31ff825bbf 100644 --- a/usr/src/sbin/savecore/savecore.c +++ b/usr/src/sbin/savecore/savecore.c @@ -1,29 +1,56 @@ /* - * Copyright (c) 1980 Regents of the University of California. - * All rights reserved. The Berkeley software License Agreement - * specifies the terms and conditions for redistribution. + * Copyright (c) 1980, 1986, 1989 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ #ifndef lint char copyright[] = -"@(#) Copyright (c) 1980 Regents of the University of California.\n\ +"@(#) Copyright (c) 1980, 1986, 1989 The Regents of the University of California.\n\ All rights reserved.\n"; -#endif not lint +#endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)savecore.c 5.1 (Berkeley) %G%"; -#endif not lint +static char sccsid[] = "@(#)savecore.c 5.26 (Berkeley) 4/8/91"; +#endif /* not lint */ -/* - * savecore - */ - -#include -#include #include -#include +#include #include #include +#include +#include +#include +#include +#include +#include #define DAY (60L*60L*24L) #define LEEWAY (3*DAY) @@ -32,10 +59,16 @@ static char sccsid[] = "@(#)savecore.c 5.1 (Berkeley) %G%"; #ifdef vax #define ok(number) ((number)&0x7fffffff) #else +#ifdef tahoe +#define ok(number) ((number)&~0xc0000000) +#else +#ifdef i386 +#define ok(number) ((number)&~0xfe000000) +#else #define ok(number) (number) #endif - -#define SHUTDOWNLOG "/usr/adm/shutdownlog" +#endif +#endif struct nlist current_nl[] = { /* namelist for currently running system */ #define X_DUMPDEV 0 @@ -69,6 +102,7 @@ struct nlist dump_nl[] = { /* name list for dumped system */ char *system; char *dirname; /* directory to save dumps in */ char *ddname; /* name of dump device */ +int dumpfd; /* read/write descriptor on block dev */ char *find_dev(); dev_t dumpdev; /* dump device */ time_t dumptime; /* time the dump was taken */ @@ -77,7 +111,7 @@ int dumpsize; /* amount of memory dumped */ int dumpmag; /* magic number in dump */ time_t now; /* current date */ char *path(); -unsigned malloc(); +char *malloc(); char *ctime(); char vers[80]; char core_vers[80]; @@ -85,70 +119,95 @@ char panic_mesg[80]; int panicstr; off_t lseek(); off_t Lseek(); -int Verbose; +int verbose; +int force; +int clear; +extern int errno; main(argc, argv) char **argv; int argc; { - - while ((argc > 1) && (argv[1][0] == '-')) { - switch (argv[1][1]) { + extern char *optarg; + extern int optind; + int ch; + char *cp; + + while ((ch = getopt(argc, argv, "cdfv")) != EOF) + switch(ch) { + case 'c': + clear = 1; + break; + case 'd': /* not documented */ case 'v': - case 'd': - Verbose = 1; + verbose = 1; + break; + case 'f': + force = 1; break; + case '?': default: - fprintf(stderr, "savecore: illegal flag -%c\n", - argv[1][1]); - fprintf(stderr, - "usage: savecore [-v] dirname [ system ]\n"); - exit(1); + usage(); } - argc--; - argv++; + argc -= optind; + argv += optind; + + /* This is wrong, but I want "savecore -c" to work. */ + if (!clear) { + if (argc != 1 && argc != 2) + usage(); + dirname = argv[0]; } + if (argc == 2) + system = argv[1]; - if (argc != 2 && argc != 3) { - fprintf(stderr, "usage: savecore [-v] dirname [ system ]\n"); - exit(1); + openlog("savecore", LOG_ODELAY, LOG_AUTH); + + read_kmem(); + if (!dump_exists()) { +/* (void)fprintf(stderr, "savecore: no core dump\n");*/ + if (!force) + exit(0); + } + if (clear) { + clear_dump(); + exit(0); } - dirname = argv[1]; - if (argc == 3) - system = argv[2]; - if (access(dirname, 2) < 0) { - perror(dirname); + (void) time(&now); + check_kmem(); + if (panicstr) + log(LOG_CRIT, "reboot after panic: %s\n", panic_mesg); + else + syslog(LOG_CRIT, "reboot\n"); + + if (access(dirname, W_OK) < 0) { + Perror(LOG_ERR, "%s: %m\n", dirname); exit(1); } - read_kmem(); + if ((!get_crashtime() || !check_space()) && !force) + exit(1); + save_core(); + clear_dump(); + exit(0); } -int dump_exists() { - register int dumpfd; int word; - dumpfd = Open(ddname, 0); - Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), 0); - Read(dumpfd, (char *)&word, sizeof word); - close(dumpfd); - if (Verbose && (word != dumpmag)) { - printf("dumplo = %d (%d bytes)\n", dumplo/512, dumplo); + Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET); + Read(dumpfd, (char *)&word, sizeof (word)); + if (verbose && word != dumpmag) printf("magic number mismatch: %x != %x\n", word, dumpmag); - } return (word == dumpmag); } clear_dump() { - register int dumpfd; int zero = 0; - dumpfd = Open(ddname, 1); - Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), 0); - Write(dumpfd, (char *)&zero, sizeof zero); - close(dumpfd); + Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET); + Write(dumpfd, (char *)&zero, sizeof (zero)); } char * @@ -156,10 +215,15 @@ find_dev(dev, type) register dev_t dev; register int type; { + register DIR *dfd = opendir(_PATH_DEV); + struct dirent *dir; struct stat statb; + static char devname[MAXPATHLEN + 1]; char *dp; - strcpy(devname, "/dev/"); + strcpy(devname, _PATH_DEV); + while ((dir = readdir(dfd))) { + strcpy(devname + sizeof(_PATH_DEV) - 1, dir->d_name); if (stat(devname, &statb)) { perror(devname); continue; @@ -167,29 +231,47 @@ find_dev(dev, type) if ((statb.st_mode&S_IFMT) != type) continue; if (dev == statb.st_rdev) { - dp = (char *)malloc(strlen(devname)+1); + closedir(dfd); + dp = malloc(strlen(devname)+1); strcpy(dp, devname); - return dp; + return (dp); } } - fprintf(stderr, "savecore: Can't find device %d,%d\n", - major(dev), minor(dev)); + closedir(dfd); + log(LOG_ERR, "Can't find device %d/%d\n", major(dev), minor(dev)); exit(1); /*NOTREACHED*/ } +char * +rawname(s) + char *s; +{ + static char name[MAXPATHLEN]; + char *sl, *rindex(); + + if ((sl = rindex(s, '/')) == NULL || sl[1] == '0') { + log(LOG_ERR, "can't make raw dump device name from %s?\n", s); + return (s); + } + sprintf(name, "%.*s/r%s", sl - s, s, sl + 1); + return (name); +} + +int cursyms[] = + { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 }; +int dumpsyms[] = + { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 }; read_kmem() { - int kmem; - FILE *fp; register char *cp; + FILE *fp; char *dump_sys; + int kmem, i; - dump_sys = system ? system : "/vmunix"; - - nlist("/vmunix", current_nl); + dump_sys = system ? system : _PATH_UNIX; + nlist(_PATH_UNIX, current_nl); nlist(dump_sys, dump_nl); - /* * Some names we need for the currently running system, * others for the system that was running when the dump was made. @@ -198,67 +280,40 @@ read_kmem() * in the dump_sys namelist, but are presumed to be the same * (since the disk partitions are probably the same!) */ - if (current_nl[X_DUMPDEV].n_value == 0) { - fprintf(stderr, "savecore: /vmunix: dumpdev not in namelist\n"); - exit(1); - } - if (current_nl[X_DUMPLO].n_value == 0) { - fprintf(stderr, "savecore: /vmunix: dumplo not in namelist\n"); - exit(1); - } - if (dump_nl[X_TIME].n_value == 0) { - fprintf(stderr, "savecore: %s: time not in namelist\n", - dump_sys); - exit(1); - } - if (dump_nl[X_DUMPSIZE].n_value == 0) { - fprintf(stderr, "savecore: %s: dumpsize not in namelist\n", - dump_sys); - exit(1); - } - /* we need VERSION in both images */ - if (current_nl[X_VERSION].n_value == 0) { - fprintf(stderr, "savecore: /vmunix: version not in namelist\n", - dump_sys); - exit(1); - } - if (dump_nl[X_VERSION].n_value == 0) { - fprintf(stderr, "savecore: %s: version not in namelist\n", - dump_sys); - exit(1); - } - if (dump_nl[X_PANICSTR].n_value == 0) { - fprintf(stderr, "savecore: %s: panicstr not in namelist\n", - dump_sys); - exit(1); - } - /* we need DUMPMAG in both images */ - if (current_nl[X_DUMPMAG].n_value == 0) { - fprintf(stderr, "savecore: /vmunix: dumpmag not in namelist\n"); - exit(1); - } - if (dump_nl[X_DUMPMAG].n_value == 0) { - fprintf(stderr, "savecore: %s: dumpmag not in namelist\n", - dump_sys); - exit(1); - } - kmem = Open("/dev/kmem", 0); - Lseek(kmem, (long)current_nl[X_DUMPDEV].n_value, 0); + for (i = 0; cursyms[i] != -1; i++) + if (current_nl[cursyms[i]].n_value == 0) { + log(LOG_ERR, "%s: %s not in namelist\n", _PATH_UNIX, + current_nl[cursyms[i]].n_name); + exit(1); + } + for (i = 0; dumpsyms[i] != -1; i++) + if (dump_nl[dumpsyms[i]].n_value == 0) { + log(LOG_ERR, "%s: %s not in namelist\n", dump_sys, + dump_nl[dumpsyms[i]].n_name); + exit(1); + } + kmem = Open(_PATH_KMEM, O_RDONLY); + Lseek(kmem, (long)current_nl[X_DUMPDEV].n_value, L_SET); Read(kmem, (char *)&dumpdev, sizeof (dumpdev)); - Lseek(kmem, (long)current_nl[X_DUMPLO].n_value, 0); + Lseek(kmem, (long)current_nl[X_DUMPLO].n_value, L_SET); Read(kmem, (char *)&dumplo, sizeof (dumplo)); - Lseek(kmem, (long)current_nl[X_DUMPMAG].n_value, 0); + if (verbose) + printf("dumplo = %d (%d * %d)\n", dumplo, dumplo/DEV_BSIZE, + DEV_BSIZE); + Lseek(kmem, (long)current_nl[X_DUMPMAG].n_value, L_SET); Read(kmem, (char *)&dumpmag, sizeof (dumpmag)); - dumplo *= 512L; + dumplo *= DEV_BSIZE; ddname = find_dev(dumpdev, S_IFBLK); - if ((fp = fdopen(kmem, "r")) == NULL) { - fprintf(stderr, "savecore: Couldn't fdopen kmem\n"); + dumpfd = Open(ddname, O_RDWR); + fp = fdopen(kmem, "r"); + if (fp == NULL) { + log(LOG_ERR, "Couldn't fdopen kmem\n"); exit(1); } if (system) return; - fseek(fp, (long)current_nl[X_VERSION].n_value, 0); - fgets(vers, sizeof vers, fp); + fseek(fp, (long)current_nl[X_VERSION].n_value, L_SET); + fgets(vers, sizeof (vers), fp); fclose(fp); } @@ -267,46 +322,46 @@ check_kmem() FILE *fp; register char *cp; - if ((fp = fopen(ddname, "r")) == NULL) { - perror(ddname); + fp = fdopen(dumpfd, "r"); + if (fp == NULL) { + log(LOG_ERR, "Can't fdopen dumpfd\n"); exit(1); } - fseek(fp, (off_t)(dumplo+ok(dump_nl[X_VERSION].n_value)), 0); - fgets(core_vers, sizeof core_vers, fp); - fclose(fp); - if (!eq(vers, core_vers) && (system == 0)) - fprintf(stderr, - "savecore: Warning: vmunix version mismatch:\n\t%sand\n\t%s", - vers, core_vers); - fp = fopen(ddname, "r"); - fseek(fp, (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), 0); - fread((char *)&panicstr, sizeof panicstr, 1, fp); + + fseek(fp, (off_t)(dumplo+ok(dump_nl[X_VERSION].n_value)), L_SET); + fgets(core_vers, sizeof (core_vers), fp); + if (!eq(vers, core_vers) && system == 0) { + log(LOG_WARNING, "Warning: %s version mismatch:\n", _PATH_UNIX); + log(LOG_WARNING, "\t%s\n", vers); + log(LOG_WARNING, "and\t%s\n", core_vers); + } + + fseek(fp, (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), L_SET); + fread((char *)&panicstr, sizeof (panicstr), 1, fp); if (panicstr) { - fseek(fp, dumplo + ok(panicstr), 0); + fseek(fp, dumplo + ok(panicstr), L_SET); cp = panic_mesg; do *cp = getc(fp); - while (*cp++); + while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)]); } - fclose(fp); + /* don't fclose(fp); we want the file descriptor */ } get_crashtime() { - int dumpfd; time_t clobber = (time_t)0; - Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_TIME].n_value)), 0); + Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_TIME].n_value)), L_SET); Read(dumpfd, (char *)&dumptime, sizeof dumptime); - close(dumpfd); if (dumptime == 0) { - if (Verbose) - printf("dump time not found\n"); + if (verbose) + printf("Dump time is zero.\n"); return (0); } printf("System went down at %s", ctime(&dumptime)); if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) { - printf("Dump time is unreasonable\n"); + printf("dump time is unreasonable\n"); return (0); } return (1); @@ -316,7 +371,7 @@ char * path(file) char *file; { - register char *cp = (char *)malloc(strlen(file) + strlen(dirname) + 2); + register char *cp = malloc(strlen(file) + strlen(dirname) + 2); (void) strcpy(cp, dirname); (void) strcat(cp, "/"); @@ -326,30 +381,22 @@ path(file) check_space() { - struct stat dsb; - register char *ddev; - int dfd, spacefree; - struct fs fs; + long minfree, spacefree; + struct statfs fsbuf; - if (stat(dirname, &dsb) < 0) { - perror(dirname); + if (statfs(dirname, &fsbuf) < 0) { + Perror(LOG_ERR, "%s: %m\n", dirname); exit(1); } - ddev = find_dev(dsb.st_dev, S_IFBLK); - dfd = Open(ddev, 0); - Lseek(dfd, (long)(SBLOCK * DEV_BSIZE), 0); - Read(dfd, (char *)&fs, sizeof fs); - close(dfd); - spacefree = fs.fs_cstotal.cs_nbfree * fs.fs_bsize / 1024; - if (read_number("minfree") > spacefree) { - fprintf(stderr, - "savecore: Dump omitted, not enough space on device\n"); + spacefree = fsbuf.f_bavail * fsbuf.f_fsize / 1024; + minfree = read_number("minfree"); + if (minfree > 0 && spacefree - dumpsize < minfree) { + log(LOG_WARNING, "Dump omitted, not enough space on device\n"); return (0); } - if (fs.fs_cstotal.cs_nbfree * fs.fs_frag + fs.fs_cstotal.cs_nffree < - fs.fs_dsize * fs.fs_minfree / 100) - fprintf(stderr, - "Dump performed, but free space threshold crossed\n"); + if (spacefree - dumpsize < minfree) + log(LOG_WARNING, + "Dump performed, but free space threshold crossed\n"); return (1); } @@ -359,7 +406,8 @@ read_number(fn) char lin[80]; register FILE *fp; - if ((fp = fopen(path(fn), "r")) == NULL) + fp = fopen(path(fn), "r"); + if (fp == NULL) return (0); if (fgets(lin, 80, fp) == NULL) { fclose(fp); @@ -369,84 +417,95 @@ read_number(fn) return (atoi(lin)); } +/*#define BUFSIZE (256*1024) /* 1/4 Mb */ +#define BUFSIZE (8*1024) + save_core() { register int n; - char buffer[32*NBPG]; - register char *cp = buffer; + register char *cp; register int ifd, ofd, bounds; + int ret; + char *bfile; register FILE *fp; + cp = malloc(BUFSIZE); + if (cp == 0) { + log(LOG_ERR, "savecore: Can't allocate i/o buffer.\n"); + return; + } bounds = read_number("bounds"); - ifd = Open(system?system:"/vmunix", 0); - while((n = Read(ifd, cp, BUFSIZ)) > 0) + ifd = Open(system ? system : _PATH_UNIX, O_RDONLY); + (void)sprintf(cp, "system.%d", bounds); + ofd = Create(path(cp), 0644); + while((n = Read(ifd, cp, BUFSIZE)) > 0) Write(ofd, cp, n); close(ifd); close(ofd); - ifd = Open(ddname, 0); - Lseek(ifd, (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), 0); - Read(ifd, (char *)&dumpsize, sizeof (dumpsize)); - sprintf(cp, "vmcore.%d", bounds); + if ((ifd = open(rawname(ddname), O_RDONLY)) == -1) { + log(LOG_WARNING, "Can't open %s (%m); using block device", + rawname(ddname)); + ifd = dumpfd; + } + Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), L_SET); + Read(dumpfd, (char *)&dumpsize, sizeof (dumpsize)); + (void)sprintf(cp, "ram.%d", bounds); ofd = Create(path(cp), 0644); - Lseek(ifd, (off_t)dumplo, 0); - printf("Saving %d bytes of image in vmcore.%d\n", NBPG*dumpsize, - bounds); + Lseek(ifd, (off_t)dumplo, L_SET); + dumpsize *= NBPG; + log(LOG_NOTICE, "Saving %d bytes of image in ram.%d\n", + dumpsize, bounds); while (dumpsize > 0) { - n = Read(ifd, cp, (dumpsize > 32 ? 32 : dumpsize) * NBPG); - Write(ofd, cp, n); - dumpsize -= n/NBPG; + n = read(ifd, cp, + dumpsize > BUFSIZE ? BUFSIZE : dumpsize); + if (n <= 0) { + if (n == 0) + log(LOG_WARNING, + "WARNING: EOF on dump device; %s\n", + "ram file may be incomplete"); + else + Perror(LOG_ERR, "read from dumpdev: %m", + "read"); + break; + } + if ((ret = write(ofd, cp, n)) < n) { + if (ret < 0) + Perror(LOG_ERR, "write: %m", "write"); + else + log(LOG_ERR, "short write: wrote %d of %d\n", + ret, n); + log(LOG_WARNING, "WARNING: ram file may be incomplete\n"); + break; + } + dumpsize -= n; } close(ifd); close(ofd); - fp = fopen(path("bounds"), "w"); - fprintf(fp, "%d\n", bounds+1); - fclose(fp); -} - -char *days[] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" -}; - -char *months[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", - "Oct", "Nov", "Dec" -}; - -log_entry() -{ - FILE *fp; - struct tm *tm, *localtime(); - - tm = localtime(&now); - fp = fopen("/usr/adm/shutdownlog", "a"); - if (fp == 0) - return; - fseek(fp, 0L, 2); - fprintf(fp, "%02d:%02d %s %s %2d, %4d. Reboot", tm->tm_hour, - tm->tm_min, days[tm->tm_wday], months[tm->tm_mon], - tm->tm_mday, tm->tm_year + 1900); - if (panicstr) - fprintf(fp, " after panic: %s\n", panic_mesg); - else - putc('\n', fp); - fclose(fp); + bfile = path("bounds"); + fp = fopen(bfile, "w"); + if (fp) { + fprintf(fp, "%d\n", bounds+1); + fclose(fp); + } else + Perror(LOG_ERR, "Can't create bounds file %s: %m", bfile); + free(cp); } /* * Versions of std routines that exit on error. */ - Open(name, rw) char *name; int rw; { int fd; - if ((fd = open(name, rw)) < 0) { - perror(name); + fd = open(name, rw); + if (fd < 0) { + Perror(LOG_ERR, "%s: %m", name); exit(1); } - return fd; + return (fd); } Read(fd, buff, size) @@ -455,11 +514,12 @@ Read(fd, buff, size) { int ret; - if ((ret = read(fd, buff, size)) < 0) { - perror("read"); + ret = read(fd, buff, size); + if (ret < 0) { + Perror(LOG_ERR, "read: %m", "read"); exit(1); } - return ret; + return (ret); } off_t @@ -469,11 +529,12 @@ Lseek(fd, off, flag) { long ret; - if ((ret = lseek(fd, off, flag)) == -1L) { - perror("lseek"); + ret = lseek(fd, off, flag); + if (ret == -1) { + Perror(LOG_ERR, "lseek: %m", "lseek"); exit(1); } - return ret; + return (ret); } Create(file, mode) @@ -482,20 +543,52 @@ Create(file, mode) { register int fd; - if ((fd = creat(file, mode)) < 0) { - perror(file); + fd = creat(file, mode); + if (fd < 0) { + Perror(LOG_ERR, "%s: %m", file); exit(1); } - return fd; + return (fd); } Write(fd, buf, size) int fd, size; char *buf; { + int n; - if (write(fd, buf, size) < size) { - perror("write"); + if ((n = write(fd, buf, size)) < size) { + if (n < 0) + Perror(LOG_ERR, "write: %m", "write"); + else + log(LOG_ERR, "short write: wrote %d of %d\n", n, size); exit(1); } } + +/* VARARGS2 */ +log(level, msg, a1, a2) + int level; + char *msg; +{ + + fprintf(stderr, msg, a1, a2); + syslog(level, msg, a1, a2); +} + +Perror(level, msg, s) + int level; + char *msg, *s; +{ + int oerrno = errno; + + perror(s); + errno = oerrno; + syslog(level, msg, s); +} + +usage() +{ + (void)fprintf(stderr, "usage: savecore [-cfv] dirname [system]\n"); + exit(1); +}