needed (dumpsize+vmunixsize) needs to be in 1k chunks not bytes
[unix-history] / usr / src / sbin / savecore / savecore.c
index 71292a3..2238bec 100644 (file)
@@ -1,58 +1,40 @@
-/*
- * Copyright (c) 1980, 1986 The Regents of the University of California.
- * All rights reserved.
+/*-
+ * Copyright (c) 1986, 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the University of California, Berkeley.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * %sccs.include.redist.c%
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-char copyright[] =
-"@(#) Copyright (c) 1980, 1986 The Regents of the University of California.\n\
- All rights reserved.\n";
+static char copyright[] =
+"@(#) Copyright (c) 1986, 1992, 1993\n\
      The Regents of the University of California.  All rights reserved.\n";
 #endif /* not lint */
 
 #ifndef lint
 #endif /* not lint */
 
 #ifndef lint
-static char sccsid[] = "@(#)savecore.c 5.14 (Berkeley) %G%";
+static char sccsid[] = "@(#)savecore.c 8.2 (Berkeley) %G%";
 #endif /* not lint */
 
 #endif /* not lint */
 
-/*
- * savecore
- */
-
-#include <stdio.h>
-#include <nlist.h>
 #include <sys/param.h>
 #include <sys/param.h>
-#include <sys/dir.h>
 #include <sys/stat.h>
 #include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/file.h>
+#include <sys/mount.h>
 #include <sys/syslog.h>
 #include <sys/syslog.h>
+#include <sys/time.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <nlist.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tzfile.h>
+#include <unistd.h>
+
+#define ok(number) ((number) - KERNBASE)
 
 
-#define        DAY     (60L*60L*24L)
-#define        LEEWAY  (3*DAY)
-
-#define eq(a,b) (!strcmp(a,b))
-#ifdef vax
-#define ok(number) ((number)&0x7fffffff)
-#else
-#ifdef tahoe
-#define ok(number) ((number)&~0xc0000000)
-#else
-#define ok(number) (number)
-#endif
-#endif
-
-struct nlist current_nl[] = {  /* namelist for currently running system */
+struct nlist current_nl[] = {  /* Namelist for currently running system. */
 #define X_DUMPDEV      0
        { "_dumpdev" },
 #define X_DUMPLO       1
 #define X_DUMPDEV      0
        { "_dumpdev" },
 #define X_DUMPLO       1
@@ -69,10 +51,12 @@ struct nlist current_nl[] = {       /* namelist for currently running system */
        { "_dumpmag" },
        { "" },
 };
        { "_dumpmag" },
        { "" },
 };
+int cursyms[] = { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 };
+int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 };
 
 
-struct nlist dump_nl[] = {     /* name list for dumped system */
-       { "_dumpdev" },         /* entries MUST be the same as */
-       { "_dumplo" },          /*      those in current_nl[]  */
+struct nlist dump_nl[] = {     /* Name list for dumped system. */
+       { "_dumpdev" },         /* Entries MUST be the same as */
+       { "_dumplo" },          /*      those in current_nl[].  */
        { "_time" },
        { "_dumpsize" },
        { "_version" },
        { "_time" },
        { "_dumpsize" },
        { "_version" },
@@ -81,382 +65,503 @@ struct nlist dump_nl[] = {        /* name list for dumped system */
        { "" },
 };
 
        { "" },
 };
 
-char   *system;
+/* Types match kernel declarations. */
+long   dumplo;                         /* where dump starts on dumpdev */
+int    dumpmag;                        /* magic number in dump */
+int    dumpsize;                       /* amount of memory dumped */
+
+char   *vmunix;
 char   *dirname;                       /* directory to save dumps in */
 char   *ddname;                        /* name of dump device */
 char   *dirname;                       /* directory to save dumps in */
 char   *ddname;                        /* name of dump device */
-char   *find_dev();
 dev_t  dumpdev;                        /* dump device */
 dev_t  dumpdev;                        /* dump device */
-time_t dumptime;                       /* time the dump was taken */
-int    dumplo;                         /* where dump starts on dumpdev */
-int    dumpsize;                       /* amount of memory dumped */
-int    dumpmag;                        /* magic number in dump */
+int    dumpfd;                         /* read/write descriptor on block dev */
 time_t now;                            /* current date */
 time_t now;                            /* current date */
-char   *path();
-char   *malloc();
-char   *ctime();
-char   vers[80];
-char   core_vers[80];
-char   panic_mesg[80];
+char   panic_mesg[1024];
 int    panicstr;
 int    panicstr;
-off_t  lseek();
-off_t  Lseek();
-int    Verbose;
-int    force;
-int    clear;
-extern int errno;
-
+char   vers[1024];
+
+int    clear, compress, force, verbose;        /* flags */
+
+void    check_kmem __P((void));
+int     check_space __P((void));
+void    clear_dump __P((void));
+int     Create __P((char *, int));
+int     dump_exists __P((void));
+char   *find_dev __P((dev_t, int));
+int     get_crashtime __P((void));
+void    kmem_setup __P((void));
+void    log __P((int, char *, ...));
+void    Lseek __P((int, off_t, int));
+int     Open __P((char *, int rw));
+int     Read __P((int, void *, int));
+char   *rawname __P((char *s));
+void    save_core __P((void));
+void    usage __P((void));
+void    Write __P((int, void *, int));
+
+int
 main(argc, argv)
 main(argc, argv)
-       char **argv;
        int argc;
        int argc;
+       char *argv[];
 {
 {
-       char *cp;
+       int ch;
 
 
-       argc--, argv++;
-       while (argc > 0 && argv[0][0] == '-') {
-               for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
+       openlog("savecore", LOG_PERROR, LOG_DAEMON);
 
 
-               case 'f':
-                       force++;
+       while ((ch = getopt(argc, argv, "cdfNvz")) != EOF)
+               switch(ch) {
+               case 'c':
+                       clear = 1;
                        break;
                        break;
-
+               case 'd':               /* Not documented. */
                case 'v':
                case 'v':
-                       Verbose++;
+                       verbose = 1;
                        break;
                        break;
-
-               case 'c':
-                       clear++;
+               case 'f':
+                       force = 1;
                        break;
                        break;
-
+               case 'N':
+                       vmunix = optarg;
+                       break;
+               case 'z':
+                       compress = 1;
+                       break;
+               case '?':
                default:
                default:
-               usage:
-                       fprintf(stderr,
-                           "usage: savecore [-f] [-v] dirname [ system ]\n");
-                       exit(1);
+                       usage();
                }
                }
-               argc--, argv++;
-       }
-       if (argc != 1 && argc != 2)
-               goto usage;
-       dirname = argv[0];
-       if (argc == 2)
-               system = argv[1];
-       openlog("savecore", LOG_ODELAY, LOG_AUTH);
-       if (access(dirname, W_OK) < 0) {
-               Perror(LOG_ERR, "%s: %m", dirname);
-               exit(1);
-       }
-       read_kmem();
-}
+       argc -= optind;
+       argv += optind;
 
 
-dump_exists()
-{
-       register int dumpfd;
-       int word;
-
-       dumpfd = Open(ddname, O_RDONLY);
-       Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
-       Read(dumpfd, (char *)&word, sizeof (word));
-       close(dumpfd);
-       if (Verbose && word != dumpmag) {
-               printf("dumplo = %d (%d bytes)\n", dumplo/DEV_BSIZE, dumplo);
-               printf("magic number mismatch: %x != %x\n", word, dumpmag);
+       if (!clear) {
+               if (argc != 1 && argc != 2)
+                       usage();
+               dirname = argv[0];
        }
        }
-       return (word == dumpmag);
-}
-
-clear_dump()
-{
-       register int dumpfd;
-       int zero = 0;
-
-       dumpfd = Open(ddname, O_WRONLY);
-       Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
-       Write(dumpfd, (char *)&zero, sizeof (zero));
-       close(dumpfd);
-}
+       if (argc == 2)
+               vmunix = argv[1];
 
 
-char *
-find_dev(dev, type)
-       register dev_t dev;
-       register int type;
-{
-       struct stat statb;
-       char *dp;
+       (void)time(&now);
+       kmem_setup();
 
 
-       strcpy(devname, "/dev/");
-               if (stat(devname, &statb)) {
-                       perror(devname);
-                       continue;
-               }
-               if ((statb.st_mode&S_IFMT) != type)
-                       continue;
-               if (dev == statb.st_rdev) {
-                       dp = malloc(strlen(devname)+1);
-                       strcpy(dp, devname);
-                       return (dp);
-               }
-       }
-       log(LOG_ERR, "Can't find device %d/%d\n", major(dev), minor(dev));
-       exit(1);
-       /*NOTREACHED*/
 }
 
 }
 
-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()
+void
+kmem_setup()
 {
 {
-       register char *cp;
        FILE *fp;
        FILE *fp;
-       char *dump_sys;
        int kmem, i;
        int kmem, i;
+       char *dump_sys;
        
        
-       dump_sys = system ? system : "/vmunix";
-       nlist("/vmunix", 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.
-        * The values obtained from the current system are used
-        * to look for things in /dev/kmem that cannot be found
-        * in the dump_sys namelist, but are presumed to be the same
-        * (since the disk partitions are probably the same!)
+        * Some names we need for the currently running system, others for
+        * the system that was running when the dump was made.  The values
+        * obtained from the current system are used to look for things in
+        * /dev/kmem that cannot be found in the dump_sys namelist, but are
+        * presumed to be the same (since the disk partitions are probably
+        * the same!)
         */
         */
+       if ((nlist(_PATH_UNIX, current_nl)) == -1)
+               syslog(LOG_ERR, "%s: nlist: %s", _PATH_UNIX, strerror(errno));
        for (i = 0; cursyms[i] != -1; i++)
                if (current_nl[cursyms[i]].n_value == 0) {
        for (i = 0; cursyms[i] != -1; i++)
                if (current_nl[cursyms[i]].n_value == 0) {
-                       log(LOG_ERR, "/vmunix: %s not in namelist\n",
-                           current_nl[cursyms[i]].n_name);
+                       syslog(LOG_ERR, "%s: %s not in namelist",
+                           _PATH_UNIX, current_nl[cursyms[i]].n_name);
                        exit(1);
                }
                        exit(1);
                }
+
+       dump_sys = vmunix ? vmunix : _PATH_UNIX;
+       if ((nlist(dump_sys, dump_nl)) == -1)
+               syslog(LOG_ERR, "%s: nlist: %s", dump_sys, strerror(errno));
        for (i = 0; dumpsyms[i] != -1; i++)
                if (dump_nl[dumpsyms[i]].n_value == 0) {
        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);
+                       syslog(LOG_ERR, "%s: %s not in namelist",
+                           dump_sys, dump_nl[dumpsyms[i]].n_name);
                        exit(1);
                }
                        exit(1);
                }
-       kmem = Open("/dev/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, L_SET);
-       Read(kmem, (char *)&dumplo, sizeof (dumplo));
-       Lseek(kmem, (long)current_nl[X_DUMPMAG].n_value, L_SET);
-       Read(kmem, (char *)&dumpmag, sizeof (dumpmag));
+
+       kmem = Open(_PATH_KMEM, O_RDONLY);
+       Lseek(kmem, (off_t)current_nl[X_DUMPDEV].n_value, L_SET);
+       (void)Read(kmem, &dumpdev, sizeof(dumpdev));
+       Lseek(kmem, (off_t)current_nl[X_DUMPLO].n_value, L_SET);
+       (void)Read(kmem, &dumplo, sizeof(dumplo));
+       if (verbose)
+               (void)printf("dumplo = %d (%d * %d)\n",
+                   dumplo, dumplo/DEV_BSIZE, DEV_BSIZE);
+       Lseek(kmem, (off_t)current_nl[X_DUMPMAG].n_value, L_SET);
+       (void)Read(kmem, &dumpmag, sizeof(dumpmag));
        dumplo *= DEV_BSIZE;
        ddname = find_dev(dumpdev, S_IFBLK);
        dumplo *= DEV_BSIZE;
        ddname = find_dev(dumpdev, S_IFBLK);
+       dumpfd = Open(ddname, O_RDWR);
        fp = fdopen(kmem, "r");
        if (fp == NULL) {
        fp = fdopen(kmem, "r");
        if (fp == NULL) {
-               log(LOG_ERR, "Couldn't fdopen kmem\n");
+               syslog(LOG_ERR, "%s: fdopen: %m", _PATH_KMEM);
                exit(1);
        }
                exit(1);
        }
-       if (system)
+       if (vmunix)
                return;
                return;
-       fseek(fp, (long)current_nl[X_VERSION].n_value, L_SET);
-       fgets(vers, sizeof (vers), fp);
-       fclose(fp);
+       (void)fseek(fp, (off_t)current_nl[X_VERSION].n_value, L_SET);
+       (void)fgets(vers, sizeof(vers), fp);
+
+       /* Don't fclose(fp), we use dumpfd later. */
 }
 
 }
 
+void
 check_kmem()
 {
 check_kmem()
 {
-       FILE *fp;
        register char *cp;
        register char *cp;
+       FILE *fp;
+       char core_vers[1024];
 
 
-       fp = fopen(ddname, "r");
+       fp = fdopen(dumpfd, "r");
        if (fp == NULL) {
        if (fp == NULL) {
-               Perror(LOG_ERR, "%s: %m", ddname);
+               syslog(LOG_ERR, "%s: fdopen: %m", ddname);
                exit(1);
        }
                exit(1);
        }
-       fseek(fp, (off_t)(dumplo+ok(dump_nl[X_VERSION].n_value)), L_SET);
-       fgets(core_vers, sizeof (core_vers), fp);
-       fclose(fp);
-       if (!eq(vers, core_vers) && system == 0) {
-               log(LOG_WARNING, "Warning: vmunix version mismatch:\n");
-               log(LOG_WARNING, "\t%s\n", vers);
-               log(LOG_WARNING, "and\t%s\n", core_vers);
-       }
-       fp = fopen(ddname, "r");
-       fseek(fp, (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), L_SET);
-       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 (strcmp(vers, core_vers) && vmunix == 0)
+               syslog(LOG_WARNING,
+                   "warning: %s version mismatch:\n\t%s\nand\t%s\n",
+                   _PATH_UNIX, vers, core_vers);
+       (void)fseek(fp,
+           (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), L_SET);
+       (void)fread(&panicstr, sizeof(panicstr), 1, fp);
        if (panicstr) {
        if (panicstr) {
-               fseek(fp, dumplo + ok(panicstr), L_SET);
+               (void)fseek(fp, dumplo + ok(panicstr), L_SET);
                cp = panic_mesg;
                do
                        *cp = getc(fp);
                cp = panic_mesg;
                do
                        *cp = getc(fp);
-               while (*cp++);
+               while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)]);
        }
        }
-       fclose(fp);
+       /* Don't fclose(fp), we use dumpfd later. */
 }
 
 }
 
-get_crashtime()
+void
+clear_dump()
 {
 {
-       int dumpfd;
-       time_t clobber = (time_t)0;
+       long newdumplo;
 
 
-       dumpfd = Open(ddname, O_RDONLY);
-       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 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");
+       newdumplo = 0;
+       Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
+       Write(dumpfd, &newdumplo, sizeof(newdumplo));
+}
+
+int
+dump_exists()
+{
+       int newdumpmag;
+
+       Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
+       (void)Read(dumpfd, &newdumpmag, sizeof(newdumpmag));
+       if (newdumpmag != dumpmag) {
+               if (verbose)
+                       syslog(LOG_WARNING, "magic number mismatch (%x != %x)",
+                           newdumpmag, dumpmag);
+               syslog(LOG_WARNING, "no core dump");
                return (0);
        }
        return (1);
 }
 
                return (0);
        }
        return (1);
 }
 
-char *
-path(file)
-       char *file;
+char buf[1024 * 1024];
+
+void
+save_core()
 {
 {
-       register char *cp = malloc(strlen(file) + strlen(dirname) + 2);
+       register FILE *fp;
+       register int bounds, ifd, nr, nw, ofd;
+       char *rawp, path[MAXPATHLEN];
+
+       /*
+        * Get the current number and update the bounds file.  Do the update
+        * now, because may fail later and don't want to overwrite anything.
+        */
+       (void)snprintf(path, sizeof(path), "%s/bounds", dirname);
+       if ((fp = fopen(path, "r")) == NULL)
+               goto err1;
+       if (fgets(buf, sizeof(buf), fp) == NULL) {
+               if (ferror(fp))
+err1:                  syslog(LOG_WARNING, "%s: %s", path, strerror(errno));
+               bounds = 0;
+       } else
+               bounds = atoi(buf);
+       if (fp != NULL)
+               (void)fclose(fp);
+       if ((fp = fopen(path, "w")) == NULL)
+               syslog(LOG_ERR, "%s: %m", path);
+       else {
+               (void)fprintf(fp, "%d\n", bounds + 1);
+               (void)fclose(fp);
+       }
+       (void)fclose(fp);
+
+       /* Create the core file. */
+       (void)snprintf(path, sizeof(path), "%s/vmcore.%d%s",
+           dirname, bounds, compress ? ".Z" : "");
+       if (compress) {
+               if ((fp = zopen(path, "w", 0)) == NULL) {
+                       syslog(LOG_ERR, "%s: %s", path, strerror(errno));
+                       exit(1);
+               }
+       } else
+               ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+       /* Open the raw device. */
+       rawp = rawname(ddname);
+       if ((ifd = open(rawp, O_RDONLY)) == -1) {
+               syslog(LOG_WARNING, "%s: %m; using block device", rawp);
+               ifd = dumpfd;
+       }
+
+       /* Read the dump size. */
+       Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), L_SET);
+       (void)Read(dumpfd, &dumpsize, sizeof(dumpsize));
+
+       /* Seek to the start of the core. */
+       Lseek(ifd, (off_t)dumplo, L_SET);
 
 
-       (void) strcpy(cp, dirname);
-       (void) strcat(cp, "/");
-       (void) strcat(cp, file);
-       return (cp);
+       /* Copy the core file. */
+       dumpsize *= NBPG;
+       syslog(LOG_NOTICE, "writing %score to %s",
+           compress ? "compressed " : "", path);
+       for (; dumpsize > 0; dumpsize -= nr) {
+               (void)printf("%6dK\r", dumpsize / 1024);
+               (void)fflush(stdout);
+               nr = read(ifd, buf, MIN(dumpsize, sizeof(buf)));
+               if (nr <= 0) {
+                       if (nr == 0)
+                               syslog(LOG_WARNING,
+                                   "WARNING: EOF on dump device");
+                       else
+                               syslog(LOG_ERR, "%s: %m", rawp);
+                       goto err2;
+               }
+               if (compress)
+                       nw = fwrite(buf, 1, nr, fp);
+               else
+                       nw = write(ofd, buf, nr);
+               if (nw != nr) {
+                       syslog(LOG_ERR, "%s: %s",
+                           path, strerror(nw == 0 ? EIO : errno));
+err2:                  syslog(LOG_WARNING,
+                           "WARNING: vmcore may be incomplete");
+                       (void)printf("\n");
+                       exit(1);
+               }
+       }
+       (void)printf("\n");
+       (void)close(ifd);
+       if (compress)
+               (void)fclose(fp);
+       else 
+               (void)close(ofd);
+
+       /* Copy the kernel. */
+       ifd = Open(vmunix ? vmunix : _PATH_UNIX, O_RDONLY);
+       (void)snprintf(path, sizeof(path), "%s/vmunix.%d%s",
+           dirname, bounds, compress ? ".Z" : "");
+       if (compress) {
+               if ((fp = zopen(path, "w", 0)) == NULL) {
+                       syslog(LOG_ERR, "%s: %s", path, strerror(errno));
+                       exit(1);
+               }
+       } else
+               ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+       syslog(LOG_NOTICE, "writing %skernel to %s",
+           compress ? "compressed " : "", path);
+       while ((nr = read(ifd, buf, sizeof(buf))) > 0) {
+               if (compress)
+                       nw = fwrite(buf, 1, nr, fp);
+               else
+                       nw = write(ofd, buf, nr);
+               if (nw != nr) {
+                       syslog(LOG_ERR, "%s: %s",
+                           path, strerror(nw == 0 ? EIO : errno));
+                       syslog(LOG_WARNING,
+                           "WARNING: vmunix may be incomplete");
+                       exit(1);
+               }
+       }
+       if (nr < 0) {
+               syslog(LOG_ERR, "%s: %s",
+                   vmunix ? vmunix : _PATH_UNIX, strerror(errno));
+               syslog(LOG_WARNING,
+                   "WARNING: vmunix may be incomplete");
+               exit(1);
+       }
+       if (compress)
+               (void)fclose(fp);
+       else
+               (void)close(ofd);
 }
 
 }
 
-check_space()
+char *
+find_dev(dev, type)
+       register dev_t dev;
+       register int type;
 {
 {
-       struct stat dsb;
-       register char *ddev;
-       int dfd, spacefree;
-       struct fs fs;
+       register DIR *dfd;
+       struct dirent *dir;
+       struct stat sb;
+       char *dp, devname[MAXPATHLEN + 1];
 
 
-       if (stat(dirname, &dsb) < 0) {
-               Perror(LOG_ERR, "%s: %m", dirname);
+       if ((dfd = opendir(_PATH_DEV)) == NULL) {
+               syslog(LOG_ERR, "%s: %s", _PATH_DEV, strerror(errno));
                exit(1);
        }
                exit(1);
        }
-       ddev = find_dev(dsb.st_dev, S_IFBLK);
-       dfd = Open(ddev, O_RDONLY);
-       Lseek(dfd, SBOFF, L_SET);
-       Read(dfd, (char *)&fs, sizeof (fs));
-       close(dfd);
-       spacefree = freespace(&fs, fs.fs_minfree) * fs.fs_fsize / 1024;
-       if (spacefree < read_number("minfree")) {
-               log(LOG_WARNING, "Dump omitted, not enough space on device\n");
-               return (0);
+       (void)strcpy(devname, _PATH_DEV);
+       while ((dir = readdir(dfd))) {
+               (void)strcpy(devname + sizeof(_PATH_DEV) - 1, dir->d_name);
+               if (stat(devname, &sb)) {
+                       syslog(LOG_ERR, "%s: %s", devname, strerror(errno));
+                       continue;
+               }
+               if ((sb.st_mode & S_IFMT) != type)
+                       continue;
+               if (dev == sb.st_rdev) {
+                       closedir(dfd);
+                       if ((dp = strdup(devname)) == NULL) {
+                               syslog(LOG_ERR, "%s", strerror(errno));
+                               exit(1);
+                       }
+                       return (dp);
+               }
        }
        }
-       if (freespace(&fs, fs.fs_minfree) < 0)
-               log(LOG_WARNING,
-                   "Dump performed, but free space threshold crossed\n");
-       return (1);
+       closedir(dfd);
+       syslog(LOG_ERR, "can't find device %d/%d", major(dev), minor(dev));
+       exit(1);
 }
 
 }
 
-read_number(fn)
-       char *fn;
+char *
+rawname(s)
+       char *s;
 {
 {
-       char lin[80];
-       register FILE *fp;
+       char *sl, name[MAXPATHLEN];
+
+       if ((sl = rindex(s, '/')) == NULL || sl[1] == '0') {
+               syslog(LOG_ERR,
+                   "can't make raw dump device name from %s", s);
+               return (s);
+       }
+       (void)snprintf(name, sizeof(name), "%.*s/r%s", sl - s, s, sl + 1);
+       if ((sl = strdup(name)) == NULL) {
+               syslog(LOG_ERR, "%s", strerror(errno));
+               exit(1);
+       }
+       return (sl);
+}
+
+int
+get_crashtime()
+{
+       time_t dumptime;                        /* Time the dump was taken. */
 
 
-       fp = fopen(path(fn), "r");
-       if (fp == NULL)
+       Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_TIME].n_value)), L_SET);
+       (void)Read(dumpfd, &dumptime, sizeof(dumptime));
+       if (dumptime == 0) {
+               if (verbose)
+                       syslog(LOG_ERR, "dump time is zero");
                return (0);
                return (0);
-       if (fgets(lin, 80, fp) == NULL) {
-               fclose(fp);
+       }
+       (void)printf("savecore: system went down at %s", ctime(&dumptime));
+#define        LEEWAY  (7 * SECSPERDAY)
+       if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
+               (void)printf("dump time is unreasonable\n");
                return (0);
        }
                return (0);
        }
-       fclose(fp);
-       return (atoi(lin));
+       return (1);
 }
 
 }
 
-#define        BUFPAGES        (256*1024/NBPG)         /* 1/4 Mb */
-
-save_core()
+int
+check_space()
 {
 {
-       register int n;
-       register char *cp;
-       register int ifd, ofd, bounds;
        register FILE *fp;
        register FILE *fp;
-
-       cp = malloc(BUFPAGES*NBPG);
-       if (cp == 0) {
-               fprintf(stderr, "savecore: Can't allocate i/o buffer.\n");
-               return;
+       char *tvmunix;
+       off_t minfree, spacefree, vmunixsize, needed;
+       struct stat st;
+       struct statfs fsbuf;
+       char buf[100], path[MAXPATHLEN];
+
+       tvmunix = vmunix ? vmunix : _PATH_UNIX;
+       if (stat(tvmunix, &st) < 0) {
+               syslog(LOG_ERR, "%s: %m", tvmunix);
+               exit(1);
        }
        }
-       bounds = read_number("bounds");
-       ifd = Open(system?system:"/vmunix", O_RDONLY);
-       while((n = Read(ifd, cp, BUFSIZ)) > 0)
-               Write(ofd, cp, n);
-       close(ifd);
-       close(ofd);
-       ifd = Open(ddname, O_RDONLY);
-       Lseek(ifd, (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), L_SET);
-       Read(ifd, (char *)&dumpsize, sizeof (dumpsize));
-       (void)sprintf(cp, "vmcore.%d", bounds);
-       ofd = Create(path(cp), 0644);
-       Lseek(ifd, (off_t)dumplo, L_SET);
-       log(LOG_NOTICE, "Saving %d bytes of image in vmcore.%d\n",
-           NBPG*dumpsize, bounds);
-       while (dumpsize > 0) {
-               n = Read(ifd, cp,
-                   (dumpsize > BUFPAGES ? BUFPAGES : dumpsize) * NBPG);
-               if (n == 0) {
-                       log(LOG_WARNING, "WARNING: vmcore may be incomplete\n");
-                       break;
-               }
-               Write(ofd, cp, n);
-               dumpsize -= n/NBPG;
+       vmunixsize = st.st_blocks * S_BLKSIZE;
+       if (statfs(dirname, &fsbuf) < 0) {
+               syslog(LOG_ERR, "%s: %m", dirname);
+               exit(1);
+       }
+       spacefree = (fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
+
+       (void)snprintf(path, sizeof(path), "%s/minfree", dirname);
+       if ((fp = fopen(path, "r")) == NULL)
+               minfree = 0;
+       else {
+               if (fgets(buf, sizeof(buf), fp) == NULL)
+                       minfree = 0;
+               else
+                       minfree = atoi(buf);
+               (void)fclose(fp);
+       }
+
+       needed = (dumpsize + vmunixsize) / 1024;
+       if (minfree > 0 && spacefree - needed < minfree) {
+               syslog(LOG_WARNING,
+                   "no dump, not enough free space on device");
+               return (0);
        }
        }
-       close(ifd);
-       close(ofd);
-       fp = fopen(path("bounds"), "w");
-       fprintf(fp, "%d\n", bounds+1);
-       fclose(fp);
-       free(cp);
+       if (spacefree - needed < minfree)
+               syslog(LOG_WARNING,
+                   "dump performed, but free space threshold crossed");
+       return (1);
 }
 
 }
 
-/*
- * Versions of std routines that exit on error.
- */
+int
 Open(name, rw)
        char *name;
        int rw;
 {
        int fd;
 
 Open(name, rw)
        char *name;
        int rw;
 {
        int fd;
 
-       fd = open(name, rw);
-       if (fd < 0) {
-               Perror(LOG_ERR, "%s: %m", name);
+       if ((fd = open(name, rw, 0)) < 0) {
+               syslog(LOG_ERR, "%s: %m", name);
                exit(1);
        }
        return (fd);
 }
 
                exit(1);
        }
        return (fd);
 }
 
-Read(fd, buff, size)
+int
+Read(fd, bp, size)
        int fd, size;
        int fd, size;
-       char *buff;
+       void *bp;
 {
 {
-       int ret;
+       int nr;
 
 
-       ret = read(fd, buff, size);
-       if (ret < 0) {
-               Perror(LOG_ERR, "read: %m");
+       nr = read(fd, bp, size);
+       if (nr != size) {
+               syslog(LOG_ERR, "read: %m");
                exit(1);
        }
                exit(1);
        }
-       return (ret);
+       return (nr);
 }
 
 }
 
-off_t
+void
 Lseek(fd, off, flag)
        int fd, flag;
 Lseek(fd, off, flag)
        int fd, flag;
-       long off;
+       off_t off;
 {
 {
-       long ret;
+       off_t ret;
 
        ret = lseek(fd, off, flag);
        if (ret == -1) {
 
        ret = lseek(fd, off, flag);
        if (ret == -1) {
-               Perror(LOG_ERR, "lseek: %m");
+               syslog(LOG_ERR, "lseek: %m");
                exit(1);
        }
                exit(1);
        }
-       return (ret);
 }
 
 }
 
+int
 Create(file, mode)
        char *file;
        int mode;
 Create(file, mode)
        char *file;
        int mode;
@@ -465,39 +570,28 @@ Create(file, mode)
 
        fd = creat(file, mode);
        if (fd < 0) {
 
        fd = creat(file, mode);
        if (fd < 0) {
-               Perror(LOG_ERR, "%s: %m", file);
+               syslog(LOG_ERR, "%s: %m", file);
                exit(1);
        }
        return (fd);
 }
 
                exit(1);
        }
        return (fd);
 }
 
-Write(fd, buf, size)
+void
+Write(fd, bp, size)
        int fd, size;
        int fd, size;
-       char *buf;
+       void *bp;
 {
 {
+       int n;
 
 
-       if (write(fd, buf, size) < size) {
-               Perror(LOG_ERR, "write: %m");
+       if ((n = write(fd, bp, size)) < size) {
+               syslog(LOG_ERR, "write: %s", strerror(n == -1 ? errno : EIO));
                exit(1);
        }
 }
 
                exit(1);
        }
 }
 
-log(level, msg, a1, a2)
-       int level;
-       char *msg;
+void
+usage()
 {
 {
-
-       fprintf(stderr, msg, a1, a2);
-       syslog(level, msg, a1, a2);
-}
-
-Perror(level, msg, s)
-       int level;
-       char *msg;
-{
-       int oerrno = errno;
-       
-       perror(s);
-       errno = oerrno;
-       syslog(level, msg, s);
+       (void)syslog(LOG_ERR, "usage: savecore [-cfvz] [-N system] directory");
+       exit(1);
 }
 }