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