lots of mickey-mouse things; use syslog; compress nlist checks
[unix-history] / usr / src / sbin / savecore / savecore.c
CommitLineData
5ff67f98
DF
1/*
2 * Copyright (c) 1980 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
261f5d78 7#ifndef lint
5ff67f98
DF
8char copyright[] =
9"@(#) Copyright (c) 1980 Regents of the University of California.\n\
10 All rights reserved.\n";
11#endif not lint
12
13#ifndef lint
865f9d47 14static char sccsid[] = "@(#)savecore.c 5.5 (Berkeley) %G%";
5ff67f98 15#endif not lint
261f5d78 16
d112e9ff 17/*
97d6da0b 18 * savecore
d112e9ff 19 */
a3e4f1d7 20
d112e9ff
BJ
21#include <stdio.h>
22#include <nlist.h>
23#include <sys/param.h>
24#include <sys/dir.h>
25#include <sys/stat.h>
ce4fd43b 26#include <sys/time.h>
865f9d47
SL
27#include <sys/file.h>
28#include <syslog.h>
d112e9ff 29
97d6da0b
BJ
30#define DAY (60L*60L*24L)
31#define LEEWAY (3*DAY)
32
33#define eq(a,b) (!strcmp(a,b))
261f5d78 34#ifdef vax
97d6da0b 35#define ok(number) ((number)&0x7fffffff)
261f5d78
SL
36#else
37#define ok(number) (number)
38#endif
97d6da0b 39
d112e9ff 40#define SHUTDOWNLOG "/usr/adm/shutdownlog"
d112e9ff 41
a3e4f1d7 42struct nlist current_nl[] = { /* namelist for currently running system */
d112e9ff
BJ
43#define X_DUMPDEV 0
44 { "_dumpdev" },
45#define X_DUMPLO 1
46 { "_dumplo" },
47#define X_TIME 2
48 { "_time" },
261f5d78
SL
49#define X_DUMPSIZE 3
50 { "_dumpsize" },
d112e9ff
BJ
51#define X_VERSION 4
52 { "_version" },
53#define X_PANICSTR 5
54 { "_panicstr" },
261f5d78
SL
55#define X_DUMPMAG 6
56 { "_dumpmag" },
57 { "" },
d112e9ff
BJ
58};
59
a3e4f1d7
RC
60struct nlist dump_nl[] = { /* name list for dumped system */
61 { "_dumpdev" }, /* entries MUST be the same as */
62 { "_dumplo" }, /* those in current_nl[] */
63 { "_time" },
64 { "_dumpsize" },
65 { "_version" },
66 { "_panicstr" },
67 { "_dumpmag" },
68 { "" },
69};
70
f6cc4b0d 71char *system;
97d6da0b
BJ
72char *dirname; /* directory to save dumps in */
73char *ddname; /* name of dump device */
74char *find_dev();
75dev_t dumpdev; /* dump device */
76time_t dumptime; /* time the dump was taken */
77int dumplo; /* where dump starts on dumpdev */
261f5d78
SL
78int dumpsize; /* amount of memory dumped */
79int dumpmag; /* magic number in dump */
97d6da0b
BJ
80time_t now; /* current date */
81char *path();
4d1d39b2 82char *malloc();
97d6da0b
BJ
83char *ctime();
84char vers[80];
85char core_vers[80];
86char panic_mesg[80];
87int panicstr;
97d6da0b
BJ
88off_t lseek();
89off_t Lseek();
a3e4f1d7 90int Verbose;
d112e9ff
BJ
91
92main(argc, argv)
97d6da0b
BJ
93 char **argv;
94 int argc;
d112e9ff 95{
865f9d47
SL
96 char *cp;
97
98 argc--, argv++;
99 while (argc > 0 && argv[0][0] == '-') {
100 for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
97d6da0b 101
a3e4f1d7 102 case 'v':
865f9d47 103 Verbose++;
a3e4f1d7 104 break;
865f9d47 105
a3e4f1d7 106 default:
865f9d47 107 usage:
a3e4f1d7 108 fprintf(stderr,
865f9d47 109 "usage: savecore [-v] dirname [ system ]\n");
a3e4f1d7
RC
110 exit(1);
111 }
865f9d47 112 argc--, argv++;
d112e9ff 113 }
865f9d47
SL
114 if (argc != 1 && argc != 2)
115 goto usage;
116 dirname = argv[0];
117 if (argc == 2)
118 system = argv[1];
119 openlog("savecore", LOG_ODELAY, LOG_USER);
120 if (access(dirname, W_OK) < 0) {
121 syslog(LOG_ERR, "%s: %m", dirname);
97d6da0b 122 exit(1);
d112e9ff 123 }
d112e9ff 124 read_kmem();
d112e9ff
BJ
125}
126
739955be
KM
127dump_exists()
128{
129 register int dumpfd;
130 int word;
131
865f9d47
SL
132 dumpfd = Open(ddname, O_RDONLY);
133 Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
134 Read(dumpfd, (char *)&word, sizeof (word));
739955be 135 close(dumpfd);
865f9d47 136 if (Verbose && word != dumpmag) {
f53aabcb 137 printf("dumplo = %d (%d bytes)\n", dumplo/DEV_BSIZE, dumplo);
a3e4f1d7
RC
138 printf("magic number mismatch: %x != %x\n", word, dumpmag);
139 }
261f5d78 140 return (word == dumpmag);
739955be
KM
141}
142
143clear_dump()
144{
145 register int dumpfd;
146 int zero = 0;
147
865f9d47
SL
148 dumpfd = Open(ddname, O_WRONLY);
149 Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
150 Write(dumpfd, (char *)&zero, sizeof (zero));
739955be
KM
151 close(dumpfd);
152}
153
97d6da0b
BJ
154char *
155find_dev(dev, type)
156 register dev_t dev;
157 register int type;
d112e9ff 158{
d112e9ff 159 struct stat statb;
f0326588 160 char *dp;
d112e9ff
BJ
161
162 strcpy(devname, "/dev/");
97d6da0b 163 if (stat(devname, &statb)) {
d112e9ff 164 perror(devname);
97d6da0b
BJ
165 continue;
166 }
167 if ((statb.st_mode&S_IFMT) != type)
168 continue;
169 if (dev == statb.st_rdev) {
4d1d39b2 170 dp = malloc(strlen(devname)+1);
97d6da0b 171 strcpy(dp, devname);
865f9d47 172 return (dp);
d112e9ff
BJ
173 }
174 }
865f9d47 175 syslog(LOG_ERR, "Can't find device %d/%d\n", major(dev), minor(dev));
97d6da0b
BJ
176 exit(1);
177 /*NOTREACHED*/
d112e9ff
BJ
178}
179
865f9d47
SL
180int cursyms[] =
181 { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 };
182int dumpsyms[] =
183 { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 };
d112e9ff
BJ
184read_kmem()
185{
d112e9ff 186 register char *cp;
865f9d47 187 FILE *fp;
a3e4f1d7 188 char *dump_sys;
865f9d47 189 int kmem, i;
a3e4f1d7
RC
190
191 dump_sys = system ? system : "/vmunix";
a3e4f1d7
RC
192 nlist("/vmunix", current_nl);
193 nlist(dump_sys, dump_nl);
a3e4f1d7
RC
194 /*
195 * Some names we need for the currently running system,
196 * others for the system that was running when the dump was made.
197 * The values obtained from the current system are used
198 * to look for things in /dev/kmem that cannot be found
199 * in the dump_sys namelist, but are presumed to be the same
200 * (since the disk partitions are probably the same!)
201 */
865f9d47
SL
202 for (i = 0; cursyms[i] != -1; i++)
203 if (current_nl[cursyms[i]].n_value == 0) {
204 syslog(LOG_ERR, "/vmunix: %s not in namelist",
205 current_nl[cursyms[i]].n_name);
206 exit(1);
207 }
208 for (i = 0; dumpsyms[i] != -1; i++)
209 if (dump_nl[dumpsyms[i]].n_value == 0) {
210 syslog(LOG_ERR, "%s: %s not in namelist", dump_sys,
211 dump_nl[dumpsyms[i]].n_name);
212 exit(1);
213 }
214 kmem = Open("/dev/kmem", O_RDONLY);
215 Lseek(kmem, (long)current_nl[X_DUMPDEV].n_value, L_SET);
261f5d78 216 Read(kmem, (char *)&dumpdev, sizeof (dumpdev));
865f9d47 217 Lseek(kmem, (long)current_nl[X_DUMPLO].n_value, L_SET);
261f5d78 218 Read(kmem, (char *)&dumplo, sizeof (dumplo));
865f9d47 219 Lseek(kmem, (long)current_nl[X_DUMPMAG].n_value, L_SET);
261f5d78 220 Read(kmem, (char *)&dumpmag, sizeof (dumpmag));
f53aabcb 221 dumplo *= DEV_BSIZE;
d112e9ff 222 ddname = find_dev(dumpdev, S_IFBLK);
865f9d47
SL
223 fp = fdopen(kmem, "r");
224 if (fp == NULL) {
225 syslog(LOG_ERR, "Couldn't fdopen kmem");
97d6da0b 226 exit(1);
d112e9ff 227 }
f6cc4b0d
BJ
228 if (system)
229 return;
865f9d47
SL
230 fseek(fp, (long)current_nl[X_VERSION].n_value, L_SET);
231 fgets(vers, sizeof (vers), fp);
d112e9ff 232 fclose(fp);
798fe693
SL
233}
234
5638c7c2
SL
235check_kmem()
236{
798fe693
SL
237 FILE *fp;
238 register char *cp;
239
865f9d47
SL
240 fp = fopen(ddname, "r");
241 if (fp == NULL) {
242 syslog(LOG_ERR, "%s: %m", ddname);
97d6da0b 243 exit(1);
d112e9ff 244 }
865f9d47
SL
245 fseek(fp, (off_t)(dumplo+ok(dump_nl[X_VERSION].n_value)), L_SET);
246 fgets(core_vers, sizeof (core_vers), fp);
d112e9ff 247 fclose(fp);
865f9d47 248 if (!eq(vers, core_vers) && system == 0)
261f5d78 249 fprintf(stderr,
865f9d47 250 "Warning: vmunix version mismatch:\n\t%sand\n\t%s",
261f5d78 251 vers, core_vers);
97d6da0b 252 fp = fopen(ddname, "r");
865f9d47
SL
253 fseek(fp, (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), L_SET);
254 fread((char *)&panicstr, sizeof (panicstr), 1, fp);
97d6da0b 255 if (panicstr) {
865f9d47 256 fseek(fp, dumplo + ok(panicstr), L_SET);
d112e9ff
BJ
257 cp = panic_mesg;
258 do
259 *cp = getc(fp);
260 while (*cp++);
d112e9ff 261 }
97d6da0b 262 fclose(fp);
f6cc4b0d 263}
d112e9ff 264
97d6da0b
BJ
265get_crashtime()
266{
d112e9ff 267 int dumpfd;
20f89b8d 268 time_t clobber = (time_t)0;
d112e9ff 269
865f9d47
SL
270 dumpfd = Open(ddname, O_RDONLY);
271 Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_TIME].n_value)), L_SET);
97d6da0b 272 Read(dumpfd, (char *)&dumptime, sizeof dumptime);
d112e9ff 273 close(dumpfd);
a3e4f1d7
RC
274 if (dumptime == 0) {
275 if (Verbose)
865f9d47 276 printf("Dump time not found.\n");
261f5d78 277 return (0);
a3e4f1d7 278 }
d112e9ff
BJ
279 printf("System went down at %s", ctime(&dumptime));
280 if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
865f9d47 281 printf("dump time is unreasonable\n");
261f5d78 282 return (0);
d112e9ff 283 }
261f5d78 284 return (1);
d112e9ff
BJ
285}
286
97d6da0b
BJ
287char *
288path(file)
289 char *file;
d112e9ff 290{
4d1d39b2 291 register char *cp = malloc(strlen(file) + strlen(dirname) + 2);
d112e9ff 292
97d6da0b
BJ
293 (void) strcpy(cp, dirname);
294 (void) strcat(cp, "/");
295 (void) strcat(cp, file);
261f5d78 296 return (cp);
d112e9ff
BJ
297}
298
d112e9ff
BJ
299check_space()
300{
301 struct stat dsb;
302 register char *ddev;
e84afc49 303 int dfd, spacefree;
798fe693 304 struct fs fs;
d112e9ff 305
97d6da0b 306 if (stat(dirname, &dsb) < 0) {
865f9d47 307 syslog(LOG_ERR, "%s: %m", dirname);
97d6da0b
BJ
308 exit(1);
309 }
d112e9ff 310 ddev = find_dev(dsb.st_dev, S_IFBLK);
865f9d47
SL
311 dfd = Open(ddev, O_RDONLY);
312 Lseek(dfd, (long)(SBLOCK * DEV_BSIZE), L_SET);
313 Read(dfd, (char *)&fs, sizeof (fs));
d112e9ff 314 close(dfd);
865f9d47
SL
315 spacefree = freespace(&fs, fs.fs_minfree) * fs.fs_fsize / 1024;
316 if (spacefree < read_number("minfree")) {
317 syslog(LOG_WARNING, "Dump omitted, not enough space on device");
97d6da0b 318 return (0);
d112e9ff 319 }
d819dd82 320 if (freespace(&fs, fs.fs_minfree) < 0)
865f9d47
SL
321 syslog(LOG_WARNING,
322 "Dump performed, but free space threshold crossed");
97d6da0b 323 return (1);
d112e9ff
BJ
324}
325
d112e9ff 326read_number(fn)
97d6da0b 327 char *fn;
d112e9ff
BJ
328{
329 char lin[80];
330 register FILE *fp;
331
865f9d47
SL
332 fp = fopen(path(fn), "r");
333 if (fp == NULL)
261f5d78 334 return (0);
97d6da0b
BJ
335 if (fgets(lin, 80, fp) == NULL) {
336 fclose(fp);
261f5d78 337 return (0);
d112e9ff 338 }
97d6da0b 339 fclose(fp);
261f5d78 340 return (atoi(lin));
d112e9ff
BJ
341}
342
4d1d39b2
MK
343#define BUFPAGES (256*1024/NBPG) /* 1/4 Mb */
344
d112e9ff
BJ
345save_core()
346{
347 register int n;
4d1d39b2 348 register char *cp;
d112e9ff
BJ
349 register int ifd, ofd, bounds;
350 register FILE *fp;
351
4d1d39b2 352 cp = malloc(BUFPAGES*NBPG);
865f9d47
SL
353 if (cp == 0) {
354 fprintf(stderr, "savecore: Can't allocate i/o buffer.\n");
4d1d39b2
MK
355 return;
356 }
d112e9ff 357 bounds = read_number("bounds");
865f9d47 358 ifd = Open(system?system:"/vmunix", O_RDONLY);
d112e9ff
BJ
359 while((n = Read(ifd, cp, BUFSIZ)) > 0)
360 Write(ofd, cp, n);
361 close(ifd);
362 close(ofd);
865f9d47
SL
363 ifd = Open(ddname, O_RDONLY);
364 Lseek(ifd, (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), L_SET);
5638c7c2 365 Read(ifd, (char *)&dumpsize, sizeof (dumpsize));
798fe693
SL
366 sprintf(cp, "vmcore.%d", bounds);
367 ofd = Create(path(cp), 0644);
865f9d47 368 Lseek(ifd, (off_t)dumplo, L_SET);
261f5d78
SL
369 printf("Saving %d bytes of image in vmcore.%d\n", NBPG*dumpsize,
370 bounds);
371 while (dumpsize > 0) {
4d1d39b2 372 n = Read(ifd, cp,
865f9d47 373 (dumpsize > BUFPAGES ? BUFPAGES : dumpsize) * NBPG);
f53aabcb
JB
374 if (n == 0) {
375 printf("WARNING: core may be incomplete\n");
376 break;
377 }
d112e9ff 378 Write(ofd, cp, n);
261f5d78 379 dumpsize -= n/NBPG;
d112e9ff
BJ
380 }
381 close(ifd);
382 close(ofd);
383 fp = fopen(path("bounds"), "w");
384 fprintf(fp, "%d\n", bounds+1);
385 fclose(fp);
4d1d39b2 386 free(cp);
d112e9ff
BJ
387}
388
865f9d47 389char *days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
d112e9ff
BJ
390char *months[] = {
391 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
392 "Oct", "Nov", "Dec"
393};
394
395log_entry()
396{
397 FILE *fp;
398 struct tm *tm, *localtime();
399
97d6da0b
BJ
400 fp = fopen("/usr/adm/shutdownlog", "a");
401 if (fp == 0)
402 return;
865f9d47
SL
403 tm = localtime(&now);
404 fseek(fp, 0L, L_XTND);
d112e9ff
BJ
405 fprintf(fp, "%02d:%02d %s %s %2d, %4d. Reboot", tm->tm_hour,
406 tm->tm_min, days[tm->tm_wday], months[tm->tm_mon],
407 tm->tm_mday, tm->tm_year + 1900);
408 if (panicstr)
97d6da0b 409 fprintf(fp, " after panic: %s\n", panic_mesg);
d112e9ff
BJ
410 else
411 putc('\n', fp);
412 fclose(fp);
413}
97d6da0b
BJ
414
415/*
416 * Versions of std routines that exit on error.
417 */
97d6da0b
BJ
418Open(name, rw)
419 char *name;
420 int rw;
421{
422 int fd;
423
865f9d47
SL
424 fd = open(name, rw);
425 if (fd < 0) {
426 syslog(LOG_ERR, "%s: %m", name);
97d6da0b
BJ
427 exit(1);
428 }
865f9d47 429 return (fd);
97d6da0b
BJ
430}
431
432Read(fd, buff, size)
433 int fd, size;
434 char *buff;
435{
436 int ret;
437
865f9d47
SL
438 ret = read(fd, buff, size);
439 if (ret < 0) {
440 syslog(LOG_ERR, "read: %m");
97d6da0b
BJ
441 exit(1);
442 }
865f9d47 443 return (ret);
97d6da0b
BJ
444}
445
446off_t
447Lseek(fd, off, flag)
448 int fd, flag;
449 long off;
450{
451 long ret;
452
865f9d47
SL
453 ret = lseek(fd, off, flag);
454 if (ret == -1) {
455 syslog(LOG_ERR, "lseek: %m");
97d6da0b
BJ
456 exit(1);
457 }
865f9d47 458 return (ret);
97d6da0b
BJ
459}
460
461Create(file, mode)
462 char *file;
463 int mode;
464{
465 register int fd;
466
865f9d47
SL
467 fd = creat(file, mode);
468 if (fd < 0) {
469 syslog(LOG_ERR, "%s: %m", file);
97d6da0b
BJ
470 exit(1);
471 }
865f9d47 472 return (fd);
97d6da0b
BJ
473}
474
475Write(fd, buf, size)
476 int fd, size;
477 char *buf;
97d6da0b
BJ
478{
479
480 if (write(fd, buf, size) < size) {
865f9d47 481 syslog(LOG_ERR, "write: %m");
97d6da0b
BJ
482 exit(1);
483 }
484}