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