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