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