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 (c) 1983, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2016 Joyent, Inc.
  24  */
  25 /*
  26  * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
  27  */
  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 <pthread.h>
  41 #include <limits.h>
  42 #include <atomic.h>
  43 #include <libnvpair.h>
  44 #include <libintl.h>
  45 #include <sys/mem.h>
  46 #include <sys/statvfs.h>
  47 #include <sys/dumphdr.h>
  48 #include <sys/dumpadm.h>
  49 #include <sys/compress.h>
  50 #include <sys/panic.h>
  51 #include <sys/sysmacros.h>
  52 #include <sys/stat.h>
  53 #include <sys/resource.h>
  54 #include <bzip2/bzlib.h>
  55 #include <sys/fm/util.h>
  56 #include <fm/libfmevent.h>
  57 #include <sys/int_fmtio.h>
  58 
  59 
  60 /* fread/fwrite buffer size */
  61 #define FBUFSIZE                (1ULL << 20)
  62 
  63 /* minimum size for output buffering */
  64 #define MINCOREBLKSIZE          (1ULL << 17)
  65 
  66 /* create this file if metrics collection is enabled in the kernel */
  67 #define METRICSFILE "METRICS.csv"
  68 
  69 static char     progname[9] = "savecore";
  70 static char     *savedir;               /* savecore directory */
  71 static char     *dumpfile;              /* source of raw crash dump */
  72 static long     bounds = -1;            /* numeric suffix */
  73 static long     pagesize;               /* dump pagesize */
  74 static int      dumpfd = -1;            /* dumpfile descriptor */
  75 static boolean_t have_dumpfile = B_TRUE;        /* dumpfile existence */
  76 static dumphdr_t corehdr, dumphdr;      /* initial and terminal dumphdrs */
  77 static boolean_t dump_incomplete;       /* dumphdr indicates incomplete */
  78 static boolean_t fm_panic;              /* dump is the result of fm_panic */
  79 static offset_t endoff;                 /* offset of end-of-dump header */
  80 static int      verbose;                /* chatty mode */
  81 static int      disregard_valid_flag;   /* disregard valid flag */
  82 static int      livedump;               /* dump the current running system */
  83 static int      interactive;            /* user invoked; no syslog */
  84 static int      csave;                  /* save dump compressed */
  85 static int      filemode;               /* processing file, not dump device */
  86 static int      percent_done;           /* progress indicator */
  87 static int      sec_done;               /* progress last report time */
  88 static hrtime_t startts;                /* timestamp at start */
  89 static volatile uint64_t saved;         /* count of pages written */
  90 static volatile uint64_t zpages;        /* count of zero pages not written */
  91 static dumpdatahdr_t datahdr;           /* compression info */
  92 static long     coreblksize;            /* preferred write size (st_blksize) */
  93 static int      cflag;                  /* run as savecore -c */
  94 static int      mflag;                  /* run as savecore -m */
  95 
  96 /*
  97  * Payload information for the events we raise.  These are used
  98  * in raise_event to determine what payload to include.
  99  */
 100 #define SC_PAYLOAD_SAVEDIR      0x0001  /* Include savedir in event */
 101 #define SC_PAYLOAD_INSTANCE     0x0002  /* Include bounds instance number */
 102 #define SC_PAYLOAD_IMAGEUUID    0x0004  /* Include dump OS instance uuid */
 103 #define SC_PAYLOAD_CRASHTIME    0x0008  /* Include epoch crashtime */
 104 #define SC_PAYLOAD_PANICSTR     0x0010  /* Include panic string */
 105 #define SC_PAYLOAD_PANICSTACK   0x0020  /* Include panic string */
 106 #define SC_PAYLOAD_FAILREASON   0x0040  /* Include failure reason */
 107 #define SC_PAYLOAD_DUMPCOMPLETE 0x0080  /* Include completeness indicator */
 108 #define SC_PAYLOAD_ISCOMPRESSED 0x0100  /* Dump is in vmdump.N form */
 109 #define SC_PAYLOAD_DUMPADM_EN   0x0200  /* Is dumpadm enabled or not? */
 110 #define SC_PAYLOAD_FM_PANIC     0x0400  /* Panic initiated by FMA */
 111 #define SC_PAYLOAD_JUSTCHECKING 0x0800  /* Run with -c flag? */
 112 
 113 enum sc_event_type {
 114         SC_EVENT_DUMP_PENDING,
 115         SC_EVENT_SAVECORE_FAILURE,
 116         SC_EVENT_DUMP_AVAILABLE
 117 };
 118 
 119 /*
 120  * Common payload
 121  */
 122 #define _SC_PAYLOAD_CMN \
 123     SC_PAYLOAD_IMAGEUUID | \
 124     SC_PAYLOAD_CRASHTIME | \
 125     SC_PAYLOAD_PANICSTR | \
 126     SC_PAYLOAD_PANICSTACK | \
 127     SC_PAYLOAD_DUMPCOMPLETE | \
 128     SC_PAYLOAD_FM_PANIC | \
 129     SC_PAYLOAD_SAVEDIR
 130 
 131 static const struct {
 132         const char *sce_subclass;
 133         uint32_t sce_payload;
 134 } sc_event[] = {
 135         /*
 136          * SC_EVENT_DUMP_PENDING
 137          */
 138         {
 139                 "dump_pending_on_device",
 140                 _SC_PAYLOAD_CMN | SC_PAYLOAD_DUMPADM_EN |
 141                     SC_PAYLOAD_JUSTCHECKING
 142         },
 143 
 144         /*
 145          * SC_EVENT_SAVECORE_FAILURE
 146          */
 147         {
 148                 "savecore_failure",
 149                 _SC_PAYLOAD_CMN | SC_PAYLOAD_INSTANCE | SC_PAYLOAD_FAILREASON
 150         },
 151 
 152         /*
 153          * SC_EVENT_DUMP_AVAILABLE
 154          */
 155         {
 156                 "dump_available",
 157                 _SC_PAYLOAD_CMN | SC_PAYLOAD_INSTANCE | SC_PAYLOAD_ISCOMPRESSED
 158         },
 159 };
 160 
 161 static void raise_event(enum sc_event_type, char *);
 162 
 163 static void
 164 usage(void)
 165 {
 166         (void) fprintf(stderr,
 167             "usage: %s [-Lvd] [-f dumpfile] [dirname]\n", progname);
 168         exit(1);
 169 }
 170 
 171 #define SC_SL_NONE      0x0001  /* no syslog */
 172 #define SC_SL_ERR       0x0002  /* syslog if !interactive, LOG_ERR */
 173 #define SC_SL_WARN      0x0004  /* syslog if !interactive, LOG_WARNING */
 174 #define SC_IF_VERBOSE   0x0008  /* message only if -v */
 175 #define SC_IF_ISATTY    0x0010  /* message only if interactive */
 176 #define SC_EXIT_OK      0x0020  /* exit(0) */
 177 #define SC_EXIT_ERR     0x0040  /* exit(1) */
 178 #define SC_EXIT_PEND    0x0080  /* exit(2) */
 179 #define SC_EXIT_FM      0x0100  /* exit(3) */
 180 
 181 #define _SC_ALLEXIT     (SC_EXIT_OK | SC_EXIT_ERR | SC_EXIT_PEND | SC_EXIT_FM)
 182 
 183 static void
 184 logprint(uint32_t flags, char *message, ...)
 185 {
 186         va_list args;
 187         char buf[1024];
 188         int do_always = ((flags & (SC_IF_VERBOSE | SC_IF_ISATTY)) == 0);
 189         int do_ifverb = (flags & SC_IF_VERBOSE) && verbose;
 190         int do_ifisatty = (flags & SC_IF_ISATTY) && interactive;
 191         int code;
 192         static int logprint_raised = 0;
 193 
 194         if (do_always || do_ifverb || do_ifisatty) {
 195                 va_start(args, message);
 196                 /*LINTED: E_SEC_PRINTF_VAR_FMT*/
 197                 (void) vsnprintf(buf, sizeof (buf), message, args);
 198                 (void) fprintf(stderr, "%s: %s\n", progname, buf);
 199                 if (!interactive) {
 200                         switch (flags & (SC_SL_NONE | SC_SL_ERR | SC_SL_WARN)) {
 201                         case SC_SL_ERR:
 202                                 /*LINTED: E_SEC_PRINTF_VAR_FMT*/
 203                                 syslog(LOG_ERR, buf);
 204                                 break;
 205 
 206                         case SC_SL_WARN:
 207                                 /*LINTED: E_SEC_PRINTF_VAR_FMT*/
 208                                 syslog(LOG_WARNING, buf);
 209                                 break;
 210 
 211                         default:
 212                                 break;
 213                         }
 214                 }
 215                 va_end(args);
 216         }
 217 
 218         switch (flags & _SC_ALLEXIT) {
 219         case 0:
 220                 return;
 221 
 222         case SC_EXIT_OK:
 223                 code = 0;
 224                 break;
 225 
 226         case SC_EXIT_PEND:
 227                 /*
 228                  * Raise an ireport saying why we are exiting.  Do not
 229                  * raise if run as savecore -m.  If something in the
 230                  * raise_event codepath calls logprint avoid recursion.
 231                  */
 232                 if (!mflag && logprint_raised++ == 0)
 233                         raise_event(SC_EVENT_SAVECORE_FAILURE, buf);
 234                 code = 2;
 235                 break;
 236 
 237         case SC_EXIT_FM:
 238                 code = 3;
 239                 break;
 240 
 241         case SC_EXIT_ERR:
 242         default:
 243                 if (!mflag && logprint_raised++ == 0 && have_dumpfile)
 244                         raise_event(SC_EVENT_SAVECORE_FAILURE, buf);
 245                 code = 1;
 246                 break;
 247         }
 248 
 249         exit(code);
 250 }
 251 
 252 /*
 253  * System call / libc wrappers that exit on error.
 254  */
 255 static int
 256 Open(const char *name, int oflags, mode_t mode)
 257 {
 258         int fd;
 259 
 260         if ((fd = open64(name, oflags, mode)) == -1)
 261                 logprint(SC_SL_ERR | SC_EXIT_ERR, "open(\"%s\"): %s",
 262                     name, strerror(errno));
 263         return (fd);
 264 }
 265 
 266 static void
 267 Fread(void *buf, size_t size, FILE *f)
 268 {
 269         if (fread(buf, size, 1, f) != 1)
 270                 logprint(SC_SL_ERR | SC_EXIT_ERR, "fread: %s",
 271                     strerror(errno));
 272 }
 273 
 274 static void
 275 Fwrite(void *buf, size_t size, FILE *f)
 276 {
 277         if (fwrite(buf, size, 1, f) != 1)
 278                 logprint(SC_SL_ERR | SC_EXIT_ERR, "fwrite: %s",
 279                     strerror(errno));
 280 }
 281 
 282 static void
 283 Fseek(offset_t off, FILE *f)
 284 {
 285         if (fseeko64(f, off, SEEK_SET) != 0)
 286                 logprint(SC_SL_ERR | SC_EXIT_ERR, "fseeko64: %s",
 287                     strerror(errno));
 288 }
 289 
 290 typedef struct stat64 Stat_t;
 291 
 292 static void
 293 Fstat(int fd, Stat_t *sb, const char *fname)
 294 {
 295         if (fstat64(fd, sb) != 0)
 296                 logprint(SC_SL_ERR | SC_EXIT_ERR, "fstat(\"%s\"): %s", fname,
 297                     strerror(errno));
 298 }
 299 
 300 static void
 301 Stat(const char *fname, Stat_t *sb)
 302 {
 303         if (stat64(fname, sb) != 0) {
 304                 have_dumpfile = B_FALSE;
 305                 logprint(SC_SL_ERR | SC_EXIT_ERR, "failed to get status "
 306                     "of file %s", fname);
 307         }
 308 }
 309 
 310 static void
 311 Pread(int fd, void *buf, size_t size, offset_t off)
 312 {
 313         ssize_t sz = pread64(fd, buf, size, off);
 314 
 315         if (sz < 0)
 316                 logprint(SC_SL_ERR | SC_EXIT_ERR,
 317                     "pread: %s", strerror(errno));
 318         else if (sz != size)
 319                 logprint(SC_SL_ERR | SC_EXIT_ERR,
 320                     "pread: size %ld != %ld", sz, size);
 321 }
 322 
 323 static void
 324 Pwrite(int fd, void *buf, size_t size, off64_t off)
 325 {
 326         if (pwrite64(fd, buf, size, off) != size)
 327                 logprint(SC_SL_ERR | SC_EXIT_ERR, "pwrite: %s",
 328                     strerror(errno));
 329 }
 330 
 331 static void *
 332 Zalloc(size_t size)
 333 {
 334         void *buf;
 335 
 336         if ((buf = calloc(size, 1)) == NULL)
 337                 logprint(SC_SL_ERR | SC_EXIT_ERR, "calloc: %s",
 338                     strerror(errno));
 339         return (buf);
 340 }
 341 
 342 static long
 343 read_number_from_file(const char *filename, long default_value)
 344 {
 345         long file_value = -1;
 346         FILE *fp;
 347 
 348         if ((fp = fopen(filename, "r")) != NULL) {
 349                 (void) fscanf(fp, "%ld", &file_value);
 350                 (void) fclose(fp);
 351         }
 352         return (file_value < 0 ? default_value : file_value);
 353 }
 354 
 355 static void
 356 read_dumphdr(void)
 357 {
 358         if (filemode)
 359                 dumpfd = Open(dumpfile, O_RDONLY, 0644);
 360         else
 361                 dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644);
 362         endoff = llseek(dumpfd, -DUMP_OFFSET, SEEK_END) & -DUMP_OFFSET;
 363         Pread(dumpfd, &dumphdr, sizeof (dumphdr), endoff);
 364         Pread(dumpfd, &datahdr, sizeof (datahdr), endoff + sizeof (dumphdr));
 365 
 366         pagesize = dumphdr.dump_pagesize;
 367 
 368         if (dumphdr.dump_magic != DUMP_MAGIC)
 369                 logprint(SC_SL_NONE | SC_EXIT_PEND, "bad magic number %x",
 370                     dumphdr.dump_magic);
 371 
 372         if ((dumphdr.dump_flags & DF_VALID) == 0 && !disregard_valid_flag)
 373                 logprint(SC_SL_NONE | SC_IF_VERBOSE | SC_EXIT_OK,
 374                     "dump already processed");
 375 
 376         if (dumphdr.dump_version != DUMP_VERSION)
 377                 logprint(SC_SL_NONE | SC_IF_VERBOSE | SC_EXIT_PEND,
 378                     "dump version (%d) != %s version (%d)",
 379                     dumphdr.dump_version, progname, DUMP_VERSION);
 380 
 381         if (dumphdr.dump_wordsize != DUMP_WORDSIZE)
 382                 logprint(SC_SL_NONE | SC_EXIT_PEND,
 383                     "dump is from %u-bit kernel - cannot save on %u-bit kernel",
 384                     dumphdr.dump_wordsize, DUMP_WORDSIZE);
 385 
 386         if (datahdr.dump_datahdr_magic == DUMP_DATAHDR_MAGIC) {
 387                 if (datahdr.dump_datahdr_version != DUMP_DATAHDR_VERSION)
 388                         logprint(SC_SL_NONE | SC_IF_VERBOSE | SC_EXIT_PEND,
 389                             "dump data version (%d) != %s data version (%d)",
 390                             datahdr.dump_datahdr_version, progname,
 391                             DUMP_DATAHDR_VERSION);
 392         } else {
 393                 (void) memset(&datahdr, 0, sizeof (datahdr));
 394                 datahdr.dump_maxcsize = pagesize;
 395         }
 396 
 397         /*
 398          * Read the initial header, clear the valid bits, and compare headers.
 399          * The main header may have been overwritten by swapping if we're
 400          * using a swap partition as the dump device, in which case we bail.
 401          */
 402         Pread(dumpfd, &corehdr, sizeof (dumphdr_t), dumphdr.dump_start);
 403 
 404         corehdr.dump_flags &= ~DF_VALID;
 405         dumphdr.dump_flags &= ~DF_VALID;
 406 
 407         if (memcmp(&corehdr, &dumphdr, sizeof (dumphdr_t)) != 0) {
 408                 /*
 409                  * Clear valid bit so we don't complain on every invocation.
 410                  */
 411                 if (!filemode)
 412                         Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff);
 413                 logprint(SC_SL_ERR | SC_EXIT_ERR,
 414                     "initial dump header corrupt");
 415         }
 416 }
 417 
 418 static void
 419 check_space(int csave)
 420 {
 421         struct statvfs fsb;
 422         int64_t spacefree, dumpsize, minfree, datasize;
 423 
 424         if (statvfs(".", &fsb) < 0)
 425                 logprint(SC_SL_ERR | SC_EXIT_ERR, "statvfs: %s",
 426                     strerror(errno));
 427 
 428         dumpsize = dumphdr.dump_data - dumphdr.dump_start;
 429         datasize = dumphdr.dump_npages * pagesize;
 430         if (!csave)
 431                 dumpsize += datasize;
 432         else
 433                 dumpsize += datahdr.dump_data_csize;
 434 
 435         spacefree = (int64_t)fsb.f_bavail * fsb.f_frsize;
 436         minfree = 1024LL * read_number_from_file("minfree", 1024);
 437         if (spacefree < minfree + dumpsize) {
 438                 logprint(SC_SL_ERR | SC_EXIT_ERR,
 439                     "not enough space in %s (%lld MB avail, %lld MB needed)",
 440                     savedir, spacefree >> 20, (minfree + dumpsize) >> 20);
 441         }
 442 }
 443 
 444 static void
 445 build_dump_map(int corefd, const pfn_t *pfn_table)
 446 {
 447         long i;
 448         static long misses = 0;
 449         size_t dump_mapsize = (corehdr.dump_hashmask + 1) * sizeof (dump_map_t);
 450         mem_vtop_t vtop;
 451         dump_map_t *dmp = Zalloc(dump_mapsize);
 452         char *inbuf = Zalloc(FBUFSIZE);
 453         FILE *in = fdopen(dup(dumpfd), "rb");
 454 
 455         (void) setvbuf(in, inbuf, _IOFBF, FBUFSIZE);
 456         Fseek(dumphdr.dump_map, in);
 457 
 458         corehdr.dump_data = corehdr.dump_map + roundup(dump_mapsize, pagesize);
 459 
 460         for (i = 0; i < corehdr.dump_nvtop; i++) {
 461                 long first = 0;
 462                 long last = corehdr.dump_npages - 1;
 463                 long middle = 0;
 464                 pfn_t pfn = 0;
 465                 uintptr_t h;
 466 
 467                 Fread(&vtop, sizeof (mem_vtop_t), in);
 468                 while (last >= first) {
 469                         middle = (first + last) / 2;
 470                         pfn = pfn_table[middle];
 471                         if (pfn == vtop.m_pfn)
 472                                 break;
 473                         if (pfn < vtop.m_pfn)
 474                                 first = middle + 1;
 475                         else
 476                                 last = middle - 1;
 477                 }
 478                 if (pfn != vtop.m_pfn) {
 479                         if (++misses <= 10)
 480                                 (void) fprintf(stderr,
 481                                     "pfn %ld not found for as=%p, va=%p\n",
 482                                     vtop.m_pfn, (void *)vtop.m_as, vtop.m_va);
 483                         continue;
 484                 }
 485 
 486                 dmp[i].dm_as = vtop.m_as;
 487                 dmp[i].dm_va = (uintptr_t)vtop.m_va;
 488                 dmp[i].dm_data = corehdr.dump_data +
 489                     ((uint64_t)middle << corehdr.dump_pageshift);
 490 
 491                 h = DUMP_HASH(&corehdr, dmp[i].dm_as, dmp[i].dm_va);
 492                 dmp[i].dm_next = dmp[h].dm_first;
 493                 dmp[h].dm_first = corehdr.dump_map + i * sizeof (dump_map_t);
 494         }
 495 
 496         Pwrite(corefd, dmp, dump_mapsize, corehdr.dump_map);
 497         free(dmp);
 498         (void) fclose(in);
 499         free(inbuf);
 500 }
 501 
 502 /*
 503  * Copy whole sections of the dump device to the file.
 504  */
 505 static void
 506 Copy(offset_t dumpoff, len_t nb, offset_t *offp, int fd, char *buf,
 507     size_t sz)
 508 {
 509         size_t nr;
 510         offset_t off = *offp;
 511 
 512         while (nb > 0) {
 513                 nr = sz < nb ? sz : (size_t)nb;
 514                 Pread(dumpfd, buf, nr, dumpoff);
 515                 Pwrite(fd, buf, nr, off);
 516                 off += nr;
 517                 dumpoff += nr;
 518                 nb -= nr;
 519         }
 520         *offp = off;
 521 }
 522 
 523 /*
 524  * Copy pages when the dump data header is missing.
 525  * This supports older kernels with latest savecore.
 526  */
 527 static void
 528 CopyPages(offset_t *offp, int fd, char *buf, size_t sz)
 529 {
 530         uint32_t csize;
 531         FILE *in = fdopen(dup(dumpfd), "rb");
 532         FILE *out = fdopen(dup(fd), "wb");
 533         char *cbuf = Zalloc(pagesize);
 534         char *outbuf = Zalloc(FBUFSIZE);
 535         pgcnt_t np = dumphdr.dump_npages;
 536 
 537         (void) setvbuf(out, outbuf, _IOFBF, FBUFSIZE);
 538         (void) setvbuf(in, buf, _IOFBF, sz);
 539         Fseek(dumphdr.dump_data, in);
 540 
 541         Fseek(*offp, out);
 542         while (np > 0) {
 543                 Fread(&csize, sizeof (uint32_t), in);
 544                 Fwrite(&csize, sizeof (uint32_t), out);
 545                 *offp += sizeof (uint32_t);
 546                 if (csize > pagesize || csize == 0) {
 547                         logprint(SC_SL_ERR,
 548                             "CopyPages: page %lu csize %d (0x%x) pagesize %d",
 549                             dumphdr.dump_npages - np, csize, csize,
 550                             pagesize);
 551                         break;
 552                 }
 553                 Fread(cbuf, csize, in);
 554                 Fwrite(cbuf, csize, out);
 555                 *offp += csize;
 556                 np--;
 557         }
 558         (void) fclose(in);
 559         (void) fclose(out);
 560         free(outbuf);
 561         free(buf);
 562 }
 563 
 564 /*
 565  * Concatenate dump contents into a new file.
 566  * Update corehdr with new offsets.
 567  */
 568 static void
 569 copy_crashfile(const char *corefile)
 570 {
 571         int corefd = Open(corefile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
 572         size_t bufsz = FBUFSIZE;
 573         char *inbuf = Zalloc(bufsz);
 574         offset_t coreoff;
 575         size_t nb;
 576 
 577         logprint(SC_SL_ERR | SC_IF_VERBOSE,
 578             "Copying %s to %s/%s\n", dumpfile, savedir, corefile);
 579 
 580         /*
 581          * This dump file is still compressed
 582          */
 583         corehdr.dump_flags |= DF_COMPRESSED | DF_VALID;
 584 
 585         /*
 586          * Leave room for corehdr, it is updated and written last
 587          */
 588         corehdr.dump_start = 0;
 589         coreoff = sizeof (corehdr);
 590 
 591         /*
 592          * Read in the compressed symbol table, copy it to corefile.
 593          */
 594         coreoff = roundup(coreoff, pagesize);
 595         corehdr.dump_ksyms = coreoff;
 596         Copy(dumphdr.dump_ksyms, dumphdr.dump_ksyms_csize, &coreoff, corefd,
 597             inbuf, bufsz);
 598 
 599         /*
 600          * Save the pfn table.
 601          */
 602         coreoff = roundup(coreoff, pagesize);
 603         corehdr.dump_pfn = coreoff;
 604         Copy(dumphdr.dump_pfn, dumphdr.dump_npages * sizeof (pfn_t), &coreoff,
 605             corefd, inbuf, bufsz);
 606 
 607         /*
 608          * Save the dump map.
 609          */
 610         coreoff = roundup(coreoff, pagesize);
 611         corehdr.dump_map = coreoff;
 612         Copy(dumphdr.dump_map, dumphdr.dump_nvtop * sizeof (mem_vtop_t),
 613             &coreoff, corefd, inbuf, bufsz);
 614 
 615         /*
 616          * Save the data pages.
 617          */
 618         coreoff = roundup(coreoff, pagesize);
 619         corehdr.dump_data = coreoff;
 620         if (datahdr.dump_data_csize != 0)
 621                 Copy(dumphdr.dump_data, datahdr.dump_data_csize, &coreoff,
 622                     corefd, inbuf, bufsz);
 623         else
 624                 CopyPages(&coreoff, corefd, inbuf, bufsz);
 625 
 626         /*
 627          * Now write the modified dump header to front and end of the copy.
 628          * Make it look like a valid dump device.
 629          *
 630          * From dumphdr.h: Two headers are written out: one at the
 631          * beginning of the dump, and the other at the very end of the
 632          * dump device. The terminal header is at a known location
 633          * (end of device) so we can always find it.
 634          *
 635          * Pad with zeros to each DUMP_OFFSET boundary.
 636          */
 637         (void) memset(inbuf, 0, DUMP_OFFSET);
 638 
 639         nb = DUMP_OFFSET - (coreoff & (DUMP_OFFSET - 1));
 640         if (nb > 0) {
 641                 Pwrite(corefd, inbuf, nb, coreoff);
 642                 coreoff += nb;
 643         }
 644 
 645         Pwrite(corefd, &corehdr, sizeof (corehdr), coreoff);
 646         coreoff += sizeof (corehdr);
 647 
 648         Pwrite(corefd, &datahdr, sizeof (datahdr), coreoff);
 649         coreoff += sizeof (datahdr);
 650 
 651         nb = DUMP_OFFSET - (coreoff & (DUMP_OFFSET - 1));
 652         if (nb > 0) {
 653                 Pwrite(corefd, inbuf, nb, coreoff);
 654         }
 655 
 656         free(inbuf);
 657         Pwrite(corefd, &corehdr, sizeof (corehdr), corehdr.dump_start);
 658 
 659         /*
 660          * Write out the modified dump header to the dump device.
 661          * The dump device has been processed, so DF_VALID is clear.
 662          */
 663         if (!filemode)
 664                 Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff);
 665 
 666         (void) close(corefd);
 667 }
 668 
 669 /*
 670  * compressed streams
 671  */
 672 typedef struct blockhdr blockhdr_t;
 673 typedef struct block block_t;
 674 
 675 struct blockhdr {
 676         block_t *head;
 677         block_t *tail;
 678 };
 679 
 680 struct block {
 681         block_t *next;
 682         char *block;
 683         int size;
 684 };
 685 
 686 typedef enum streamstate {
 687         STREAMSTART,
 688         STREAMPAGES
 689 } streamstate_t;
 690 
 691 typedef struct stream {
 692         streamstate_t state;
 693         int init;
 694         int tag;
 695         int bound;
 696         int nout;
 697         char *blkbuf;
 698         blockhdr_t blocks;
 699         pgcnt_t pagenum;
 700         pgcnt_t curpage;
 701         pgcnt_t npages;
 702         pgcnt_t done;
 703         bz_stream strm;
 704         dumpcsize_t sc;
 705         dumpstreamhdr_t sh;
 706 } stream_t;
 707 
 708 static stream_t *streams;
 709 static stream_t *endstreams;
 710 
 711 const int cs = sizeof (dumpcsize_t);
 712 
 713 typedef struct tinfo {
 714         pthread_t tid;
 715         int corefd;
 716 } tinfo_t;
 717 
 718 static int threads_stop;
 719 static int threads_active;
 720 static tinfo_t *tinfo;
 721 static tinfo_t *endtinfo;
 722 
 723 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
 724 static pthread_cond_t cvfree = PTHREAD_COND_INITIALIZER;
 725 static pthread_cond_t cvwork = PTHREAD_COND_INITIALIZER;
 726 static pthread_cond_t cvbarrier = PTHREAD_COND_INITIALIZER;
 727 
 728 static blockhdr_t freeblocks;
 729 
 730 static void
 731 enqt(blockhdr_t *h, block_t *b)
 732 {
 733         b->next = NULL;
 734         if (h->tail == NULL)
 735                 h->head = b;
 736         else
 737                 h->tail->next = b;
 738         h->tail = b;
 739 }
 740 
 741 static block_t *
 742 deqh(blockhdr_t *h)
 743 {
 744         block_t *b = h->head;
 745 
 746         if (b != NULL) {
 747                 h->head = b->next;
 748                 if (h->head == NULL)
 749                         h->tail = NULL;
 750         }
 751         return (b);
 752 }
 753 
 754 static void *runstreams(void *arg);
 755 
 756 static void
 757 initstreams(int corefd, int nstreams, int maxcsize)
 758 {
 759         int nthreads;
 760         int nblocks;
 761         int i;
 762         block_t *b;
 763         tinfo_t *t;
 764 
 765         nthreads = sysconf(_SC_NPROCESSORS_ONLN);
 766         if (nstreams < nthreads)
 767                 nthreads = nstreams;
 768         if (nthreads < 1)
 769                 nthreads = 1;
 770         nblocks = nthreads * 2;
 771 
 772         tinfo = Zalloc(nthreads * sizeof (tinfo_t));
 773         endtinfo = &tinfo[nthreads];
 774 
 775         /* init streams */
 776         streams = Zalloc(nstreams * sizeof (stream_t));
 777         endstreams = &streams[nstreams];
 778 
 779         /* init stream block buffers */
 780         for (i = 0; i < nblocks; i++) {
 781                 b = Zalloc(sizeof (block_t));
 782                 b->block = Zalloc(maxcsize);
 783                 enqt(&freeblocks, b);
 784         }
 785 
 786         /* init worker threads */
 787         (void) pthread_mutex_lock(&lock);
 788         threads_active = 1;
 789         threads_stop = 0;
 790         for (t = tinfo; t != endtinfo; t++) {
 791                 t->corefd = dup(corefd);
 792                 if (t->corefd < 0) {
 793                         nthreads = t - tinfo;
 794                         endtinfo = t;
 795                         break;
 796                 }
 797                 if (pthread_create(&t->tid, NULL, runstreams, t) != 0)
 798                         logprint(SC_SL_ERR | SC_EXIT_ERR, "pthread_create: %s",
 799                             strerror(errno));
 800         }
 801         (void) pthread_mutex_unlock(&lock);
 802 }
 803 
 804 static void
 805 sbarrier()
 806 {
 807         stream_t *s;
 808 
 809         (void) pthread_mutex_lock(&lock);
 810         for (s = streams; s != endstreams; s++) {
 811                 while (s->bound || s->blocks.head != NULL)
 812                         (void) pthread_cond_wait(&cvbarrier, &lock);
 813         }
 814         (void) pthread_mutex_unlock(&lock);
 815 }
 816 
 817 static void
 818 stopstreams()
 819 {
 820         tinfo_t *t;
 821 
 822         if (threads_active) {
 823                 sbarrier();
 824                 (void) pthread_mutex_lock(&lock);
 825                 threads_stop = 1;
 826                 (void) pthread_cond_signal(&cvwork);
 827                 (void) pthread_mutex_unlock(&lock);
 828                 for (t = tinfo; t != endtinfo; t++)
 829                         (void) pthread_join(t->tid, NULL);
 830                 free(tinfo);
 831                 tinfo = NULL;
 832                 threads_active = 0;
 833         }
 834 }
 835 
 836 static block_t *
 837 getfreeblock()
 838 {
 839         block_t *b;
 840 
 841         (void) pthread_mutex_lock(&lock);
 842         while ((b = deqh(&freeblocks)) == NULL)
 843                 (void) pthread_cond_wait(&cvfree, &lock);
 844         (void) pthread_mutex_unlock(&lock);
 845         return (b);
 846 }
 847 
 848 /* data page offset from page number */
 849 #define BTOP(b)         ((b) >> dumphdr.dump_pageshift)
 850 #define PTOB(p)         ((p) << dumphdr.dump_pageshift)
 851 #define DATAOFF(p)      (corehdr.dump_data + PTOB(p))
 852 
 853 /* check for coreblksize boundary */
 854 static int
 855 isblkbnd(pgcnt_t pgnum)
 856 {
 857         return (P2PHASE(DATAOFF(pgnum), coreblksize) == 0);
 858 }
 859 
 860 static int
 861 iszpage(char *buf)
 862 {
 863         size_t sz;
 864         uint64_t *pl;
 865 
 866         /*LINTED:E_BAD_PTR_CAST_ALIGN*/
 867         pl = (uint64_t *)(buf);
 868         for (sz = 0; sz < pagesize; sz += sizeof (*pl))
 869                 if (*pl++ != 0)
 870                         return (0);
 871         return (1);
 872 }
 873 
 874 volatile uint_t *hist;
 875 
 876 /* write pages to the core file */
 877 static void
 878 putpage(int corefd, char *buf, pgcnt_t pgnum, pgcnt_t np)
 879 {
 880         atomic_inc_uint(&hist[np]);
 881         if (np > 0)
 882                 Pwrite(corefd, buf, PTOB(np), DATAOFF(pgnum));
 883 }
 884 
 885 /*
 886  * Process one lzjb block.
 887  * No object (stream header or page) will be split over a block boundary.
 888  */
 889 static void
 890 lzjbblock(int corefd, stream_t *s, char *block, size_t blocksz)
 891 {
 892         int in = 0;
 893         int csize;
 894         int doflush;
 895         char *out;
 896         size_t dsize;
 897         dumpcsize_t sc;
 898         dumpstreamhdr_t sh;
 899 
 900         if (!s->init) {
 901                 s->init = 1;
 902                 if (s->blkbuf == NULL)
 903                         s->blkbuf = Zalloc(coreblksize);
 904                 s->state = STREAMSTART;
 905         }
 906         while (in < blocksz) {
 907                 switch (s->state) {
 908                 case STREAMSTART:
 909                         (void) memcpy(&sh, block + in, sizeof (sh));
 910                         in += sizeof (sh);
 911                         if (strcmp(DUMP_STREAM_MAGIC, sh.stream_magic) != 0)
 912                                 logprint(SC_SL_ERR | SC_EXIT_ERR,
 913                                     "LZJB STREAMSTART: bad stream header");
 914                         if (sh.stream_npages > datahdr.dump_maxrange)
 915                                 logprint(SC_SL_ERR | SC_EXIT_ERR,
 916                                     "LZJB STREAMSTART: bad range: %d > %d",
 917                                     sh.stream_npages, datahdr.dump_maxrange);
 918                         s->pagenum = sh.stream_pagenum;
 919                         s->npages = sh.stream_npages;
 920                         s->curpage = s->pagenum;
 921                         s->nout = 0;
 922                         s->done = 0;
 923                         s->state = STREAMPAGES;
 924                         break;
 925                 case STREAMPAGES:
 926                         (void) memcpy(&sc, block + in, cs);
 927                         in += cs;
 928                         csize = DUMP_GET_CSIZE(sc);
 929                         if (csize > pagesize)
 930                                 logprint(SC_SL_ERR | SC_EXIT_ERR,
 931                                     "LZJB STREAMPAGES: bad csize=%d", csize);
 932 
 933                         out =  s->blkbuf + PTOB(s->nout);
 934                         dsize = decompress(block + in, out, csize, pagesize);
 935 
 936                         if (dsize != pagesize)
 937                                 logprint(SC_SL_ERR | SC_EXIT_ERR,
 938                                     "LZJB STREAMPAGES: dsize %d != pagesize %d",
 939                                     dsize, pagesize);
 940 
 941                         in += csize;
 942                         atomic_inc_64(&saved);
 943 
 944                         doflush = 0;
 945                         if (s->nout == 0 && iszpage(out)) {
 946                                 doflush = 1;
 947                                 atomic_inc_64(&zpages);
 948                         } else if (++s->nout >= BTOP(coreblksize) ||
 949                             isblkbnd(s->curpage + s->nout)) {
 950                                 doflush = 1;
 951                         }
 952                         if (++s->done >= s->npages) {
 953                                 s->state = STREAMSTART;
 954                                 doflush = 1;
 955                         }
 956                         if (doflush) {
 957                                 putpage(corefd, s->blkbuf, s->curpage, s->nout);
 958                                 s->nout = 0;
 959                                 s->curpage = s->pagenum + s->done;
 960                         }
 961                         break;
 962                 }
 963         }
 964 }
 965 
 966 /* bzlib library reports errors with this callback */
 967 void
 968 bz_internal_error(int errcode)
 969 {
 970         logprint(SC_SL_ERR | SC_EXIT_ERR, "bz_internal_error: err %s\n",
 971             BZ2_bzErrorString(errcode));
 972 }
 973 
 974 /*
 975  * Return one object in the stream.
 976  *
 977  * An object (stream header or page) will likely span an input block
 978  * of compression data. Return non-zero when an entire object has been
 979  * retrieved from the stream.
 980  */
 981 static int
 982 bz2decompress(stream_t *s, void *buf, size_t size)
 983 {
 984         int rc;
 985 
 986         if (s->strm.avail_out == 0) {
 987                 s->strm.next_out = buf;
 988                 s->strm.avail_out = size;
 989         }
 990         while (s->strm.avail_in > 0) {
 991                 rc = BZ2_bzDecompress(&s->strm);
 992                 if (rc == BZ_STREAM_END) {
 993                         rc = BZ2_bzDecompressReset(&s->strm);
 994                         if (rc != BZ_OK)
 995                                 logprint(SC_SL_ERR | SC_EXIT_ERR,
 996                                     "BZ2_bzDecompressReset: %s",
 997                                     BZ2_bzErrorString(rc));
 998                         continue;
 999                 }
1000 
1001                 if (s->strm.avail_out == 0)
1002                         break;
1003         }
1004         return (s->strm.avail_out == 0);
1005 }
1006 
1007 /*
1008  * Process one bzip2 block.
1009  * The interface is documented here:
1010  * http://www.bzip.org/1.0.5/bzip2-manual-1.0.5.html
1011  */
1012 static void
1013 bz2block(int corefd, stream_t *s, char *block, size_t blocksz)
1014 {
1015         int rc = 0;
1016         int doflush;
1017         char *out;
1018 
1019         if (!s->init) {
1020                 s->init = 1;
1021                 rc = BZ2_bzDecompressInit(&s->strm, 0, 0);
1022                 if (rc != BZ_OK)
1023                         logprint(SC_SL_ERR | SC_EXIT_ERR,
1024                             "BZ2_bzDecompressInit: %s", BZ2_bzErrorString(rc));
1025                 if (s->blkbuf == NULL)
1026                         s->blkbuf = Zalloc(coreblksize);
1027                 s->strm.avail_out = 0;
1028                 s->state = STREAMSTART;
1029         }
1030         s->strm.next_in = block;
1031         s->strm.avail_in = blocksz;
1032 
1033         while (s->strm.avail_in > 0) {
1034                 switch (s->state) {
1035                 case STREAMSTART:
1036                         if (!bz2decompress(s, &s->sh, sizeof (s->sh)))
1037                                 return;
1038                         if (strcmp(DUMP_STREAM_MAGIC, s->sh.stream_magic) != 0)
1039                                 logprint(SC_SL_ERR | SC_EXIT_ERR,
1040                                     "BZ2 STREAMSTART: bad stream header");
1041                         if (s->sh.stream_npages > datahdr.dump_maxrange)
1042                                 logprint(SC_SL_ERR | SC_EXIT_ERR,
1043                                     "BZ2 STREAMSTART: bad range: %d > %d",
1044                                     s->sh.stream_npages, datahdr.dump_maxrange);
1045                         s->pagenum = s->sh.stream_pagenum;
1046                         s->npages = s->sh.stream_npages;
1047                         s->curpage = s->pagenum;
1048                         s->nout = 0;
1049                         s->done = 0;
1050                         s->state = STREAMPAGES;
1051                         break;
1052                 case STREAMPAGES:
1053                         out = s->blkbuf + PTOB(s->nout);
1054                         if (!bz2decompress(s, out, pagesize))
1055                                 return;
1056 
1057                         atomic_inc_64(&saved);
1058 
1059                         doflush = 0;
1060                         if (s->nout == 0 && iszpage(out)) {
1061                                 doflush = 1;
1062                                 atomic_inc_64(&zpages);
1063                         } else if (++s->nout >= BTOP(coreblksize) ||
1064                             isblkbnd(s->curpage + s->nout)) {
1065                                 doflush = 1;
1066                         }
1067                         if (++s->done >= s->npages) {
1068                                 s->state = STREAMSTART;
1069                                 doflush = 1;
1070                         }
1071                         if (doflush) {
1072                                 putpage(corefd, s->blkbuf, s->curpage, s->nout);
1073                                 s->nout = 0;
1074                                 s->curpage = s->pagenum + s->done;
1075                         }
1076                         break;
1077                 }
1078         }
1079 }
1080 
1081 /* report progress */
1082 static void
1083 report_progress()
1084 {
1085         int sec, percent;
1086 
1087         if (!interactive)
1088                 return;
1089 
1090         percent = saved * 100LL / corehdr.dump_npages;
1091         sec = (gethrtime() - startts) / NANOSEC;
1092         if (percent > percent_done || sec > sec_done) {
1093                 (void) printf("\r%2d:%02d %3d%% done", sec / 60, sec % 60,
1094                     percent);
1095                 (void) fflush(stdout);
1096                 sec_done = sec;
1097                 percent_done = percent;
1098         }
1099 }
1100 
1101 /* thread body */
1102 static void *
1103 runstreams(void *arg)
1104 {
1105         tinfo_t *t = arg;
1106         stream_t *s;
1107         block_t *b;
1108         int bound;
1109 
1110         (void) pthread_mutex_lock(&lock);
1111         while (!threads_stop) {
1112                 bound = 0;
1113                 for (s = streams; s != endstreams; s++) {
1114                         if (s->bound || s->blocks.head == NULL)
1115                                 continue;
1116                         s->bound = 1;
1117                         bound = 1;
1118                         (void) pthread_cond_signal(&cvwork);
1119                         while (s->blocks.head != NULL) {
1120                                 b = deqh(&s->blocks);
1121                                 (void) pthread_mutex_unlock(&lock);
1122 
1123                                 if (datahdr.dump_clevel < DUMP_CLEVEL_BZIP2)
1124                                         lzjbblock(t->corefd, s, b->block,
1125                                             b->size);
1126                                 else
1127                                         bz2block(t->corefd, s, b->block,
1128                                             b->size);
1129 
1130                                 (void) pthread_mutex_lock(&lock);
1131                                 enqt(&freeblocks, b);
1132                                 (void) pthread_cond_signal(&cvfree);
1133 
1134                                 report_progress();
1135                         }
1136                         s->bound = 0;
1137                         (void) pthread_cond_signal(&cvbarrier);
1138                 }
1139                 if (!bound && !threads_stop)
1140                         (void) pthread_cond_wait(&cvwork, &lock);
1141         }
1142         (void) close(t->corefd);
1143         (void) pthread_cond_signal(&cvwork);
1144         (void) pthread_mutex_unlock(&lock);
1145         return (arg);
1146 }
1147 
1148 /*
1149  * Process compressed pages.
1150  *
1151  * The old format, now called single-threaded lzjb, is a 32-bit size
1152  * word followed by 'size' bytes of lzjb compression data for one
1153  * page. The new format extends this by storing a 12-bit "tag" in the
1154  * upper bits of the size word. When the size word is pagesize or
1155  * less, it is assumed to be one lzjb page. When the size word is
1156  * greater than pagesize, it is assumed to be a "stream block",
1157  * belonging to up to 4095 streams. In practice, the number of streams
1158  * is set to one less than the number of CPUs running at crash
1159  * time. One CPU processes the crash dump, the remaining CPUs
1160  * separately process groups of data pages.
1161  *
1162  * savecore creates a thread per stream, but never more threads than
1163  * the number of CPUs running savecore. This is because savecore can
1164  * be processing a crash file from a remote machine, which may have
1165  * more CPUs.
1166  *
1167  * When the kernel uses parallel lzjb or parallel bzip2, we expect a
1168  * series of 128KB blocks of compression data. In this case, each
1169  * block has a "tag", in the range 1-4095. Each block is handed off to
1170  * to the threads running "runstreams". The dump format is either lzjb
1171  * or bzip2, never a mixture. These threads, in turn, process the
1172  * compression data for groups of pages. Groups of pages are delimited
1173  * by a "stream header", which indicates a starting pfn and number of
1174  * pages. When a stream block has been read, the condition variable
1175  * "cvwork" is signalled, which causes one of the avaiable threads to
1176  * wake up and process the stream.
1177  *
1178  * In the parallel case there will be streams blocks encoding all data
1179  * pages. The stream of blocks is terminated by a zero size
1180  * word. There can be a few lzjb pages tacked on the end, depending on
1181  * the architecture. The sbarrier function ensures that all stream
1182  * blocks have been processed so that the page number for the few
1183  * single pages at the end can be known.
1184  */
1185 static void
1186 decompress_pages(int corefd)
1187 {
1188         char *cpage = NULL;
1189         char *dpage = NULL;
1190         char *out;
1191         pgcnt_t curpage = 0;
1192         block_t *b;
1193         FILE *dumpf;
1194         FILE *tracef = NULL;
1195         stream_t *s;
1196         size_t dsize;
1197         size_t insz = FBUFSIZE;
1198         char *inbuf = Zalloc(insz);
1199         uint32_t csize;
1200         dumpcsize_t dcsize;
1201         int nstreams = datahdr.dump_nstreams;
1202         int maxcsize = datahdr.dump_maxcsize;
1203         int nout = 0, tag, doflush;
1204 
1205         dumpf = fdopen(dup(dumpfd), "rb");
1206         if (dumpf == NULL)
1207                 logprint(SC_SL_ERR | SC_EXIT_ERR, "fdopen: %s",
1208                     strerror(errno));
1209 
1210         (void) setvbuf(dumpf, inbuf, _IOFBF, insz);
1211         Fseek(dumphdr.dump_data, dumpf);
1212 
1213         /*LINTED: E_CONSTANT_CONDITION*/
1214         while (1) {
1215 
1216                 /*
1217                  * The csize word delimits stream blocks.
1218                  * See dumphdr.h for a description.
1219                  */
1220                 Fread(&dcsize, sizeof (dcsize), dumpf);
1221 
1222                 tag = DUMP_GET_TAG(dcsize);
1223                 csize = DUMP_GET_CSIZE(dcsize);
1224 
1225                 if (tag != 0) {         /* a stream block */
1226 
1227                         if (nstreams == 0)
1228                                 logprint(SC_SL_ERR | SC_EXIT_ERR,
1229                                     "starting data header is missing");
1230 
1231                         if (tag > nstreams)
1232                                 logprint(SC_SL_ERR | SC_EXIT_ERR,
1233                                     "stream tag %d not in range 1..%d",
1234                                     tag, nstreams);
1235 
1236                         if (csize > maxcsize)
1237                                 logprint(SC_SL_ERR | SC_EXIT_ERR,
1238                                     "block size 0x%x > max csize 0x%x",
1239                                     csize, maxcsize);
1240 
1241                         if (streams == NULL)
1242                                 initstreams(corefd, nstreams, maxcsize);
1243                         s = &streams[tag - 1];
1244                         s->tag = tag;
1245 
1246                         b = getfreeblock();
1247                         b->size = csize;
1248                         Fread(b->block, csize, dumpf);
1249 
1250                         (void) pthread_mutex_lock(&lock);
1251                         enqt(&s->blocks, b);
1252                         if (!s->bound)
1253                                 (void) pthread_cond_signal(&cvwork);
1254                         (void) pthread_mutex_unlock(&lock);
1255 
1256                 } else if (csize > 0) {              /* one lzjb page */
1257 
1258                         if (csize > pagesize)
1259                                 logprint(SC_SL_ERR | SC_EXIT_ERR,
1260                                     "csize 0x%x > pagesize 0x%x",
1261                                     csize, pagesize);
1262 
1263                         if (cpage == NULL)
1264                                 cpage = Zalloc(pagesize);
1265                         if (dpage == NULL) {
1266                                 dpage = Zalloc(coreblksize);
1267                                 nout = 0;
1268                         }
1269 
1270                         Fread(cpage, csize, dumpf);
1271 
1272                         out = dpage + PTOB(nout);
1273                         dsize = decompress(cpage, out, csize, pagesize);
1274 
1275                         if (dsize != pagesize)
1276                                 logprint(SC_SL_ERR | SC_EXIT_ERR,
1277                                     "dsize 0x%x != pagesize 0x%x",
1278                                     dsize, pagesize);
1279 
1280                         /*
1281                          * wait for streams to flush so that 'saved' is correct
1282                          */
1283                         if (threads_active)
1284                                 sbarrier();
1285 
1286                         doflush = 0;
1287                         if (nout == 0)
1288                                 curpage = saved;
1289 
1290                         atomic_inc_64(&saved);
1291 
1292                         if (nout == 0 && iszpage(dpage)) {
1293                                 doflush = 1;
1294                                 atomic_inc_64(&zpages);
1295                         } else if (++nout >= BTOP(coreblksize) ||
1296                             isblkbnd(curpage + nout) ||
1297                             saved >= dumphdr.dump_npages) {
1298                                 doflush = 1;
1299                         }
1300 
1301                         if (doflush) {
1302                                 putpage(corefd, dpage, curpage, nout);
1303                                 nout = 0;
1304                         }
1305 
1306                         report_progress();
1307 
1308                         /*
1309                          * Non-streams lzjb does not use blocks.  Stop
1310                          * here if all the pages have been decompressed.
1311                          */
1312                         if (saved >= dumphdr.dump_npages)
1313                                 break;
1314 
1315                 } else {
1316                         break;                  /* end of data */
1317                 }
1318         }
1319 
1320         stopstreams();
1321         if (tracef != NULL)
1322                 (void) fclose(tracef);
1323         (void) fclose(dumpf);
1324         if (inbuf)
1325                 free(inbuf);
1326         if (cpage)
1327                 free(cpage);
1328         if (dpage)
1329                 free(dpage);
1330         if (streams)
1331                 free(streams);
1332 }
1333 
1334 static void
1335 build_corefile(const char *namelist, const char *corefile)
1336 {
1337         size_t pfn_table_size = dumphdr.dump_npages * sizeof (pfn_t);
1338         size_t ksyms_size = dumphdr.dump_ksyms_size;
1339         size_t ksyms_csize = dumphdr.dump_ksyms_csize;
1340         pfn_t *pfn_table;
1341         char *ksyms_base = Zalloc(ksyms_size);
1342         char *ksyms_cbase = Zalloc(ksyms_csize);
1343         size_t ksyms_dsize;
1344         Stat_t st;
1345         int corefd = Open(corefile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
1346         int namefd = Open(namelist, O_WRONLY | O_CREAT | O_TRUNC, 0644);
1347 
1348         (void) printf("Constructing namelist %s/%s\n", savedir, namelist);
1349 
1350         /*
1351          * Determine the optimum write size for the core file
1352          */
1353         Fstat(corefd, &st, corefile);
1354 
1355         if (verbose > 1)
1356                 (void) printf("%s: %ld block size\n", corefile,
1357                     (long)st.st_blksize);
1358         coreblksize = st.st_blksize;
1359         if (coreblksize < MINCOREBLKSIZE || !ISP2(coreblksize))
1360                 coreblksize = MINCOREBLKSIZE;
1361 
1362         hist = Zalloc((sizeof (uint64_t) * BTOP(coreblksize)) + 1);
1363 
1364         /*
1365          * This dump file is now uncompressed
1366          */
1367         corehdr.dump_flags &= ~DF_COMPRESSED;
1368 
1369         /*
1370          * Read in the compressed symbol table, copy it to corefile,
1371          * decompress it, and write the result to namelist.
1372          */
1373         corehdr.dump_ksyms = pagesize;
1374         Pread(dumpfd, ksyms_cbase, ksyms_csize, dumphdr.dump_ksyms);
1375         Pwrite(corefd, ksyms_cbase, ksyms_csize, corehdr.dump_ksyms);
1376 
1377         ksyms_dsize = decompress(ksyms_cbase, ksyms_base, ksyms_csize,
1378             ksyms_size);
1379         if (ksyms_dsize != ksyms_size)
1380                 logprint(SC_SL_WARN,
1381                     "bad data in symbol table, %lu of %lu bytes saved",
1382                     ksyms_dsize, ksyms_size);
1383 
1384         Pwrite(namefd, ksyms_base, ksyms_size, 0);
1385         (void) close(namefd);
1386         free(ksyms_cbase);
1387         free(ksyms_base);
1388 
1389         (void) printf("Constructing corefile %s/%s\n", savedir, corefile);
1390 
1391         /*
1392          * Read in and write out the pfn table.
1393          */
1394         pfn_table = Zalloc(pfn_table_size);
1395         corehdr.dump_pfn = corehdr.dump_ksyms + roundup(ksyms_size, pagesize);
1396         Pread(dumpfd, pfn_table, pfn_table_size, dumphdr.dump_pfn);
1397         Pwrite(corefd, pfn_table, pfn_table_size, corehdr.dump_pfn);
1398 
1399         /*
1400          * Convert the raw translation data into a hashed dump map.
1401          */
1402         corehdr.dump_map = corehdr.dump_pfn + roundup(pfn_table_size, pagesize);
1403         build_dump_map(corefd, pfn_table);
1404         free(pfn_table);
1405 
1406         /*
1407          * Decompress the pages
1408          */
1409         decompress_pages(corefd);
1410         (void) printf(": %ld of %ld pages saved\n", (pgcnt_t)saved,
1411             dumphdr.dump_npages);
1412 
1413         if (verbose)
1414                 (void) printf("%ld (%ld%%) zero pages were not written\n",
1415                     (pgcnt_t)zpages, (pgcnt_t)zpages * 100 /
1416                     dumphdr.dump_npages);
1417 
1418         if (saved != dumphdr.dump_npages)
1419                 logprint(SC_SL_WARN, "bad data after page %ld", saved);
1420 
1421         /*
1422          * Write out the modified dump headers.
1423          */
1424         Pwrite(corefd, &corehdr, sizeof (corehdr), 0);
1425         if (!filemode)
1426                 Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff);
1427 
1428         (void) close(corefd);
1429 }
1430 
1431 /*
1432  * When the system panics, the kernel saves all undelivered messages (messages
1433  * that never made it out to syslogd(1M)) in the dump.  At a mimimum, the
1434  * panic message itself will always fall into this category.  Upon reboot,
1435  * the syslog startup script runs savecore -m to recover these messages.
1436  *
1437  * To do this, we read the unsent messages from the dump and send them to
1438  * /dev/conslog on priority band 1.  This has the effect of prepending them
1439  * to any already-accumulated messages in the console backlog, thus preserving
1440  * temporal ordering across the reboot.
1441  *
1442  * Note: since savecore -m is used *only* for this purpose, it does *not*
1443  * attempt to save the crash dump.  The dump will be saved later, after
1444  * syslogd(1M) starts, by the savecore startup script.
1445  */
1446 static int
1447 message_save(void)
1448 {
1449         offset_t dumpoff = -(DUMP_OFFSET + DUMP_LOGSIZE);
1450         offset_t ldoff;
1451         log_dump_t ld;
1452         log_ctl_t lc;
1453         struct strbuf ctl, dat;
1454         int logfd;
1455 
1456         logfd = Open("/dev/conslog", O_WRONLY, 0644);
1457         dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644);
1458         dumpoff = llseek(dumpfd, dumpoff, SEEK_END) & -DUMP_OFFSET;
1459 
1460         ctl.buf = (void *)&lc;
1461         ctl.len = sizeof (log_ctl_t);
1462 
1463         dat.buf = Zalloc(DUMP_LOGSIZE);
1464 
1465         for (;;) {
1466                 ldoff = dumpoff;
1467 
1468                 Pread(dumpfd, &ld, sizeof (log_dump_t), dumpoff);
1469                 dumpoff += sizeof (log_dump_t);
1470                 dat.len = ld.ld_msgsize;
1471 
1472                 if (ld.ld_magic == 0)
1473                         break;
1474 
1475                 if (ld.ld_magic != LOG_MAGIC)
1476                         logprint(SC_SL_ERR | SC_IF_VERBOSE | SC_EXIT_ERR,
1477                             "bad magic %x", ld.ld_magic);
1478 
1479                 if (dat.len >= DUMP_LOGSIZE)
1480                         logprint(SC_SL_ERR | SC_IF_VERBOSE | SC_EXIT_ERR,
1481                             "bad size %d", ld.ld_msgsize);
1482 
1483                 Pread(dumpfd, ctl.buf, ctl.len, dumpoff);
1484                 dumpoff += ctl.len;
1485 
1486                 if (ld.ld_csum != checksum32(ctl.buf, ctl.len))
1487                         logprint(SC_SL_ERR | SC_IF_VERBOSE | SC_EXIT_OK,
1488                             "bad log_ctl checksum");
1489 
1490                 lc.flags |= SL_LOGONLY;
1491 
1492                 Pread(dumpfd, dat.buf, dat.len, dumpoff);
1493                 dumpoff += dat.len;
1494 
1495                 if (ld.ld_msum != checksum32(dat.buf, dat.len))
1496                         logprint(SC_SL_ERR | SC_IF_VERBOSE | SC_EXIT_OK,
1497                             "bad message checksum");
1498 
1499                 if (putpmsg(logfd, &ctl, &dat, 1, MSG_BAND) == -1)
1500                         logprint(SC_SL_ERR | SC_EXIT_ERR, "putpmsg: %s",
1501                             strerror(errno));
1502 
1503                 ld.ld_magic = 0;        /* clear magic so we never save twice */
1504                 Pwrite(dumpfd, &ld, sizeof (log_dump_t), ldoff);
1505         }
1506         return (0);
1507 }
1508 
1509 static long
1510 getbounds(const char *f)
1511 {
1512         long b = -1;
1513         const char *p = strrchr(f, '/');
1514 
1515         if (p == NULL || strncmp(p, "vmdump", 6) != 0)
1516                 p = strstr(f, "vmdump");
1517 
1518         if (p != NULL && *p == '/')
1519                 p++;
1520 
1521         (void) sscanf(p ? p : f, "vmdump.%ld", &b);
1522 
1523         return (b);
1524 }
1525 
1526 static void
1527 stack_retrieve(char *stack)
1528 {
1529         summary_dump_t sd;
1530         offset_t dumpoff = -(DUMP_OFFSET + DUMP_LOGSIZE +
1531             DUMP_ERPTSIZE);
1532         dumpoff -= DUMP_SUMMARYSIZE;
1533 
1534         dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644);
1535         dumpoff = llseek(dumpfd, dumpoff, SEEK_END) & -DUMP_OFFSET;
1536 
1537         Pread(dumpfd, &sd, sizeof (summary_dump_t), dumpoff);
1538         dumpoff += sizeof (summary_dump_t);
1539 
1540         if (sd.sd_magic == 0) {
1541                 *stack = '\0';
1542                 return;
1543         }
1544 
1545         if (sd.sd_magic != SUMMARY_MAGIC) {
1546                 *stack = '\0';
1547                 logprint(SC_SL_NONE | SC_IF_VERBOSE,
1548                     "bad summary magic %x", sd.sd_magic);
1549                 return;
1550         }
1551         Pread(dumpfd, stack, STACK_BUF_SIZE, dumpoff);
1552         if (sd.sd_ssum != checksum32(stack, STACK_BUF_SIZE))
1553                 logprint(SC_SL_NONE | SC_IF_VERBOSE, "bad stack checksum");
1554 }
1555 
1556 static void
1557 raise_event(enum sc_event_type evidx, char *warn_string)
1558 {
1559         uint32_t pl = sc_event[evidx].sce_payload;
1560         char panic_stack[STACK_BUF_SIZE];
1561         nvlist_t *attr = NULL;
1562         char uuidbuf[36 + 1];
1563         int err = 0;
1564 
1565         if (nvlist_alloc(&attr, NV_UNIQUE_NAME, 0) != 0)
1566                 goto publish;   /* try to send payload-free event */
1567 
1568         if (pl & SC_PAYLOAD_SAVEDIR && savedir != NULL)
1569                 err |= nvlist_add_string(attr, "dumpdir", savedir);
1570 
1571         if (pl & SC_PAYLOAD_INSTANCE && bounds != -1)
1572                 err |= nvlist_add_int64(attr, "instance", bounds);
1573 
1574         if (pl & SC_PAYLOAD_ISCOMPRESSED) {
1575                 err |= nvlist_add_boolean_value(attr, "compressed",
1576                     csave ? B_TRUE : B_FALSE);
1577         }
1578 
1579         if (pl & SC_PAYLOAD_DUMPADM_EN) {
1580                 char *disabled = defread("DUMPADM_ENABLE=no");
1581 
1582                 err |= nvlist_add_boolean_value(attr, "savecore-enabled",
1583                     disabled ? B_FALSE : B_TRUE);
1584         }
1585 
1586         if (pl & SC_PAYLOAD_IMAGEUUID) {
1587                 (void) strncpy(uuidbuf, corehdr.dump_uuid, 36);
1588                 uuidbuf[36] = '\0';
1589                 err |= nvlist_add_string(attr, "os-instance-uuid", uuidbuf);
1590         }
1591 
1592         if (pl & SC_PAYLOAD_CRASHTIME) {
1593                 err |= nvlist_add_int64(attr, "crashtime",
1594                     (int64_t)corehdr.dump_crashtime);
1595         }
1596 
1597         if (pl & SC_PAYLOAD_PANICSTR && corehdr.dump_panicstring[0] != '\0') {
1598                 err |= nvlist_add_string(attr, "panicstr",
1599                     corehdr.dump_panicstring);
1600         }
1601 
1602         if (pl & SC_PAYLOAD_PANICSTACK) {
1603                 stack_retrieve(panic_stack);
1604 
1605                 if (panic_stack[0] != '\0') {
1606                         /*
1607                          * The summary page may not be present if the dump
1608                          * was previously recorded compressed.
1609                          */
1610                         (void) nvlist_add_string(attr, "panicstack",
1611                             panic_stack);
1612                 }
1613         }
1614 
1615         /* add warning string if this is an ireport for dump failure */
1616         if (pl & SC_PAYLOAD_FAILREASON && warn_string != NULL)
1617                 (void) nvlist_add_string(attr, "failure-reason", warn_string);
1618 
1619         if (pl & SC_PAYLOAD_DUMPCOMPLETE)
1620                 err |= nvlist_add_boolean_value(attr, "dump-incomplete",
1621                     dump_incomplete ? B_TRUE : B_FALSE);
1622 
1623         if (pl & SC_PAYLOAD_FM_PANIC) {
1624                 err |= nvlist_add_boolean_value(attr, "fm-panic",
1625                     fm_panic ? B_TRUE : B_FALSE);
1626         }
1627 
1628         if (pl & SC_PAYLOAD_JUSTCHECKING) {
1629                 err |= nvlist_add_boolean_value(attr, "will-attempt-savecore",
1630                     cflag ? B_FALSE : B_TRUE);
1631         }
1632 
1633         if (err)
1634                 logprint(SC_SL_WARN, "Errors while constructing '%s' "
1635                     "event payload; will try to publish anyway.");
1636 publish:
1637         if (fmev_rspublish_nvl(FMEV_RULESET_ON_SUNOS,
1638             "panic", sc_event[evidx].sce_subclass, FMEV_HIPRI,
1639             attr) != FMEV_SUCCESS) {
1640                 logprint(SC_SL_ERR, "failed to publish '%s' event: %s",
1641                     sc_event[evidx].sce_subclass, fmev_strerror(fmev_errno));
1642                 nvlist_free(attr);
1643         }
1644 
1645 }
1646 
1647 
1648 int
1649 main(int argc, char *argv[])
1650 {
1651         int i, c, bfd;
1652         Stat_t st;
1653         struct rlimit rl;
1654         long filebounds = -1;
1655         char namelist[30], corefile[30], boundstr[30];
1656         dumpfile = NULL;
1657 
1658         startts = gethrtime();
1659 
1660         (void) getrlimit(RLIMIT_NOFILE, &rl);
1661         rl.rlim_cur = rl.rlim_max;
1662         (void) setrlimit(RLIMIT_NOFILE, &rl);
1663 
1664         openlog(progname, LOG_ODELAY, LOG_AUTH);
1665 
1666         (void) defopen("/etc/dumpadm.conf");
1667         savedir = defread("DUMPADM_SAVDIR=");
1668         if (savedir != NULL)
1669                 savedir = strdup(savedir);
1670 
1671         while ((c = getopt(argc, argv, "Lvcdmf:")) != EOF) {
1672                 switch (c) {
1673                 case 'L':
1674                         livedump++;
1675                         break;
1676                 case 'v':
1677                         verbose++;
1678                         break;
1679                 case 'c':
1680                         cflag++;
1681                         break;
1682                 case 'd':
1683                         disregard_valid_flag++;
1684                         break;
1685                 case 'm':
1686                         mflag++;
1687                         break;
1688                 case 'f':
1689                         dumpfile = optarg;
1690                         filebounds = getbounds(dumpfile);
1691                         break;
1692                 case '?':
1693                         usage();
1694                 }
1695         }
1696 
1697         /*
1698          * If doing something other than extracting an existing dump (i.e.
1699          * dumpfile has been provided as an option), the user must be root.
1700          */
1701         if (geteuid() != 0 && dumpfile == NULL) {
1702                 (void) fprintf(stderr, "%s: %s %s\n", progname,
1703                     gettext("you must be root to use"), progname);
1704                 exit(1);
1705         }
1706 
1707         interactive = isatty(STDOUT_FILENO);
1708 
1709         if (cflag && livedump)
1710                 usage();
1711 
1712         if (dumpfile == NULL || livedump)
1713                 dumpfd = Open("/dev/dump", O_RDONLY, 0444);
1714 
1715         if (dumpfile == NULL) {
1716                 dumpfile = Zalloc(MAXPATHLEN);
1717                 if (ioctl(dumpfd, DIOCGETDEV, dumpfile) == -1) {
1718                         have_dumpfile = B_FALSE;
1719                         logprint(SC_SL_NONE | SC_IF_ISATTY | SC_EXIT_ERR,
1720                             "no dump device configured");
1721                 }
1722         }
1723 
1724         if (mflag)
1725                 return (message_save());
1726 
1727         if (optind == argc - 1)
1728                 savedir = argv[optind];
1729 
1730         if (savedir == NULL || optind < argc - 1)
1731                 usage();
1732 
1733         if (livedump && ioctl(dumpfd, DIOCDUMP, NULL) == -1)
1734                 logprint(SC_SL_NONE | SC_EXIT_ERR,
1735                     "dedicated dump device required");
1736 
1737         (void) close(dumpfd);
1738         dumpfd = -1;
1739 
1740         Stat(dumpfile, &st);
1741 
1742         filemode = S_ISREG(st.st_mode);
1743 
1744         if (!filemode && defread("DUMPADM_CSAVE=off") == NULL)
1745                 csave = 1;
1746 
1747         read_dumphdr();
1748 
1749         /*
1750          * We want this message to go to the log file, but not the console.
1751          * There's no good way to do that with the existing syslog facility.
1752          * We could extend it to handle this, but there doesn't seem to be
1753          * a general need for it, so we isolate the complexity here instead.
1754          */
1755         if (dumphdr.dump_panicstring[0] != '\0') {
1756                 int logfd = Open("/dev/conslog", O_WRONLY, 0644);
1757                 log_ctl_t lc;
1758                 struct strbuf ctl, dat;
1759                 char msg[DUMP_PANICSIZE + 100];
1760                 char fmt[] = "reboot after panic: %s";
1761                 uint32_t msgid;
1762 
1763                 STRLOG_MAKE_MSGID(fmt, msgid);
1764 
1765                 /* LINTED: E_SEC_SPRINTF_UNBOUNDED_COPY */
1766                 (void) sprintf(msg, "%s: [ID %u FACILITY_AND_PRIORITY] ",
1767                     progname, msgid);
1768                 /* LINTED: E_SEC_PRINTF_VAR_FMT */
1769                 (void) sprintf(msg + strlen(msg), fmt,
1770                     dumphdr.dump_panicstring);
1771 
1772                 lc.pri = LOG_AUTH | LOG_ERR;
1773                 lc.flags = SL_CONSOLE | SL_LOGONLY;
1774                 lc.level = 0;
1775 
1776                 ctl.buf = (void *)&lc;
1777                 ctl.len = sizeof (log_ctl_t);
1778 
1779                 dat.buf = (void *)msg;
1780                 dat.len = strlen(msg) + 1;
1781 
1782                 (void) putmsg(logfd, &ctl, &dat, 0);
1783                 (void) close(logfd);
1784         }
1785 
1786         if ((dumphdr.dump_flags & DF_COMPLETE) == 0) {
1787                 logprint(SC_SL_WARN, "incomplete dump on dump device");
1788                 dump_incomplete = B_TRUE;
1789         }
1790 
1791         if (dumphdr.dump_fm_panic)
1792                 fm_panic = B_TRUE;
1793 
1794         /*
1795          * We have a valid dump on a dump device and know as much about
1796          * it as we're going to at this stage.  Raise an event for
1797          * logging and so that FMA can open a case for this panic.
1798          * Avoid this step for FMA-initiated panics - FMA will replay
1799          * ereports off the dump device independently of savecore and
1800          * will make a diagnosis, so we don't want to open two cases
1801          * for the same event.  Also avoid raising an event for a
1802          * livedump, or when we inflating a compressed dump.
1803          */
1804         if (!fm_panic && !livedump && !filemode)
1805                 raise_event(SC_EVENT_DUMP_PENDING, NULL);
1806 
1807         logprint(SC_SL_WARN, "System dump time: %s",
1808             ctime(&dumphdr.dump_crashtime));
1809 
1810         /*
1811          * Option -c is designed for use from svc-dumpadm where we know
1812          * that dumpadm -n is in effect but run savecore -c just to
1813          * get the above dump_pending_on_device event raised.  If it is run
1814          * interactively then just print further panic details.
1815          */
1816         if (cflag) {
1817                 char *disabled = defread("DUMPADM_ENABLE=no");
1818                 int lvl = interactive ? SC_SL_WARN : SC_SL_ERR;
1819                 int ec = fm_panic ? SC_EXIT_FM : SC_EXIT_PEND;
1820 
1821                 logprint(lvl | ec,
1822                     "Panic crashdump pending on dump device%s "
1823                     "run savecore(1M) manually to extract. "
1824                     "Image UUID %s%s.",
1825                     disabled ? " but dumpadm -n in effect;" : ";",
1826                     corehdr.dump_uuid,
1827                     fm_panic ?  "(fault-management initiated)" : "");
1828                 /*NOTREACHED*/
1829         }
1830 
1831         if (chdir(savedir) == -1)
1832                 logprint(SC_SL_ERR | SC_EXIT_ERR, "chdir(\"%s\"): %s",
1833                     savedir, strerror(errno));
1834 
1835         check_space(csave);
1836 
1837         if (filebounds < 0)
1838                 bounds = read_number_from_file("bounds", 0);
1839         else
1840                 bounds = filebounds;
1841 
1842         if (csave) {
1843                 size_t metrics_size = datahdr.dump_metrics;
1844 
1845                 (void) sprintf(corefile, "vmdump.%ld", bounds);
1846 
1847                 datahdr.dump_metrics = 0;
1848 
1849                 logprint(SC_SL_ERR,
1850                     "Saving compressed system crash dump in %s/%s",
1851                     savedir, corefile);
1852 
1853                 copy_crashfile(corefile);
1854 
1855                 /*
1856                  * Raise a fault management event that indicates the system
1857                  * has panicked. We know a reasonable amount about the
1858                  * condition at this time, but the dump is still compressed.
1859                  */
1860                 if (!livedump && !fm_panic)
1861                         raise_event(SC_EVENT_DUMP_AVAILABLE, NULL);
1862 
1863                 if (metrics_size > 0) {
1864                         int sec = (gethrtime() - startts) / 1000 / 1000 / 1000;
1865                         FILE *mfile = fopen(METRICSFILE, "a");
1866                         char *metrics = Zalloc(metrics_size + 1);
1867 
1868                         Pread(dumpfd, metrics, metrics_size, endoff +
1869                             sizeof (dumphdr) + sizeof (datahdr));
1870 
1871                         if (sec < 1)
1872                                 sec = 1;
1873 
1874                         if (mfile == NULL) {
1875                                 logprint(SC_SL_WARN,
1876                                     "Can't create %s:\n%s",
1877                                     METRICSFILE, metrics);
1878                         } else {
1879                                 (void) fprintf(mfile, "[[[[,,,");
1880                                 for (i = 0; i < argc; i++)
1881                                         (void) fprintf(mfile, "%s ", argv[i]);
1882                                 (void) fprintf(mfile, "\n");
1883                                 (void) fprintf(mfile, ",,,%s %s %s %s %s\n",
1884                                     dumphdr.dump_utsname.sysname,
1885                                     dumphdr.dump_utsname.nodename,
1886                                     dumphdr.dump_utsname.release,
1887                                     dumphdr.dump_utsname.version,
1888                                     dumphdr.dump_utsname.machine);
1889                                 (void) fprintf(mfile, ",,,%s dump time %s\n",
1890                                     dumphdr.dump_flags & DF_LIVE ? "Live" :
1891                                     "Crash", ctime(&dumphdr.dump_crashtime));
1892                                 (void) fprintf(mfile, ",,,%s/%s\n", savedir,
1893                                     corefile);
1894                                 (void) fprintf(mfile, "Metrics:\n%s\n",
1895                                     metrics);
1896                                 (void) fprintf(mfile, "Copy pages,%ld\n",
1897                                     dumphdr.  dump_npages);
1898                                 (void) fprintf(mfile, "Copy time,%d\n", sec);
1899                                 (void) fprintf(mfile, "Copy pages/sec,%ld\n",
1900                                     dumphdr.dump_npages / sec);
1901                                 (void) fprintf(mfile, "]]]]\n");
1902                                 (void) fclose(mfile);
1903                         }
1904                         free(metrics);
1905                 }
1906 
1907                 logprint(SC_SL_ERR,
1908                     "Decompress the crash dump with "
1909                     "\n'savecore -vf %s/%s'",
1910                     savedir, corefile);
1911 
1912         } else {
1913                 (void) sprintf(namelist, "unix.%ld", bounds);
1914                 (void) sprintf(corefile, "vmcore.%ld", bounds);
1915 
1916                 if (interactive && filebounds >= 0 && access(corefile, F_OK)
1917                     == 0)
1918                         logprint(SC_SL_NONE | SC_EXIT_ERR,
1919                             "%s already exists: remove with "
1920                             "'rm -f %s/{unix,vmcore}.%ld'",
1921                             corefile, savedir, bounds);
1922 
1923                 logprint(SC_SL_ERR,
1924                     "saving system crash dump in %s/{unix,vmcore}.%ld",
1925                     savedir, bounds);
1926 
1927                 build_corefile(namelist, corefile);
1928 
1929                 if (!livedump && !filemode && !fm_panic)
1930                         raise_event(SC_EVENT_DUMP_AVAILABLE, NULL);
1931 
1932                 if (access(METRICSFILE, F_OK) == 0) {
1933                         int sec = (gethrtime() - startts) / 1000 / 1000 / 1000;
1934                         FILE *mfile = fopen(METRICSFILE, "a");
1935 
1936                         if (sec < 1)
1937                                 sec = 1;
1938 
1939                         if (mfile == NULL) {
1940                                 logprint(SC_SL_WARN,
1941                                     "Can't create %s: %s",
1942                                     METRICSFILE, strerror(errno));
1943                         } else {
1944                                 (void) fprintf(mfile, "[[[[,,,");
1945                                 for (i = 0; i < argc; i++)
1946                                         (void) fprintf(mfile, "%s ", argv[i]);
1947                                 (void) fprintf(mfile, "\n");
1948                                 (void) fprintf(mfile, ",,,%s/%s\n", savedir,
1949                                     corefile);
1950                                 (void) fprintf(mfile, ",,,%s %s %s %s %s\n",
1951                                     dumphdr.dump_utsname.sysname,
1952                                     dumphdr.dump_utsname.nodename,
1953                                     dumphdr.dump_utsname.release,
1954                                     dumphdr.dump_utsname.version,
1955                                     dumphdr.dump_utsname.machine);
1956                                 (void) fprintf(mfile,
1957                                     "Uncompress pages,%"PRIu64"\n", saved);
1958                                 (void) fprintf(mfile, "Uncompress time,%d\n",
1959                                     sec);
1960                                 (void) fprintf(mfile, "Uncompress pages/sec,%"
1961                                     PRIu64"\n", saved / sec);
1962                                 (void) fprintf(mfile, "]]]]\n");
1963                                 (void) fclose(mfile);
1964                         }
1965                 }
1966         }
1967 
1968         if (filebounds < 0) {
1969                 (void) sprintf(boundstr, "%ld\n", bounds + 1);
1970                 bfd = Open("bounds", O_WRONLY | O_CREAT | O_TRUNC, 0644);
1971                 Pwrite(bfd, boundstr, strlen(boundstr), 0);
1972                 (void) close(bfd);
1973         }
1974 
1975         if (verbose) {
1976                 int sec = (gethrtime() - startts) / 1000 / 1000 / 1000;
1977 
1978                 (void) printf("%d:%02d dump %s is done\n",
1979                     sec / 60, sec % 60,
1980                     csave ? "copy" : "decompress");
1981         }
1982 
1983         if (verbose > 1 && hist != NULL) {
1984                 int i, nw;
1985 
1986                 for (i = 1, nw = 0; i <= BTOP(coreblksize); ++i)
1987                         nw += hist[i] * i;
1988                 (void) printf("pages count     %%\n");
1989                 for (i = 0; i <= BTOP(coreblksize); ++i) {
1990                         if (hist[i] == 0)
1991                                 continue;
1992                         (void) printf("%3d   %5u  %6.2f\n",
1993                             i, hist[i], 100.0 * hist[i] * i / nw);
1994                 }
1995         }
1996 
1997         (void) close(dumpfd);
1998         dumpfd = -1;
1999 
2000         return (0);
2001 }