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