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