1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1998-2002 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <stdarg.h> 32 #include <unistd.h> 33 #include <fcntl.h> 34 #include <errno.h> 35 #include <string.h> 36 #include <deflt.h> 37 #include <time.h> 38 #include <syslog.h> 39 #include <stropts.h> 40 #include <sys/mem.h> 41 #include <sys/statvfs.h> 42 #include <sys/dumphdr.h> 43 #include <sys/dumpadm.h> 44 #include <sys/compress.h> 45 #include <sys/sysmacros.h> 46 47 static char progname[9] = "savecore"; 48 static char *savedir; /* savecore directory */ 49 static char *dumpfile; /* source of raw crash dump */ 50 static long pagesize; /* dump pagesize */ 51 static int dumpfd = -1; /* dumpfile descriptor */ 52 static dumphdr_t corehdr, dumphdr; /* initial and terminal dumphdrs */ 53 static offset_t endoff; /* offset of end-of-dump header */ 54 static int verbose; /* chatty mode */ 55 static int disregard_valid_flag; /* disregard valid flag */ 56 static int livedump; /* dump the current running system */ 57 58 static void 59 usage(void) 60 { 61 (void) fprintf(stderr, 62 "usage: %s [-Lvd] [-f dumpfile] [dirname]\n", progname); 63 exit(1); 64 } 65 66 static void 67 logprint(int logpri, int showmsg, int exitcode, char *message, ...) 68 { 69 va_list args; 70 char buf[1024]; 71 72 if (showmsg) { 73 va_start(args, message); 74 (void) vsnprintf(buf, 1024, message, args); 75 (void) fprintf(stderr, "%s: %s\n", progname, buf); 76 if (logpri >= 0) 77 syslog(logpri, buf); 78 va_end(args); 79 } 80 if (exitcode >= 0) 81 exit(exitcode); 82 } 83 84 /* 85 * System call / libc wrappers that exit on error. 86 */ 87 static int 88 Open(const char *name, int oflags, mode_t mode) 89 { 90 int fd; 91 92 if ((fd = open64(name, oflags, mode)) == -1) 93 logprint(LOG_ERR, 1, 1, "open(\"%s\"): %s", 94 name, strerror(errno)); 95 return (fd); 96 } 97 98 static void 99 Pread(int fd, void *buf, size_t size, offset_t off) 100 { 101 if (pread64(fd, buf, size, off) != size) 102 logprint(LOG_ERR, 1, 1, "pread: %s", strerror(errno)); 103 } 104 105 static void 106 Pwrite(int fd, void *buf, size_t size, offset_t off) 107 { 108 if (pwrite64(fd, buf, size, off) != size) 109 logprint(LOG_ERR, 1, 1, "pwrite: %s", strerror(errno)); 110 } 111 112 static void * 113 Zalloc(size_t size) 114 { 115 void *buf; 116 117 if ((buf = calloc(size, 1)) == NULL) 118 logprint(LOG_ERR, 1, 1, "calloc: %s", strerror(errno)); 119 return (buf); 120 } 121 122 static long 123 read_number_from_file(const char *filename, long default_value) 124 { 125 long file_value = -1; 126 FILE *fp; 127 128 if ((fp = fopen(filename, "r")) != NULL) { 129 (void) fscanf(fp, "%ld", &file_value); 130 (void) fclose(fp); 131 } 132 return (file_value < 0 ? default_value : file_value); 133 } 134 135 static void 136 read_dumphdr(void) 137 { 138 dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644); 139 endoff = llseek(dumpfd, -DUMP_OFFSET, SEEK_END) & -DUMP_OFFSET; 140 Pread(dumpfd, &dumphdr, sizeof (dumphdr), endoff); 141 142 pagesize = dumphdr.dump_pagesize; 143 144 if ((dumphdr.dump_flags & DF_VALID) == 0 && !disregard_valid_flag) 145 logprint(-1, verbose, 0, "dump already processed"); 146 147 if (dumphdr.dump_magic != DUMP_MAGIC) 148 logprint(-1, verbose, 0, "bad magic number %x", 149 dumphdr.dump_magic); 150 151 if (dumphdr.dump_version != DUMP_VERSION) 152 logprint(-1, verbose, 0, 153 "dump version (%d) != %s version (%d)", 154 dumphdr.dump_version, progname, DUMP_VERSION); 155 156 if (dumphdr.dump_wordsize != DUMP_WORDSIZE) 157 logprint(LOG_WARNING, 1, 0, 158 "dump is from %u-bit kernel - cannot save on %u-bit kernel", 159 dumphdr.dump_wordsize, DUMP_WORDSIZE); 160 /* 161 * Read the initial header, clear the valid bits, and compare headers. 162 * The main header may have been overwritten by swapping if we're 163 * using a swap partition as the dump device, in which case we bail. 164 */ 165 Pread(dumpfd, &corehdr, sizeof (dumphdr_t), dumphdr.dump_start); 166 167 corehdr.dump_flags &= ~DF_VALID; 168 dumphdr.dump_flags &= ~DF_VALID; 169 170 if (memcmp(&corehdr, &dumphdr, sizeof (dumphdr_t)) != 0) { 171 /* 172 * Clear valid bit so we don't complain on every invocation. 173 */ 174 Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff); 175 logprint(LOG_ERR, 1, 1, "initial dump header corrupt"); 176 } 177 } 178 179 static void 180 check_space(void) 181 { 182 struct statvfs fsb; 183 int64_t spacefree, dumpsize, minfree; 184 185 if (statvfs(".", &fsb) < 0) 186 logprint(LOG_ERR, 1, 1, "statvfs: %s", strerror(errno)); 187 188 dumpsize = (dumphdr.dump_data - dumphdr.dump_start) + 189 (int64_t)dumphdr.dump_npages * pagesize; 190 spacefree = (int64_t)fsb.f_bavail * fsb.f_frsize; 191 minfree = 1024LL * read_number_from_file("minfree", 1024); 192 if (spacefree < minfree + dumpsize) 193 logprint(LOG_ERR, 1, 1, 194 "not enough space in %s (%lld MB avail, %lld MB needed)", 195 savedir, spacefree >> 20, (minfree + dumpsize) >> 20); 196 } 197 198 static void 199 build_dump_map(int corefd, const pfn_t *pfn_table) 200 { 201 long i; 202 static long misses = 0; 203 size_t dump_mapsize = (corehdr.dump_hashmask + 1) * sizeof (dump_map_t); 204 mem_vtop_t vtop; 205 dump_map_t *dmp = Zalloc(dump_mapsize); 206 207 corehdr.dump_data = corehdr.dump_map + roundup(dump_mapsize, pagesize); 208 209 for (i = 0; i < corehdr.dump_nvtop; i++) { 210 long first = 0; 211 long last = corehdr.dump_npages - 1; 212 long middle; 213 pfn_t pfn; 214 uintptr_t h; 215 216 Pread(dumpfd, &vtop, sizeof (mem_vtop_t), 217 dumphdr.dump_map + i * sizeof (mem_vtop_t)); 218 219 while (last >= first) { 220 middle = (first + last) / 2; 221 pfn = pfn_table[middle]; 222 if (pfn == vtop.m_pfn) 223 break; 224 if (pfn < vtop.m_pfn) 225 first = middle + 1; 226 else 227 last = middle - 1; 228 } 229 if (pfn != vtop.m_pfn) { 230 if (++misses <= 10) 231 (void) fprintf(stderr, 232 "pfn %ld not found for as=%p, va=%p\n", 233 vtop.m_pfn, (void *)vtop.m_as, vtop.m_va); 234 continue; 235 } 236 237 dmp[i].dm_as = vtop.m_as; 238 dmp[i].dm_va = (uintptr_t)vtop.m_va; 239 dmp[i].dm_data = corehdr.dump_data + 240 ((uint64_t)middle << corehdr.dump_pageshift); 241 242 h = DUMP_HASH(&corehdr, dmp[i].dm_as, dmp[i].dm_va); 243 dmp[i].dm_next = dmp[h].dm_first; 244 dmp[h].dm_first = corehdr.dump_map + i * sizeof (dump_map_t); 245 } 246 247 Pwrite(corefd, dmp, dump_mapsize, corehdr.dump_map); 248 free(dmp); 249 } 250 251 static void 252 build_corefile(const char *namelist, const char *corefile) 253 { 254 char *inbuf = Zalloc(pagesize); 255 char *outbuf = Zalloc(pagesize); 256 size_t pfn_table_size = dumphdr.dump_npages * sizeof (pfn_t); 257 size_t ksyms_size = dumphdr.dump_ksyms_size; 258 size_t ksyms_csize = dumphdr.dump_ksyms_csize; 259 pfn_t *pfn_table = Zalloc(pfn_table_size); 260 char *ksyms_base = Zalloc(ksyms_size); 261 char *ksyms_cbase = Zalloc(ksyms_csize); 262 int corefd = Open(corefile, O_WRONLY | O_CREAT | O_TRUNC, 0644); 263 int namefd = Open(namelist, O_WRONLY | O_CREAT | O_TRUNC, 0644); 264 offset_t dumpoff; 265 int percent_done = 0; 266 pgcnt_t saved = 0; 267 uint32_t csize; 268 size_t dsize; 269 270 (void) printf("Constructing namelist %s/%s\n", savedir, namelist); 271 272 /* 273 * Read in the compressed symbol table, copy it to corefile, 274 * decompress it, and write the result to namelist. 275 */ 276 corehdr.dump_ksyms = pagesize; 277 Pread(dumpfd, ksyms_cbase, ksyms_csize, dumphdr.dump_ksyms); 278 Pwrite(corefd, ksyms_cbase, ksyms_csize, corehdr.dump_ksyms); 279 280 if ((dsize = decompress(ksyms_cbase, ksyms_base, ksyms_csize, 281 ksyms_size)) != ksyms_size) 282 logprint(LOG_WARNING, 1, -1, "bad data in symbol table, %lu of %lu" 283 " bytes saved", dsize, ksyms_size); 284 285 Pwrite(namefd, ksyms_base, ksyms_size, 0); 286 (void) close(namefd); 287 free(ksyms_cbase); 288 free(ksyms_base); 289 290 (void) printf("Constructing corefile %s/%s\n", savedir, corefile); 291 292 /* 293 * Read in and write out the pfn table. 294 */ 295 corehdr.dump_pfn = corehdr.dump_ksyms + roundup(ksyms_size, pagesize); 296 Pread(dumpfd, pfn_table, pfn_table_size, dumphdr.dump_pfn); 297 Pwrite(corefd, pfn_table, pfn_table_size, corehdr.dump_pfn); 298 299 /* 300 * Convert the raw translation data into a hashed dump map. 301 */ 302 corehdr.dump_map = corehdr.dump_pfn + roundup(pfn_table_size, pagesize); 303 build_dump_map(corefd, pfn_table); 304 305 /* 306 * Decompress and save the pages. 307 */ 308 dumpoff = dumphdr.dump_data; 309 while (saved < dumphdr.dump_npages) { 310 Pread(dumpfd, &csize, sizeof (uint32_t), dumpoff); 311 dumpoff += sizeof (uint32_t); 312 if (csize > pagesize) 313 break; 314 Pread(dumpfd, inbuf, csize, dumpoff); 315 dumpoff += csize; 316 if (decompress(inbuf, outbuf, csize, pagesize) != pagesize) 317 break; 318 Pwrite(corefd, outbuf, pagesize, 319 corehdr.dump_data + saved * pagesize); 320 if (++saved * 100LL / dumphdr.dump_npages > percent_done) { 321 (void) printf("\r%3d%% done", ++percent_done); 322 (void) fflush(stdout); 323 } 324 } 325 326 (void) printf(": %ld of %ld pages saved\n", saved, dumphdr.dump_npages); 327 328 if (saved != dumphdr.dump_npages) 329 logprint(LOG_WARNING, 1, -1, "bad data after page %ld", saved); 330 331 /* 332 * Write out the modified dump headers. 333 */ 334 Pwrite(corefd, &corehdr, sizeof (corehdr), 0); 335 Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff); 336 337 (void) close(corefd); 338 (void) close(dumpfd); 339 } 340 341 /* 342 * When the system panics, the kernel saves all undelivered messages (messages 343 * that never made it out to syslogd(1M)) in the dump. At a mimimum, the 344 * panic message itself will always fall into this category. Upon reboot, 345 * the syslog startup script runs savecore -m to recover these messages. 346 * 347 * To do this, we read the unsent messages from the dump and send them to 348 * /dev/conslog on priority band 1. This has the effect of prepending them 349 * to any already-accumulated messages in the console backlog, thus preserving 350 * temporal ordering across the reboot. 351 * 352 * Note: since savecore -m is used *only* for this purpose, it does *not* 353 * attempt to save the crash dump. The dump will be saved later, after 354 * syslogd(1M) starts, by the savecore startup script. 355 */ 356 static int 357 message_save(void) 358 { 359 offset_t dumpoff = -(DUMP_OFFSET + DUMP_LOGSIZE); 360 offset_t ldoff; 361 log_dump_t ld; 362 log_ctl_t lc; 363 struct strbuf ctl, dat; 364 int logfd; 365 366 logfd = Open("/dev/conslog", O_WRONLY, 0644); 367 dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644); 368 dumpoff = llseek(dumpfd, dumpoff, SEEK_END) & -DUMP_OFFSET; 369 370 ctl.buf = (void *)&lc; 371 ctl.len = sizeof (log_ctl_t); 372 373 dat.buf = Zalloc(DUMP_LOGSIZE); 374 375 for (;;) { 376 ldoff = dumpoff; 377 378 Pread(dumpfd, &ld, sizeof (log_dump_t), dumpoff); 379 dumpoff += sizeof (log_dump_t); 380 dat.len = ld.ld_msgsize; 381 382 if (ld.ld_magic == 0) 383 break; 384 385 if (ld.ld_magic != LOG_MAGIC) 386 logprint(-1, verbose, 0, "bad magic %x", ld.ld_magic); 387 388 if (dat.len >= DUMP_LOGSIZE) 389 logprint(-1, verbose, 0, "bad size %d", ld.ld_msgsize); 390 391 Pread(dumpfd, ctl.buf, ctl.len, dumpoff); 392 dumpoff += ctl.len; 393 394 if (ld.ld_csum != checksum32(ctl.buf, ctl.len)) 395 logprint(-1, verbose, 0, "bad log_ctl checksum"); 396 397 lc.flags |= SL_LOGONLY; 398 399 Pread(dumpfd, dat.buf, dat.len, dumpoff); 400 dumpoff += dat.len; 401 402 if (ld.ld_msum != checksum32(dat.buf, dat.len)) 403 logprint(-1, verbose, 0, "bad message checksum"); 404 405 if (putpmsg(logfd, &ctl, &dat, 1, MSG_BAND) == -1) 406 logprint(LOG_ERR, 1, 1, "putpmsg: %s", strerror(errno)); 407 408 ld.ld_magic = 0; /* clear magic so we never save twice */ 409 Pwrite(dumpfd, &ld, sizeof (log_dump_t), ldoff); 410 } 411 return (0); 412 } 413 414 int 415 main(int argc, char *argv[]) 416 { 417 int c, bfd; 418 int mflag = 0; 419 long bounds; 420 char namelist[30], corefile[30], boundstr[30]; 421 422 openlog(progname, LOG_ODELAY, LOG_AUTH); 423 (void) defopen("/etc/dumpadm.conf"); 424 savedir = defread("DUMPADM_SAVDIR="); 425 426 while ((c = getopt(argc, argv, "Lvdmf:")) != EOF) { 427 switch (c) { 428 case 'L': 429 livedump++; 430 break; 431 case 'v': 432 verbose++; 433 break; 434 case 'd': 435 disregard_valid_flag++; 436 break; 437 case 'm': 438 mflag++; 439 break; 440 case 'f': 441 dumpfile = optarg; 442 break; 443 case '?': 444 usage(); 445 } 446 } 447 448 if (dumpfile == NULL || livedump) 449 dumpfd = Open("/dev/dump", O_RDONLY, 0444); 450 451 if (dumpfile == NULL) { 452 dumpfile = Zalloc(MAXPATHLEN); 453 if (ioctl(dumpfd, DIOCGETDEV, dumpfile) == -1) 454 logprint(LOG_ERR, 1, 1, "no dump device configured"); 455 } 456 457 if (mflag) 458 return (message_save()); 459 460 if (optind == argc - 1) 461 savedir = argv[optind]; 462 if (savedir == NULL || optind < argc - 1) 463 usage(); 464 465 if (livedump && ioctl(dumpfd, DIOCDUMP, NULL) == -1) 466 logprint(-1, 1, 1, "dedicated dump device required"); 467 468 (void) close(dumpfd); 469 470 read_dumphdr(); 471 472 /* 473 * We want this message to go to the log file, but not the console. 474 * There's no good way to do that with the existing syslog facility. 475 * We could extend it to handle this, but there doesn't seem to be 476 * a general need for it, so we isolate the complexity here instead. 477 */ 478 if (dumphdr.dump_panicstring[0] != '\0') { 479 int logfd = Open("/dev/conslog", O_WRONLY, 0644); 480 log_ctl_t lc; 481 struct strbuf ctl, dat; 482 char msg[DUMP_PANICSIZE + 100]; 483 char fmt[] = "reboot after panic: %s"; 484 uint32_t msgid; 485 486 STRLOG_MAKE_MSGID(fmt, msgid); 487 488 (void) sprintf(msg, "%s: [ID %u FACILITY_AND_PRIORITY] ", 489 progname, msgid); 490 (void) sprintf(msg + strlen(msg), fmt, 491 dumphdr.dump_panicstring); 492 493 lc.pri = LOG_AUTH | LOG_ERR; 494 lc.flags = SL_CONSOLE | SL_LOGONLY; 495 lc.level = 0; 496 497 ctl.buf = (void *)&lc; 498 ctl.len = sizeof (log_ctl_t); 499 500 dat.buf = (void *)msg; 501 dat.len = strlen(msg) + 1; 502 503 (void) putmsg(logfd, &ctl, &dat, 0); 504 (void) close(logfd); 505 } 506 507 if (chdir(savedir) == -1) 508 logprint(LOG_ERR, 1, 1, "chdir(\"%s\"): %s", 509 savedir, strerror(errno)); 510 511 if ((dumphdr.dump_flags & DF_COMPLETE) == 0) 512 logprint(LOG_WARNING, 1, -1, "incomplete dump on dump device"); 513 514 (void) printf("System dump time: %s", ctime(&dumphdr.dump_crashtime)); 515 516 check_space(); 517 518 bounds = read_number_from_file("bounds", 0); 519 520 (void) sprintf(namelist, "unix.%ld", bounds); 521 (void) sprintf(corefile, "vmcore.%ld", bounds); 522 523 syslog(LOG_ERR, "saving system crash dump in %s/*.%ld", 524 savedir, bounds); 525 526 build_corefile(namelist, corefile); 527 528 (void) sprintf(boundstr, "%ld\n", bounds + 1); 529 bfd = Open("bounds", O_WRONLY | O_CREAT | O_TRUNC, 0644); 530 Pwrite(bfd, boundstr, strlen(boundstr), 0); 531 (void) close(bfd); 532 533 return (0); 534 } 535