update bounds file if write fails on vmcore
[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
bfda3826 25static char sccsid[] = "@(#)savecore.c 5.20 (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) {
bfda3826 148 Perror(LOG_ERR, "%s: %m\n", 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 160 if (Verbose && word != dumpmag)
a3e4f1d7 161 printf("magic number mismatch: %x != %x\n", word, dumpmag);
261f5d78 162 return (word == dumpmag);
739955be
KM
163}
164
165clear_dump()
166{
739955be
KM
167 int zero = 0;
168
865f9d47
SL
169 Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
170 Write(dumpfd, (char *)&zero, sizeof (zero));
739955be
KM
171}
172
97d6da0b
BJ
173char *
174find_dev(dev, type)
175 register dev_t dev;
176 register int type;
d112e9ff 177{
d112e9ff 178 struct stat statb;
f0326588 179 char *dp;
d112e9ff 180
7abf8d65 181 strcpy(devname, _PATH_DEV);
97d6da0b 182 if (stat(devname, &statb)) {
d112e9ff 183 perror(devname);
97d6da0b
BJ
184 continue;
185 }
186 if ((statb.st_mode&S_IFMT) != type)
187 continue;
188 if (dev == statb.st_rdev) {
4d1d39b2 189 dp = malloc(strlen(devname)+1);
97d6da0b 190 strcpy(dp, devname);
865f9d47 191 return (dp);
d112e9ff
BJ
192 }
193 }
ce104dfd 194 log(LOG_ERR, "Can't find device %d/%d\n", major(dev), minor(dev));
97d6da0b
BJ
195 exit(1);
196 /*NOTREACHED*/
d112e9ff
BJ
197}
198
95503932
MK
199char *
200rawname(s)
201 char *s;
202{
203 static char name[MAXPATHLEN];
204 char *sl, *rindex();
205
206 if ((sl = rindex(s, '/')) == NULL || sl[1] == '0') {
207 log(LOG_ERR, "can't make raw dump device name from %s?\n", s);
208 return (s);
209 }
210 sprintf(name, "%.*s/r%s", sl - s, s, sl + 1);
211 return (name);
212}
213
865f9d47
SL
214int cursyms[] =
215 { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 };
216int dumpsyms[] =
217 { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 };
d112e9ff
BJ
218read_kmem()
219{
d112e9ff 220 register char *cp;
865f9d47 221 FILE *fp;
a3e4f1d7 222 char *dump_sys;
865f9d47 223 int kmem, i;
a3e4f1d7 224
f76fd03c
KB
225 dump_sys = system ? system : _PATH_UNIX;
226 nlist(_PATH_UNIX, current_nl);
a3e4f1d7 227 nlist(dump_sys, dump_nl);
a3e4f1d7
RC
228 /*
229 * Some names we need for the currently running system,
230 * others for the system that was running when the dump was made.
231 * The values obtained from the current system are used
232 * to look for things in /dev/kmem that cannot be found
233 * in the dump_sys namelist, but are presumed to be the same
234 * (since the disk partitions are probably the same!)
235 */
865f9d47
SL
236 for (i = 0; cursyms[i] != -1; i++)
237 if (current_nl[cursyms[i]].n_value == 0) {
f76fd03c 238 log(LOG_ERR, "%s: %s not in namelist\n", _PATH_UNIX,
865f9d47
SL
239 current_nl[cursyms[i]].n_name);
240 exit(1);
241 }
242 for (i = 0; dumpsyms[i] != -1; i++)
243 if (dump_nl[dumpsyms[i]].n_value == 0) {
99b8650e 244 log(LOG_ERR, "%s: %s not in namelist\n", dump_sys,
865f9d47
SL
245 dump_nl[dumpsyms[i]].n_name);
246 exit(1);
247 }
f76fd03c 248 kmem = Open(_PATH_KMEM, O_RDONLY);
865f9d47 249 Lseek(kmem, (long)current_nl[X_DUMPDEV].n_value, L_SET);
261f5d78 250 Read(kmem, (char *)&dumpdev, sizeof (dumpdev));
865f9d47 251 Lseek(kmem, (long)current_nl[X_DUMPLO].n_value, L_SET);
261f5d78 252 Read(kmem, (char *)&dumplo, sizeof (dumplo));
4085e419
MK
253 if (Verbose)
254 printf("dumplo = %d (%d * %d)\n", dumplo, dumplo/DEV_BSIZE,
255 DEV_BSIZE);
865f9d47 256 Lseek(kmem, (long)current_nl[X_DUMPMAG].n_value, L_SET);
261f5d78 257 Read(kmem, (char *)&dumpmag, sizeof (dumpmag));
f53aabcb 258 dumplo *= DEV_BSIZE;
d112e9ff 259 ddname = find_dev(dumpdev, S_IFBLK);
95503932 260 dumpfd = Open(ddname, O_RDWR);
865f9d47
SL
261 fp = fdopen(kmem, "r");
262 if (fp == NULL) {
99b8650e 263 log(LOG_ERR, "Couldn't fdopen kmem\n");
97d6da0b 264 exit(1);
d112e9ff 265 }
f6cc4b0d
BJ
266 if (system)
267 return;
865f9d47
SL
268 fseek(fp, (long)current_nl[X_VERSION].n_value, L_SET);
269 fgets(vers, sizeof (vers), fp);
d112e9ff 270 fclose(fp);
798fe693
SL
271}
272
5638c7c2
SL
273check_kmem()
274{
798fe693
SL
275 FILE *fp;
276 register char *cp;
277
95503932 278 fp = fdopen(dumpfd, "r");
865f9d47 279 if (fp == NULL) {
bfda3826 280 log(LOG_ERR, "Can't fdopen dumpfd\n");
97d6da0b 281 exit(1);
d112e9ff 282 }
865f9d47
SL
283 fseek(fp, (off_t)(dumplo+ok(dump_nl[X_VERSION].n_value)), L_SET);
284 fgets(core_vers, sizeof (core_vers), fp);
99b8650e 285 if (!eq(vers, core_vers) && system == 0) {
f76fd03c 286 log(LOG_WARNING, "Warning: %s version mismatch:\n", _PATH_UNIX);
99b8650e
MK
287 log(LOG_WARNING, "\t%s\n", vers);
288 log(LOG_WARNING, "and\t%s\n", core_vers);
289 }
865f9d47
SL
290 fseek(fp, (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), L_SET);
291 fread((char *)&panicstr, sizeof (panicstr), 1, fp);
97d6da0b 292 if (panicstr) {
865f9d47 293 fseek(fp, dumplo + ok(panicstr), L_SET);
d112e9ff
BJ
294 cp = panic_mesg;
295 do
296 *cp = getc(fp);
95503932 297 while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)]);
d112e9ff 298 }
95503932 299 /* don't fclose(fp); we want the file descriptor */
f6cc4b0d 300}
d112e9ff 301
97d6da0b
BJ
302get_crashtime()
303{
20f89b8d 304 time_t clobber = (time_t)0;
d112e9ff 305
865f9d47 306 Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_TIME].n_value)), L_SET);
97d6da0b 307 Read(dumpfd, (char *)&dumptime, sizeof dumptime);
a3e4f1d7
RC
308 if (dumptime == 0) {
309 if (Verbose)
c6e3f9fb 310 printf("Dump time is zero.\n");
261f5d78 311 return (0);
a3e4f1d7 312 }
d112e9ff
BJ
313 printf("System went down at %s", ctime(&dumptime));
314 if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
865f9d47 315 printf("dump time is unreasonable\n");
261f5d78 316 return (0);
d112e9ff 317 }
261f5d78 318 return (1);
d112e9ff
BJ
319}
320
97d6da0b
BJ
321char *
322path(file)
323 char *file;
d112e9ff 324{
4d1d39b2 325 register char *cp = malloc(strlen(file) + strlen(dirname) + 2);
d112e9ff 326
97d6da0b
BJ
327 (void) strcpy(cp, dirname);
328 (void) strcat(cp, "/");
329 (void) strcat(cp, file);
261f5d78 330 return (cp);
d112e9ff
BJ
331}
332
d112e9ff
BJ
333check_space()
334{
335 struct stat dsb;
336 register char *ddev;
e84afc49 337 int dfd, spacefree;
798fe693 338 struct fs fs;
d112e9ff 339
97d6da0b 340 if (stat(dirname, &dsb) < 0) {
bfda3826 341 Perror(LOG_ERR, "%s: %m\n", dirname);
97d6da0b
BJ
342 exit(1);
343 }
d112e9ff 344 ddev = find_dev(dsb.st_dev, S_IFBLK);
865f9d47 345 dfd = Open(ddev, O_RDONLY);
a66ab591 346 Lseek(dfd, SBOFF, L_SET);
865f9d47 347 Read(dfd, (char *)&fs, sizeof (fs));
d112e9ff 348 close(dfd);
865f9d47
SL
349 spacefree = freespace(&fs, fs.fs_minfree) * fs.fs_fsize / 1024;
350 if (spacefree < read_number("minfree")) {
99b8650e 351 log(LOG_WARNING, "Dump omitted, not enough space on device\n");
97d6da0b 352 return (0);
d112e9ff 353 }
ce104dfd
SL
354 if (freespace(&fs, fs.fs_minfree) < 0)
355 log(LOG_WARNING,
99b8650e 356 "Dump performed, but free space threshold crossed\n");
97d6da0b 357 return (1);
d112e9ff
BJ
358}
359
d112e9ff 360read_number(fn)
97d6da0b 361 char *fn;
d112e9ff
BJ
362{
363 char lin[80];
364 register FILE *fp;
365
865f9d47
SL
366 fp = fopen(path(fn), "r");
367 if (fp == NULL)
261f5d78 368 return (0);
97d6da0b
BJ
369 if (fgets(lin, 80, fp) == NULL) {
370 fclose(fp);
261f5d78 371 return (0);
d112e9ff 372 }
97d6da0b 373 fclose(fp);
261f5d78 374 return (atoi(lin));
d112e9ff
BJ
375}
376
4085e419 377#define BUFSIZE (256*1024) /* 1/4 Mb */
4d1d39b2 378
d112e9ff
BJ
379save_core()
380{
381 register int n;
4d1d39b2 382 register char *cp;
d112e9ff 383 register int ifd, ofd, bounds;
bfda3826 384 int ret;
95503932 385 char *bfile;
d112e9ff
BJ
386 register FILE *fp;
387
4085e419 388 cp = malloc(BUFSIZE);
865f9d47 389 if (cp == 0) {
95503932 390 log(LOG_ERR, "savecore: Can't allocate i/o buffer.\n");
4d1d39b2
MK
391 return;
392 }
d112e9ff 393 bounds = read_number("bounds");
f76fd03c 394 ifd = Open(system ? system : _PATH_UNIX, O_RDONLY);
4085e419 395 while((n = Read(ifd, cp, BUFSIZE)) > 0)
d112e9ff
BJ
396 Write(ofd, cp, n);
397 close(ifd);
398 close(ofd);
95503932
MK
399 if ((ifd = open(rawname(ddname), O_RDONLY)) == -1) {
400 log(LOG_WARNING, "Can't open %s (%m); using block device",
401 rawname(ddname));
402 ifd = dumpfd;
403 }
4085e419
MK
404 Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), L_SET);
405 Read(dumpfd, (char *)&dumpsize, sizeof (dumpsize));
9bd38ba8 406 (void)sprintf(cp, "vmcore.%d", bounds);
798fe693 407 ofd = Create(path(cp), 0644);
865f9d47 408 Lseek(ifd, (off_t)dumplo, L_SET);
4085e419 409 dumpsize *= NBPG;
ce104dfd 410 log(LOG_NOTICE, "Saving %d bytes of image in vmcore.%d\n",
4085e419 411 dumpsize, bounds);
261f5d78 412 while (dumpsize > 0) {
4085e419
MK
413 n = read(ifd, cp,
414 dumpsize > BUFSIZE ? BUFSIZE : dumpsize);
415 if (n <= 0) {
416 if (n == 0)
417 log(LOG_WARNING,
bfda3826
MK
418 "WARNING: EOF on dump device; %s\n",
419 "vmcore may be incomplete");
4085e419 420 else
bfda3826
MK
421 Perror(LOG_ERR, "read from dumpdev: %m",
422 "read");
423 break;
424 }
425 if ((ret = write(ofd, cp, n)) < n) {
426 if (ret < 0)
427 Perror(LOG_ERR, "write: %m", "write");
428 else
429 log(LOG_ERR, "short write: wrote %d of %d\n",
430 ret, n);
431 log(LOG_WARNING, "WARNING: vmcore may be incomplete\n");
f53aabcb
JB
432 break;
433 }
4085e419 434 dumpsize -= n;
d112e9ff
BJ
435 }
436 close(ifd);
437 close(ofd);
95503932
MK
438 bfile = path("bounds");
439 fp = fopen(bfile, "w");
440 if (fp) {
441 fprintf(fp, "%d\n", bounds+1);
442 fclose(fp);
443 } else
444 Perror(LOG_ERR, "Can't create bounds file %s: %m", bfile);
4d1d39b2 445 free(cp);
d112e9ff
BJ
446}
447
97d6da0b
BJ
448/*
449 * Versions of std routines that exit on error.
450 */
97d6da0b
BJ
451Open(name, rw)
452 char *name;
453 int rw;
454{
455 int fd;
456
865f9d47
SL
457 fd = open(name, rw);
458 if (fd < 0) {
ce104dfd 459 Perror(LOG_ERR, "%s: %m", name);
97d6da0b
BJ
460 exit(1);
461 }
865f9d47 462 return (fd);
97d6da0b
BJ
463}
464
465Read(fd, buff, size)
466 int fd, size;
467 char *buff;
468{
469 int ret;
470
865f9d47
SL
471 ret = read(fd, buff, size);
472 if (ret < 0) {
95503932 473 Perror(LOG_ERR, "read: %m", "read");
97d6da0b
BJ
474 exit(1);
475 }
865f9d47 476 return (ret);
97d6da0b
BJ
477}
478
479off_t
480Lseek(fd, off, flag)
481 int fd, flag;
482 long off;
483{
484 long ret;
485
865f9d47
SL
486 ret = lseek(fd, off, flag);
487 if (ret == -1) {
95503932 488 Perror(LOG_ERR, "lseek: %m", "lseek");
97d6da0b
BJ
489 exit(1);
490 }
865f9d47 491 return (ret);
97d6da0b
BJ
492}
493
494Create(file, mode)
495 char *file;
496 int mode;
497{
498 register int fd;
499
865f9d47
SL
500 fd = creat(file, mode);
501 if (fd < 0) {
ce104dfd 502 Perror(LOG_ERR, "%s: %m", file);
97d6da0b
BJ
503 exit(1);
504 }
865f9d47 505 return (fd);
97d6da0b
BJ
506}
507
508Write(fd, buf, size)
509 int fd, size;
510 char *buf;
97d6da0b 511{
bfda3826 512 int n;
97d6da0b 513
bfda3826
MK
514 if ((n = write(fd, buf, size)) < size) {
515 if (n < 0)
516 Perror(LOG_ERR, "write: %m", "write");
517 else
518 log(LOG_ERR, "short write: wrote %d of %d\n", n, size);
97d6da0b
BJ
519 exit(1);
520 }
521}
ce104dfd 522
95503932 523/* VARARGS2 */
ce104dfd
SL
524log(level, msg, a1, a2)
525 int level;
526 char *msg;
527{
528
529 fprintf(stderr, msg, a1, a2);
530 syslog(level, msg, a1, a2);
531}
532
533Perror(level, msg, s)
534 int level;
535 char *msg;
536{
537 int oerrno = errno;
538
539 perror(s);
540 errno = oerrno;
541 syslog(level, msg, s);
542}