* Copyright (c) 1986, 1992 The Regents of the University of California.
* %sccs.include.redist.c%
"@(#) Copyright (c) 1986, 1992 The Regents of the University of California.\n\
static char sccsid
[] = "@(#)savecore.c 5.34 (Berkeley) %G%";
#define ok(number) ((number) - KERNBASE)
struct nlist current_nl
[] = { /* Namelist for currently running system. */
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[]. */
/* Types match kernel declarations. */
long dumplo
; /* where dump starts on dumpdev */
int dumpmag
; /* magic number in dump */
int dumpsize
; /* amount of memory dumped */
char *dirname
; /* directory to save dumps in */
char *ddname
; /* name of dump device */
dev_t dumpdev
; /* dump device */
int dumpfd
; /* read/write descriptor on block dev */
time_t now
; /* current date */
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 Write
__P((int, void *, int));
openlog("savecore", LOG_PERROR
, LOG_DAEMON
);
while ((ch
= getopt(argc
, argv
, "cdfNvz")) != EOF
)
case 'd': /* Not documented. */
if (argc
!= 1 && argc
!= 2)
* 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
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) {
syslog(LOG_ERR
, "%s: %s not in namelist",
_PATH_UNIX
, current_nl
[cursyms
[i
]].n_name
);
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) {
syslog(LOG_ERR
, "%s: %s not in namelist",
dump_sys
, dump_nl
[dumpsyms
[i
]].n_name
);
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
));
(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
));
ddname
= find_dev(dumpdev
, S_IFBLK
);
dumpfd
= Open(ddname
, O_RDWR
);
syslog(LOG_ERR
, "%s: fdopen: %m", _PATH_KMEM
);
(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. */
fp
= fdopen(dumpfd
, "r");
syslog(LOG_ERR
, "%s: fdopen: %m", ddname
);
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)
"warning: %s version mismatch:\n\t%s\nand\t%s\n",
_PATH_UNIX
, vers
, core_vers
);
(off_t
)(dumplo
+ ok(dump_nl
[X_PANICSTR
].n_value
)), L_SET
);
(void)fread(&panicstr
, sizeof(panicstr
), 1, fp
);
(void)fseek(fp
, dumplo
+ ok(panicstr
), L_SET
);
while (*cp
++ && cp
< &panic_mesg
[sizeof(panic_mesg
)]);
/* Don't fclose(fp), we use dumpfd later. */
Lseek(dumpfd
, (off_t
)(dumplo
+ ok(dump_nl
[X_DUMPMAG
].n_value
)), L_SET
);
Write(dumpfd
, &newdumplo
, sizeof(newdumplo
));
Lseek(dumpfd
, (off_t
)(dumplo
+ ok(dump_nl
[X_DUMPMAG
].n_value
)), L_SET
);
(void)Read(dumpfd
, &newdumpmag
, sizeof(newdumpmag
));
if (newdumpmag
!= dumpmag
) {
syslog(LOG_WARNING
, "magic number mismatch (%x != %x)",
syslog(LOG_WARNING
, "no core dump");
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
)
if (fgets(buf
, sizeof(buf
), fp
) == NULL
) {
err1
: syslog(LOG_WARNING
, "%s: %s", path
, strerror(errno
));
if ((fp
= fopen(path
, "w")) == NULL
)
syslog(LOG_ERR
, "%s: %m", path
);
(void)fprintf(fp
, "%d\n", bounds
+ 1);
/* Create the core file. */
(void)snprintf(path
, sizeof(path
), "%s/vmcore.%d%s",
dirname
, bounds
, compress
? ".Z" : "");
if ((fp
= zopen(path
, "w", 0)) == NULL
) {
syslog(LOG_ERR
, "%s: %s", path
, strerror(errno
));
ofd
= Create(path
, S_IRUSR
| S_IWUSR
);
/* Open the raw device. */
if ((ifd
= open(rawp
, O_RDONLY
)) == -1) {
syslog(LOG_WARNING
, "%s: %m; using block device", rawp
);
/* 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
);
/* Copy the core file. */
syslog(LOG_NOTICE
, "writing %score to %s",
compress
? "compressed " : "", path
);
for (; dumpsize
> 0; dumpsize
-= nr
) {
(void)printf("%6dK\r", dumpsize
/ 1024);
nr
= read(ifd
, buf
, MIN(dumpsize
, sizeof(buf
)));
"WARNING: EOF on dump device");
syslog(LOG_ERR
, "%s: %m", rawp
);
nw
= fwrite(buf
, 1, nr
, fp
);
nw
= write(ofd
, buf
, nr
);
syslog(LOG_ERR
, "%s: %s",
path
, strerror(nw
== 0 ? EIO
: errno
));
err2
: syslog(LOG_WARNING
,
"WARNING: vmcore may be incomplete");
ifd
= Open(vmunix
? vmunix
: _PATH_UNIX
, O_RDONLY
);
(void)snprintf(path
, sizeof(path
), "%s/vmunix.%d%s",
dirname
, bounds
, compress
? ".Z" : "");
if ((fp
= zopen(path
, "w", 0)) == NULL
) {
syslog(LOG_ERR
, "%s: %s", path
, strerror(errno
));
ofd
= Create(path
, S_IRUSR
| S_IWUSR
);
syslog(LOG_NOTICE
, "writing %skernel to %s",
compress
? "compressed " : "", path
);
while ((nr
= read(ifd
, buf
, sizeof(buf
))) > 0) {
nw
= fwrite(buf
, 1, nr
, fp
);
nw
= write(ofd
, buf
, nr
);
syslog(LOG_ERR
, "%s: %s",
path
, strerror(nw
== 0 ? EIO
: errno
));
"WARNING: vmunix may be incomplete");
syslog(LOG_ERR
, "%s: %s",
vmunix
? vmunix
: _PATH_UNIX
, strerror(errno
));
"WARNING: vmunix may be incomplete");
char *dp
, devname
[MAXPATHLEN
+ 1];
if ((dfd
= opendir(_PATH_DEV
)) == NULL
) {
syslog(LOG_ERR
, "%s: %s", _PATH_DEV
, strerror(errno
));
(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
));
if ((sb
.st_mode
& S_IFMT
) != type
)
if ((dp
= strdup(devname
)) == NULL
) {
syslog(LOG_ERR
, "%s", strerror(errno
));
syslog(LOG_ERR
, "can't find device %d/%d", major(dev
), minor(dev
));
char *sl
, name
[MAXPATHLEN
];
if ((sl
= rindex(s
, '/')) == NULL
|| sl
[1] == '0') {
"can't make raw dump device name from %s", 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
));
time_t dumptime
; /* Time the dump was taken. */
Lseek(dumpfd
, (off_t
)(dumplo
+ ok(dump_nl
[X_TIME
].n_value
)), L_SET
);
(void)Read(dumpfd
, &dumptime
, sizeof(dumptime
));
syslog(LOG_ERR
, "dump time is zero");
(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");
off_t minfree
, spacefree
;
char buf
[100], path
[MAXPATHLEN
];
if (statfs(dirname
, &fsbuf
) < 0) {
syslog(LOG_ERR
, "%s: %m", dirname
);
spacefree
= (fsbuf
.f_bavail
* fsbuf
.f_bsize
) / 1024;
(void)snprintf(path
, sizeof(path
), "%s/minfree", dirname
);
if ((fp
= fopen(path
, "r")) == NULL
)
if (fgets(buf
, sizeof(buf
), fp
) == NULL
)
if (minfree
> 0 && spacefree
- dumpsize
< minfree
) {
"no dump, not enough free space on device");
if (spacefree
- dumpsize
< minfree
)
"dump performed, but free space threshold crossed");
if ((fd
= open(name
, rw
, 0)) < 0) {
syslog(LOG_ERR
, "%s: %m", name
);
syslog(LOG_ERR
, "read: %m");
ret
= lseek(fd
, off
, flag
);
syslog(LOG_ERR
, "lseek: %m");
syslog(LOG_ERR
, "%s: %m", file
);
if ((n
= write(fd
, bp
, size
)) < size
) {
syslog(LOG_ERR
, "write: %s", strerror(n
== -1 ? errno
: EIO
));
(void)syslog(LOG_ERR
, "usage: savecore [-cfvz] [-N system] directory");