1 /* 2 * Copyright (c) 1980 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 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.2 (Berkeley) 09/08/85"; 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 29 #define DAY (60L*60L*24L) 30 #define LEEWAY (3*DAY) 31 32 #define eq(a,b) (!strcmp(a,b)) 33 #ifdef vax 34 #define ok(number) ((number)&0x7fffffff) 35 #else 36 #define ok(number) (number) 37 #endif 38 39 #define SHUTDOWNLOG "/usr/adm/shutdownlog" 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 unsigned 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 91 main(argc, argv) 92 char **argv; 93 int argc; 94 { 95 96 while ((argc > 1) && (argv[1][0] == '-')) { 97 switch (argv[1][1]) { 98 case 'v': 99 case 'd': 100 Verbose = 1; 101 break; 102 default: 103 fprintf(stderr, "savecore: illegal flag -%c\n", 104 argv[1][1]); 105 fprintf(stderr, 106 "usage: savecore [-v] dirname [ system ]\n"); 107 exit(1); 108 } 109 argc--; 110 argv++; 111 } 112 113 if (argc != 2 && argc != 3) { 114 fprintf(stderr, "usage: savecore [-v] dirname [ system ]\n"); 115 exit(1); 116 } 117 dirname = argv[1]; 118 if (argc == 3) 119 system = argv[2]; 120 if (access(dirname, 2) < 0) { 121 perror(dirname); 122 exit(1); 123 } 124 read_kmem(); 125 if (dump_exists()) { 126 (void) time(&now); 127 check_kmem(); 128 log_entry(); 129 if (get_crashtime() && check_space()) { 130 save_core(); 131 clear_dump(); 132 } else { 133 if (Verbose) 134 fprintf(stderr, "No space or time\n"); 135 exit(1); 136 } 137 } 138 else if (Verbose) { 139 fprintf(stderr, "No dump exists\n"); 140 } 141 return 0; 142 } 143 144 int 145 dump_exists() 146 { 147 register int dumpfd; 148 int word; 149 150 dumpfd = Open(ddname, 0); 151 Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), 0); 152 Read(dumpfd, (char *)&word, sizeof word); 153 close(dumpfd); 154 if (Verbose && (word != dumpmag)) { 155 printf("dumplo = %d (%d bytes)\n", dumplo/DEV_BSIZE, dumplo); 156 printf("magic number mismatch: %x != %x\n", word, dumpmag); 157 } 158 return (word == dumpmag); 159 } 160 161 clear_dump() 162 { 163 register int dumpfd; 164 int zero = 0; 165 166 dumpfd = Open(ddname, 1); 167 Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), 0); 168 Write(dumpfd, (char *)&zero, sizeof zero); 169 close(dumpfd); 170 } 171 172 char * 173 find_dev(dev, type) 174 register dev_t dev; 175 register int type; 176 { 177 register DIR *dfd = opendir("/dev"); 178 struct direct *dir; 179 struct stat statb; 180 static char devname[MAXPATHLEN + 1]; 181 char *dp; 182 183 strcpy(devname, "/dev/"); 184 while ((dir = readdir(dfd))) { 185 strcpy(devname + 5, dir->d_name); 186 if (stat(devname, &statb)) { 187 perror(devname); 188 continue; 189 } 190 if ((statb.st_mode&S_IFMT) != type) 191 continue; 192 if (dev == statb.st_rdev) { 193 closedir(dfd); 194 dp = (char *)malloc(strlen(devname)+1); 195 strcpy(dp, devname); 196 return dp; 197 } 198 } 199 closedir(dfd); 200 fprintf(stderr, "savecore: Can't find device %d,%d\n", 201 major(dev), minor(dev)); 202 exit(1); 203 /*NOTREACHED*/ 204 } 205 206 read_kmem() 207 { 208 int kmem; 209 FILE *fp; 210 register char *cp; 211 char *dump_sys; 212 213 dump_sys = system ? system : "/vmunix"; 214 215 nlist("/vmunix", current_nl); 216 nlist(dump_sys, dump_nl); 217 218 /* 219 * Some names we need for the currently running system, 220 * others for the system that was running when the dump was made. 221 * The values obtained from the current system are used 222 * to look for things in /dev/kmem that cannot be found 223 * in the dump_sys namelist, but are presumed to be the same 224 * (since the disk partitions are probably the same!) 225 */ 226 if (current_nl[X_DUMPDEV].n_value == 0) { 227 fprintf(stderr, "savecore: /vmunix: dumpdev not in namelist\n"); 228 exit(1); 229 } 230 if (current_nl[X_DUMPLO].n_value == 0) { 231 fprintf(stderr, "savecore: /vmunix: dumplo not in namelist\n"); 232 exit(1); 233 } 234 if (dump_nl[X_TIME].n_value == 0) { 235 fprintf(stderr, "savecore: %s: time not in namelist\n", 236 dump_sys); 237 exit(1); 238 } 239 if (dump_nl[X_DUMPSIZE].n_value == 0) { 240 fprintf(stderr, "savecore: %s: dumpsize not in namelist\n", 241 dump_sys); 242 exit(1); 243 } 244 /* we need VERSION in both images */ 245 if (current_nl[X_VERSION].n_value == 0) { 246 fprintf(stderr, "savecore: /vmunix: version not in namelist\n", 247 dump_sys); 248 exit(1); 249 } 250 if (dump_nl[X_VERSION].n_value == 0) { 251 fprintf(stderr, "savecore: %s: version not in namelist\n", 252 dump_sys); 253 exit(1); 254 } 255 if (dump_nl[X_PANICSTR].n_value == 0) { 256 fprintf(stderr, "savecore: %s: panicstr not in namelist\n", 257 dump_sys); 258 exit(1); 259 } 260 /* we need DUMPMAG in both images */ 261 if (current_nl[X_DUMPMAG].n_value == 0) { 262 fprintf(stderr, "savecore: /vmunix: dumpmag not in namelist\n"); 263 exit(1); 264 } 265 if (dump_nl[X_DUMPMAG].n_value == 0) { 266 fprintf(stderr, "savecore: %s: dumpmag not in namelist\n", 267 dump_sys); 268 exit(1); 269 } 270 kmem = Open("/dev/kmem", 0); 271 Lseek(kmem, (long)current_nl[X_DUMPDEV].n_value, 0); 272 Read(kmem, (char *)&dumpdev, sizeof (dumpdev)); 273 Lseek(kmem, (long)current_nl[X_DUMPLO].n_value, 0); 274 Read(kmem, (char *)&dumplo, sizeof (dumplo)); 275 Lseek(kmem, (long)current_nl[X_DUMPMAG].n_value, 0); 276 Read(kmem, (char *)&dumpmag, sizeof (dumpmag)); 277 dumplo *= DEV_BSIZE; 278 ddname = find_dev(dumpdev, S_IFBLK); 279 if ((fp = fdopen(kmem, "r")) == NULL) { 280 fprintf(stderr, "savecore: Couldn't fdopen kmem\n"); 281 exit(1); 282 } 283 if (system) 284 return; 285 fseek(fp, (long)current_nl[X_VERSION].n_value, 0); 286 fgets(vers, sizeof vers, fp); 287 fclose(fp); 288 } 289 290 check_kmem() 291 { 292 FILE *fp; 293 register char *cp; 294 295 if ((fp = fopen(ddname, "r")) == NULL) { 296 perror(ddname); 297 exit(1); 298 } 299 fseek(fp, (off_t)(dumplo+ok(dump_nl[X_VERSION].n_value)), 0); 300 fgets(core_vers, sizeof core_vers, fp); 301 fclose(fp); 302 if (!eq(vers, core_vers) && (system == 0)) 303 fprintf(stderr, 304 "savecore: Warning: vmunix version mismatch:\n\t%sand\n\t%s", 305 vers, core_vers); 306 fp = fopen(ddname, "r"); 307 fseek(fp, (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), 0); 308 fread((char *)&panicstr, sizeof panicstr, 1, fp); 309 if (panicstr) { 310 fseek(fp, dumplo + ok(panicstr), 0); 311 cp = panic_mesg; 312 do 313 *cp = getc(fp); 314 while (*cp++); 315 } 316 fclose(fp); 317 } 318 319 get_crashtime() 320 { 321 int dumpfd; 322 time_t clobber = (time_t)0; 323 324 dumpfd = Open(ddname, 0); 325 Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_TIME].n_value)), 0); 326 Read(dumpfd, (char *)&dumptime, sizeof dumptime); 327 close(dumpfd); 328 if (dumptime == 0) { 329 if (Verbose) 330 printf("dump time not found\n"); 331 return (0); 332 } 333 printf("System went down at %s", ctime(&dumptime)); 334 if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) { 335 printf("Dump time is unreasonable\n"); 336 return (0); 337 } 338 return (1); 339 } 340 341 char * 342 path(file) 343 char *file; 344 { 345 register char *cp = (char *)malloc(strlen(file) + strlen(dirname) + 2); 346 347 (void) strcpy(cp, dirname); 348 (void) strcat(cp, "/"); 349 (void) strcat(cp, file); 350 return (cp); 351 } 352 353 check_space() 354 { 355 struct stat dsb; 356 register char *ddev; 357 int dfd, spacefree; 358 struct fs fs; 359 360 if (stat(dirname, &dsb) < 0) { 361 perror(dirname); 362 exit(1); 363 } 364 ddev = find_dev(dsb.st_dev, S_IFBLK); 365 dfd = Open(ddev, 0); 366 Lseek(dfd, (long)(SBLOCK * DEV_BSIZE), 0); 367 Read(dfd, (char *)&fs, sizeof fs); 368 close(dfd); 369 spacefree = fs.fs_cstotal.cs_nbfree * fs.fs_bsize / 1024; 370 if (read_number("minfree") > spacefree) { 371 fprintf(stderr, 372 "savecore: Dump omitted, not enough space on device\n"); 373 return (0); 374 } 375 if (fs.fs_cstotal.cs_nbfree * fs.fs_frag + fs.fs_cstotal.cs_nffree < 376 fs.fs_dsize * fs.fs_minfree / 100) 377 fprintf(stderr, 378 "Dump performed, but free space threshold crossed\n"); 379 return (1); 380 } 381 382 read_number(fn) 383 char *fn; 384 { 385 char lin[80]; 386 register FILE *fp; 387 388 if ((fp = fopen(path(fn), "r")) == NULL) 389 return (0); 390 if (fgets(lin, 80, fp) == NULL) { 391 fclose(fp); 392 return (0); 393 } 394 fclose(fp); 395 return (atoi(lin)); 396 } 397 398 save_core() 399 { 400 register int n; 401 char buffer[32*NBPG]; 402 register char *cp = buffer; 403 register int ifd, ofd, bounds; 404 register FILE *fp; 405 406 bounds = read_number("bounds"); 407 ifd = Open(system?system:"/vmunix", 0); 408 sprintf(cp, "vmunix.%d", bounds); 409 ofd = Create(path(cp), 0644); 410 while((n = Read(ifd, cp, BUFSIZ)) > 0) 411 Write(ofd, cp, n); 412 close(ifd); 413 close(ofd); 414 ifd = Open(ddname, 0); 415 Lseek(ifd, (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), 0); 416 Read(ifd, (char *)&dumpsize, sizeof (dumpsize)); 417 sprintf(cp, "vmcore.%d", bounds); 418 ofd = Create(path(cp), 0644); 419 Lseek(ifd, (off_t)dumplo, 0); 420 printf("Saving %d bytes of image in vmcore.%d\n", NBPG*dumpsize, 421 bounds); 422 while (dumpsize > 0) { 423 n = Read(ifd, cp, (dumpsize > 32 ? 32 : dumpsize) * NBPG); 424 if (n == 0) { 425 printf("WARNING: core may be incomplete\n"); 426 break; 427 } 428 Write(ofd, cp, n); 429 dumpsize -= n/NBPG; 430 } 431 close(ifd); 432 close(ofd); 433 fp = fopen(path("bounds"), "w"); 434 fprintf(fp, "%d\n", bounds+1); 435 fclose(fp); 436 } 437 438 char *days[] = { 439 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 440 }; 441 442 char *months[] = { 443 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", 444 "Oct", "Nov", "Dec" 445 }; 446 447 log_entry() 448 { 449 FILE *fp; 450 struct tm *tm, *localtime(); 451 452 tm = localtime(&now); 453 fp = fopen("/usr/adm/shutdownlog", "a"); 454 if (fp == 0) 455 return; 456 fseek(fp, 0L, 2); 457 fprintf(fp, "%02d:%02d %s %s %2d, %4d. Reboot", tm->tm_hour, 458 tm->tm_min, days[tm->tm_wday], months[tm->tm_mon], 459 tm->tm_mday, tm->tm_year + 1900); 460 if (panicstr) 461 fprintf(fp, " after panic: %s\n", panic_mesg); 462 else 463 putc('\n', fp); 464 fclose(fp); 465 } 466 467 /* 468 * Versions of std routines that exit on error. 469 */ 470 471 Open(name, rw) 472 char *name; 473 int rw; 474 { 475 int fd; 476 477 if ((fd = open(name, rw)) < 0) { 478 perror(name); 479 exit(1); 480 } 481 return fd; 482 } 483 484 Read(fd, buff, size) 485 int fd, size; 486 char *buff; 487 { 488 int ret; 489 490 if ((ret = read(fd, buff, size)) < 0) { 491 perror("read"); 492 exit(1); 493 } 494 return ret; 495 } 496 497 off_t 498 Lseek(fd, off, flag) 499 int fd, flag; 500 long off; 501 { 502 long ret; 503 504 if ((ret = lseek(fd, off, flag)) == -1L) { 505 perror("lseek"); 506 exit(1); 507 } 508 return ret; 509 } 510 511 Create(file, mode) 512 char *file; 513 int mode; 514 { 515 register int fd; 516 517 if ((fd = creat(file, mode)) < 0) { 518 perror(file); 519 exit(1); 520 } 521 return fd; 522 } 523 524 Write(fd, buf, size) 525 int fd, size; 526 char *buf; 527 { 528 529 if (write(fd, buf, size) < size) { 530 perror("write"); 531 exit(1); 532 } 533 } 534