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