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