1
0
Files
irix-657m-src/irix/cmd/savecore/savecore.c
2022-09-29 17:59:04 +03:00

836 lines
18 KiB
C

/*
* Copyright (c) 1980,1986 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
/*
* There are a number of exit codes with the new 'savecore' binary:
*
* 0 = success (core dump saved)
* 1 = fail (could not save core dump for any reason)
* 2 = partial fail (could not save core dump due to space reasons)
*/
#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1980,1986 Regents of the University of California.\n\
All rights reserved.\n";
#endif /* not lint */
#ifndef lint
static char sccsid[] = "@(#)savecore.c 5.8 (Berkeley) 5/26/86";
#endif /* not lint */
/*
* savecore
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <nlist.h>
#include <sys/param.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/immu.h>
#include <sys/sysmacros.h>
#include <sys/utsname.h>
#include <sys/dump.h>
#include <syslog.h>
#include <diskinfo.h>
#include <sys/capability.h>
#define MAXLEN 80
#define eq(a,b,l) (!bcmp(a,b,l))
#if defined(vax) || defined(mips)
#define ok(number) ((number)&0x7fffffff)
#else
#define ok(number) (number)
#endif
#if (_MIPS_SZLONG == 64)
struct nlist64 current_nl[] = { /* namelist for currently running system */
#define X_DUMPDEV 0
{ "dumpdev" },
#define X_DUMPLO 1
{ "dumplo" },
#define X_TIME 2
{ "time" },
#define X_DUMPSIZE 3
{ "dumpsize" },
#define X_UTSNAME 4
{ "utsname" },
#define X_PANICSTR 5
{ "panicstr" },
#define X_DUMPMAG 6
{ "dumpmag" },
#define X_KERNEL_MAGIC 7
{ "kernel_magic" },
#define X_END 8
{ "end" },
#define X_PAGECONST 9
{ "_pageconst" },
{ "" },
};
struct nlist64 dump_nl[] = { /* name list for dumped system */
{ "dumpdev" }, /* entries MUST be the same as */
{ "dumplo" }, /* those in current_nl[] */
{ "time" },
{ "dumpsize" },
{ "utsname" },
{ "panicstr" },
{ "dumpmag" },
{ "kernel_magic" },
{ "end" },
{ "_pageconst" },
{ "" },
};
#else
struct nlist current_nl[] = { /* namelist for currently running system */
#define X_DUMPDEV 0
{ "dumpdev" },
#define X_DUMPLO 1
{ "dumplo" },
#define X_TIME 2
{ "time" },
#define X_DUMPSIZE 3
{ "dumpsize" },
#define X_UTSNAME 4
{ "utsname" },
#define X_PANICSTR 5
{ "panicstr" },
#define X_DUMPMAG 6
{ "dumpmag" },
#define X_KERNEL_MAGIC 7
{ "kernel_magic" },
#define X_END 8
{ "end" },
#define X_PAGECONST 9
{ "_pageconst" },
{ "" },
};
struct nlist dump_nl[] = { /* name list for dumped system */
{ "dumpdev" }, /* entries MUST be the same as */
{ "dumplo" }, /* those in current_nl[] */
{ "time" },
{ "dumpsize" },
{ "utsname" },
{ "panicstr" },
{ "dumpmag" },
{ "kernel_magic" },
{ "end" },
{ "_pageconst" },
{ "" },
};
#endif /* _MIPS_SZLONG */
char *systemfile;
char *dirname; /* directory to save dumps in */
char *dd_bname, *dd_rname; /* name of dump block and raw device */
time_t dumptime; /* time the dump was taken */
int dumpsize; /* amount of memory dumped */
int dumpdevsize; /* size of the dump device */
int dumplo; /* where dump starts on dumpdev */
unsigned long long new_dumpmag; /* magic number in new dump headers */
unsigned int old_dumpmag; /* magic number in old dumps */
dump_hdr_t dump_hdr; /* Dump header */
struct utsname cur_uts;
struct utsname core_uts;
int panicstr;
char panic_mesg[MAXLEN];
int Force = 0;
int Verbose = 0;
int Avail = 0; /* activates availability monitor */
/*
* We get the pagesize from the /unix that we're going to save
*/
static int pagesize;
extern int errno;
/* Function prototypes */
ssize_t Write(int, char *, ssize_t);
int Create(char *, int);
off_t Lseek(int, long, int);
ssize_t Read(int, char *, ssize_t);
int Open(char *, int);
unsigned int read_number(char *);
char * path(char *);
char * stat_dev(char *, char *, dev_t, int);
char * find_dev(char *, char *, dev_t, int);
void save_core(int);
void log_putbuf(dump_hdr_t *, int);
void logheader(dump_hdr_t *, int);
extern void get_panicstr(char *, dump_hdr_t *d, int);
int read_kmem(void);
void get_crashtime(void);
int dump_exists(int);
void clear_dump(void);
void print_dumpsize_warning(int,int);
void logavail(dump_hdr_t *dh);
void expand_header(dump_hdr_t *dump_hdr, FILE *fp);
main(int argc, char **argv)
{
char *cp;
int slash_unix; /* Set when /unix is the running system */
if (geteuid() != 0) {
fprintf(stderr,
"savecore: Must be root to use this program\n");
exit(1);
}
argc--, argv++;
while (argc > 0 && argv[0][0] == '-') {
for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
case 'f':
Force++;
break;
case 'v':
Verbose++;
break;
case 'a':
Avail++;
break;
default:
usage:
fprintf(stderr,
"usage: savecore [-f] [-v] dirname [ system ]\n");
exit(1);
}
argc--, argv++;
}
if (argc != 1 && argc != 2)
goto usage;
dirname = argv[0];
if (argc == 2)
systemfile = argv[1];
openlog("savecore", LOG_ODELAY, LOG_AUTH);
if (access(dirname, W_OK) < 0) {
int oerrno = errno;
perror(dirname);
errno = oerrno;
syslog(LOG_ERR, "%s: %m", dirname);
exit(1);
}
/*
* If /unix isn't the running system, we need to make our best
* guess. Try the beginning of /dev/swap. These things can be changed
* on the kernel command line, but guessing wrong here isn't fatal.
*/
if (!(slash_unix = read_kmem())) {
if (Verbose)
fprintf(stderr, "savecore: Trying default parameters.\n");
pagesize = getpagesize(); /* This will "usually" be correct */
dd_bname = "/dev/swap";
dd_rname = "/dev/rswap";
dumplo = 0;
} else {
int ncpu = sysconf(_SC_NPROC_CONF);
/*
* if we have access to the symbols and we are running on a
* large configuration, make sure that we have enough dump
* space available.
* The only other time where this would be checked is if we take
* a hit and run out of dump space resulting in an incomplete
* system dump .....
*/
if (ncpu >= 64) {
/*
* our recommendation is 256Mbyte per 32CPU (8MB CPU)
*
*/
int dump_in_mb = ((dumpdevsize*sysconf(_SC_PAGESIZE))
/ (1024 * 1024));
if (dump_in_mb < (ncpu * 8))
print_dumpsize_warning(dump_in_mb,ncpu);
}
}
if (!dump_exists(Force)) {
if (Verbose)
fprintf(stderr, "savecore: No dump exists.\n");
exit(1);
} else {
clear_dump();
}
/* Grab the panic string from the dump header. */
get_panicstr(panic_mesg, &dump_hdr, MAXLEN);
if (strlen(panic_mesg))
syslog(LOG_CRIT, "reboot after panic: %s", panic_mesg);
else
syslog(LOG_CRIT, "reboot");
/* Copy the putbuf from the dump header to syslog. */
log_putbuf(&dump_hdr, 1);
/* Get the crash time and log it. */
get_crashtime();
/*
* if avail monitoring, dump header to availdir
*/
if (Avail)
{
logavail(&dump_hdr);
}
save_core(slash_unix);
exit(0);
/*NOTREACHED*/
}
int
dump_exists(int force)
{
register int dumpfd;
unsigned long long word;
dumpfd = Open(dd_bname, O_RDONLY);
Lseek(dumpfd, (off_t) dumplo, L_SET);
new_dumpmag = DUMP_MAGIC;
Read(dumpfd, (char *)&word, sizeof (word));
close(dumpfd);
if (Verbose && word != new_dumpmag) {
printf("dumplo = %d (%d bytes)\n", dumplo/NBPSCTR, dumplo);
printf("Magic number mismatch: 0x%llx != 0x%llx\n", word,
new_dumpmag);
}
if ((word == new_dumpmag) || force) {
dumpfd = Open(dd_bname, O_RDONLY);
Lseek(dumpfd, (off_t) dumplo, L_SET);
Read(dumpfd, (char *)&dump_hdr, sizeof(dump_hdr_t));
close(dumpfd);
}
return ((word == new_dumpmag) || force);
}
void
clear_dump(void)
{
register int dumpfd;
long long zero = 0LL;
cap_t ocap;
cap_value_t cap_mac_write = CAP_MAC_WRITE;
ocap = cap_acquire(1, &cap_mac_write);
dumpfd = Open(dd_bname, O_WRONLY);
cap_surrender(ocap);
/* Clear dump magic number */
Lseek(dumpfd, (off_t) dumplo, L_SET);
Write(dumpfd, (char *)&zero, sizeof (long long));
close(dumpfd);
}
char *
stat_dev(char *dir, char *name, dev_t dev, int type)
{
struct stat statb;
static char devname[MAXPATHLEN + 1];
char *dp;
strcpy(devname, dir);
strcat(devname, "/");
strcat(devname, name);
if (stat(devname, &statb)) {
if (Verbose)
perror(devname);
return (NULL);
}
if (((statb.st_mode&S_IFMT) == type) && (dev == statb.st_rdev)) {
dp = malloc(strlen(devname)+1);
strcpy(dp, devname);
return (dp);
}
return (NULL);
}
char *
find_dev(register char *devdir, register char *devhint,
register dev_t dev, register int type)
{
register DIR *dfd;
struct dirent *dir;
char *dp;
/* check hint file (i.e. /dev/swap) first */
if (devhint != NULL) {
if ((dp = stat_dev(devdir, devhint, dev, type)) != NULL)
return (dp);
}
dfd = opendir(devdir);
if (dfd) {
while ((dir = readdir(dfd))) {
if ((dp = stat_dev(devdir, dir->d_name, dev, type)) != NULL) {
closedir(dfd);
return (dp);
}
}
closedir(dfd);
}
return((char *)NULL);
}
int cursyms[] =
{ X_DUMPDEV, X_DUMPLO, -1 };
/* true == /unix is the running system. 0 == /unix isn't the running system
* Don't copy /unix if it's not what's running... Odds are it isn't what
* _was_ running either. That's what the header's uname is for.
*/
int
read_kmem(void)
{
char *dump_sys;
struct pageconst pageconst;
int kmem, i;
long kernel_magic;
dev_t dumpdev; /* dump device */
char *tmpname;
dump_sys = systemfile ? systemfile : "/unix";
#if (_MIPS_SZLONG == 64)
nlist64("/unix", current_nl);
nlist64(dump_sys, dump_nl);
#else
nlist("/unix", current_nl);
nlist(dump_sys, dump_nl);
#endif
/*
* Check that /unix and /dev/kmem match. If they don't match,
* let main() setup the default values, since incorrectly
* lseeking into /dev/kmem and reading (e.g. for pagesize)
* is far worse than the default.
*/
kmem = Open("/dev/kmem", O_RDONLY);
Lseek(kmem, (long)current_nl[X_KERNEL_MAGIC].n_value, L_SET);
Read(kmem, (char *)&kernel_magic, sizeof(kernel_magic));
if (kernel_magic != current_nl[X_END].n_value) {
fprintf(stderr, "savecore: /unix is not the running system\n");
syslog(LOG_ERR, "/unix is not the running system\n");
return 0;
}
/*
* 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!)
*
* Actually, with our new dump format, we don't want any symbols
* from the dump.
*/
for (i = 0; cursyms[i] != -1; i++)
if (current_nl[cursyms[i]].n_value == 0) {
fprintf(stderr, "savecore: /unix: %s not in namelist\n",
current_nl[cursyms[i]].n_name);
syslog(LOG_ERR, "/unix: %s not in namelist",
current_nl[cursyms[i]].n_name);
exit(1);
}
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_PAGECONST].n_value, L_SET);
Read(kmem, (char *)&pageconst, sizeof (pageconst));
pagesize = pageconst.p_pagesz;
Lseek(kmem, (long)current_nl[X_DUMPSIZE].n_value, L_SET);
Read(kmem, (char *)&dumpdevsize,sizeof (dumpdevsize));
dumplo *= NBPSCTR;
if ((dd_bname = find_dev("/dev", "swap", dumpdev, S_IFBLK)) == NULL)
if ((dd_bname = find_dev("/dev/dsk", NULL, dumpdev, S_IFBLK))
== NULL) {
fprintf(stderr,
"savecore: can't find block device corresponding to %d/%d\n",
major(dumpdev), minor(dumpdev));
syslog(LOG_ERR,
"can't find block device corresponding to %d/%d\n",
major(dumpdev), minor(dumpdev));
exit(1);
}
if ((tmpname = findrawpath(dd_bname)) == NULL) {
fprintf(stderr,
"savecore: can't find raw device corresponding to %s\n",
dd_bname);
syslog(LOG_ERR,
"can't find raw device corresponding to %d/%d\n",
major(dumpdev), minor(dumpdev));
exit(1);
}
dd_rname = strdup(tmpname);
if (systemfile)
return (kernel_magic == current_nl[X_END].n_value);
close(kmem);
return (kernel_magic == current_nl[X_END].n_value);
}
void
get_crashtime(void)
{
/*
* Get the panic time from either the variable in the image or
* The dump header if it's available.
*/
dumptime = dump_hdr.dmp_crash_time;
/* If we get a bogus dump time, don't print ... 1970 */
if (dumptime == 0) {
if (Verbose)
printf("Dump time not found.\n");
syslog(LOG_ERR, "Dump time not found.\n");
} else {
printf("System went down at %s", ctime(&dumptime));
syslog(LOG_CRIT, "System went down at %s", ctime(&dumptime));
}
}
char *
path(char *file)
{
char *cp = malloc(strlen(file) + strlen(dirname) + 2);
(void) strcpy(cp, dirname);
(void) strcat(cp, "/");
(void) strcat(cp, file);
return (cp);
}
long long
check_space(void)
{
struct stat dsb;
long long spacefree;
struct statfs fs;
if (stat(dirname, &dsb) < 0) {
int oerrno = errno;
perror(dirname);
errno = oerrno;
syslog(LOG_ERR, "%s: %m", dirname);
exit(1);
}
statfs(dirname, &fs, sizeof(fs), 0);
spacefree = (long long)fs.f_bfree * fs.f_bsize;
return spacefree - read_number("minfree");
}
unsigned int
read_number(char *fn)
{
char lin[80];
register FILE *fp;
fp = fopen(path(fn), "r");
if (fp == NULL)
return (0);
if (fgets(lin, 80, fp) == NULL) {
fclose(fp);
return (0);
}
fclose(fp);
return (atoi(lin));
}
#define BUFPAGES (256*1024/pagesize) /* 1/4 Mb */
void
save_core(int slash_unix)
{
register int amount;
register char *cp;
register int ifd, ofd, bounds;
register FILE *fp;
long long space_to_use;
long long dump_bytes;
ssize_t n;
/* space_to_use == freespace - minfree. */
space_to_use = check_space();
if (Verbose)
printf("savecore: %lld bytes available for dump.\n",
space_to_use);
/* Don't save anything if space_to_use <= 0 -- old savecore behavior */
if (space_to_use <= 0) {
printf("savecore: Dump omitted, not enough space on device\n");
syslog(LOG_WARNING,
"dump omitted, not enough space on device");
exit(2);
}
cp = malloc(BUFPAGES*pagesize);
if (cp == 0) {
fprintf(stderr, "savecore: Can't allocate i/o buffer.\n");
return;
}
bounds = read_number("bounds");
/* Save the dump header in /var/adm/crash/crashlog.%d */
logheader(&dump_hdr, bounds);
if (slash_unix) {
ifd = Open(systemfile?systemfile:"/unix", O_RDONLY);
sprintf(cp, "unix.%d", bounds);
ofd = Create(path(cp), 0600);
while((n = Read(ifd, cp, BUFSIZ)) > 0)
Write(ofd, cp, n);
close(ifd);
close(ofd);
} else {
fprintf(stderr, "savecore: Not saving /unix.\n");
}
/* Get the actual dump size from the header. */
dumpsize = dump_hdr.dmp_pages;
dump_bytes = (long long)pagesize * dumpsize;
if (dump_bytes > space_to_use) {
syslog(LOG_NOTICE, "Truncating dump. (Original size %lld)\n",
dump_bytes);
if (Verbose)
printf("Truncating dump. (Original size %lld)\n",
dump_bytes);
dump_bytes = space_to_use;
dumpsize = dump_bytes / pagesize;
}
ifd = Open(dd_rname, O_RDONLY);
sprintf(cp, "vmcore.%d.comp", bounds);
ofd = Create(path(cp), 0600);
Lseek(ifd, (off_t)dumplo, L_SET);
printf("savecore: Saving %lld bytes of image in %s/vmcore.%d.comp\n",
dump_bytes, dirname, bounds);
syslog(LOG_NOTICE, "saving %lld bytes of image in %s/vmcore.%d.comp\n",
dump_bytes, dirname, bounds);
while (dumpsize > 0) {
amount = (dumpsize > BUFPAGES ? BUFPAGES : dumpsize) * pagesize;
n = Read(ifd, cp, amount);
if (n != amount) {
printf("savecore: warning: vmcore may be incomplete\n");
syslog(LOG_WARNING,
"warning: vmcore may be incomplete\n");
break;
}
if (Write(ofd, cp, n) != n) {
printf("savecore: warning: vmcore incomplete\n");
syslog(LOG_WARNING,
"warning: vmcore incomplete\n");
break;
}
dumpsize -= n/pagesize;
}
close(ifd);
/* Restore magic number! */
Lseek(ofd, 0, L_SET);
Write(ofd, (char *)&(new_dumpmag), sizeof(dump_hdr.dmp_magic));
close(ofd);
fp = fopen(path("bounds"), "w");
fprintf(fp, "%d\n", bounds+1);
fclose(fp);
free(cp);
}
void
logavail(dump_hdr_t *dh)
{
FILE *fp;
char *logfile = "/var/adm/avail/availlog";
if ((fp = fopen(logfile, "ab")) == NULL) {
printf("savecore::logavail => can't open %s (OK)\n",
logfile);
perror(logfile);
return;
}
fprintf(fp, "CRASH|Savecore/Begin Header\n");
expand_header(dh, fp);
fprintf(fp, "CRASH|Savecore/End Header\n");
fprintf(fp, "CRASH|%ld|%s", dumptime, ctime(&dumptime));
/* newline is included in output of ctime() */
fclose(fp);
}
/* Create a /var/adm/crashlog.%d file that contains the dump header. */
void
logheader(dump_hdr_t *dh, int bounds)
{
FILE *fp;
char logfile[20];
sprintf(logfile, "crashlog.%d", bounds);
if ((fp = fopen(path(logfile), "w")) == NULL) {
printf("savecore: Can't create %s\n", path(logfile));
perror("savecore");
return;
}
fprintf(fp, "savecore: Created log %s", ctime(&dumptime));
expand_header(dh, fp);
fclose(fp);
}
/*
* Versions of std routines that exit on error.
*/
Open(char *name, int rw)
{
int fd;
fd = open(name, rw);
if (fd < 0) {
int oerrno = errno;
perror(name);
errno = oerrno;
syslog(LOG_ERR, "%s: %m", name);
exit(1);
}
return (fd);
}
ssize_t
Read(int fd, char *buff, ssize_t size)
{
ssize_t ret;
ret = read(fd, buff, size);
if (ret < 0) {
int oerrno = errno;
perror("read");
errno = oerrno;
syslog(LOG_ERR, "read: %m");
exit(1);
}
return (ret);
}
off_t
Lseek(int fd, long off, int flag)
{
long ret;
if ((flag == L_SET) && (off & 0x80000000))
off &= ~0x80000000;
ret = lseek(fd, off, flag);
if (ret == -1) {
int oerrno = errno;
perror("lseek");
errno = oerrno;
syslog(LOG_ERR, "lseek: %m");
exit(1);
}
return (ret);
}
int
Create(char *file, int mode)
{
register int fd;
fd = creat(file, mode);
if (fd < 0) {
int oerrno = errno;
perror(file);
errno = oerrno;
syslog(LOG_ERR, "%s: %m", file);
exit(1);
}
return (fd);
}
ssize_t
Write(int fd, char *buf, ssize_t size)
{
ssize_t n;
if ((n = write(fd, buf, size)) < 0) {
int oerrno = errno;
perror("write");
errno = oerrno;
syslog(LOG_ERR, "write: %m");
exit(1);
}
return (n);
}
void
print_dumpsize_warning(int dumpsize_in_mb,int ncpu)
{
printf(
"\nWARNING : The size of the dump device (%d MB) might be too small\n",
dumpsize_in_mb);
printf(
"for a system dump. The recommended size for this machine is %d MB\n\n",
ncpu * 8);
}