Print this page
10654 savecore(1M) should be able to work on read-only dump devices
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: John Levon <john.levon@joyent.com>
Reviewed by: Andy Stormont <astormont@racktopsystems.com>
Reviewed by: Gergő Doma <domag02@gmail.com>
Reviewed by: Toomas Soome <tsoome@me.com>

Split Close
Expand all
Collapse all
          --- old/usr/src/cmd/savecore/savecore.c
          +++ new/usr/src/cmd/savecore/savecore.c
↓ open down ↓ 12 lines elided ↑ open up ↑
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  /*
  22   22   * Copyright (c) 1983, 2010, Oracle and/or its affiliates. All rights reserved.
  23      - * Copyright 2016 Joyent, Inc.
       23 + * Copyright 2019 Joyent, Inc.
  24   24   */
  25   25  /*
  26   26   * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
  27   27   */
  28   28  
  29   29  #include <stdio.h>
  30   30  #include <stdlib.h>
  31   31  #include <stdarg.h>
  32   32  #include <unistd.h>
  33   33  #include <fcntl.h>
↓ open down ↓ 51 lines elided ↑ open up ↑
  85   85  static int      filemode;               /* processing file, not dump device */
  86   86  static int      percent_done;           /* progress indicator */
  87   87  static int      sec_done;               /* progress last report time */
  88   88  static hrtime_t startts;                /* timestamp at start */
  89   89  static volatile uint64_t saved;         /* count of pages written */
  90   90  static volatile uint64_t zpages;        /* count of zero pages not written */
  91   91  static dumpdatahdr_t datahdr;           /* compression info */
  92   92  static long     coreblksize;            /* preferred write size (st_blksize) */
  93   93  static int      cflag;                  /* run as savecore -c */
  94   94  static int      mflag;                  /* run as savecore -m */
       95 +static int      rflag;                  /* run as savecore -r */
  95   96  
  96   97  /*
  97   98   * Payload information for the events we raise.  These are used
  98   99   * in raise_event to determine what payload to include.
  99  100   */
 100  101  #define SC_PAYLOAD_SAVEDIR      0x0001  /* Include savedir in event */
 101  102  #define SC_PAYLOAD_INSTANCE     0x0002  /* Include bounds instance number */
 102  103  #define SC_PAYLOAD_IMAGEUUID    0x0004  /* Include dump OS instance uuid */
 103  104  #define SC_PAYLOAD_CRASHTIME    0x0008  /* Include epoch crashtime */
 104  105  #define SC_PAYLOAD_PANICSTR     0x0010  /* Include panic string */
↓ open down ↓ 52 lines elided ↑ open up ↑
 157  158                  _SC_PAYLOAD_CMN | SC_PAYLOAD_INSTANCE | SC_PAYLOAD_ISCOMPRESSED
 158  159          },
 159  160  };
 160  161  
 161  162  static void raise_event(enum sc_event_type, char *);
 162  163  
 163  164  static void
 164  165  usage(void)
 165  166  {
 166  167          (void) fprintf(stderr,
 167      -            "usage: %s [-Lvd] [-f dumpfile] [dirname]\n", progname);
      168 +            "usage: %s [-L | -r] [-vd] [-f dumpfile] [dirname]\n", progname);
 168  169          exit(1);
 169  170  }
 170  171  
 171  172  #define SC_SL_NONE      0x0001  /* no syslog */
 172  173  #define SC_SL_ERR       0x0002  /* syslog if !interactive, LOG_ERR */
 173  174  #define SC_SL_WARN      0x0004  /* syslog if !interactive, LOG_WARNING */
 174  175  #define SC_IF_VERBOSE   0x0008  /* message only if -v */
 175  176  #define SC_IF_ISATTY    0x0010  /* message only if interactive */
 176  177  #define SC_EXIT_OK      0x0020  /* exit(0) */
 177  178  #define SC_EXIT_ERR     0x0040  /* exit(1) */
↓ open down ↓ 44 lines elided ↑ open up ↑
 222  223          case SC_EXIT_OK:
 223  224                  code = 0;
 224  225                  break;
 225  226  
 226  227          case SC_EXIT_PEND:
 227  228                  /*
 228  229                   * Raise an ireport saying why we are exiting.  Do not
 229  230                   * raise if run as savecore -m.  If something in the
 230  231                   * raise_event codepath calls logprint avoid recursion.
 231  232                   */
 232      -                if (!mflag && logprint_raised++ == 0)
      233 +                if (!mflag && !rflag && logprint_raised++ == 0)
 233  234                          raise_event(SC_EVENT_SAVECORE_FAILURE, buf);
 234  235                  code = 2;
 235  236                  break;
 236  237  
 237  238          case SC_EXIT_FM:
 238  239                  code = 3;
 239  240                  break;
 240  241  
 241  242          case SC_EXIT_ERR:
 242  243          default:
 243      -                if (!mflag && logprint_raised++ == 0 && have_dumpfile)
      244 +                if (!mflag && !rflag && logprint_raised++ == 0 && have_dumpfile)
 244  245                          raise_event(SC_EVENT_SAVECORE_FAILURE, buf);
 245  246                  code = 1;
 246  247                  break;
 247  248          }
 248  249  
 249  250          exit(code);
 250  251  }
 251  252  
 252  253  /*
 253  254   * System call / libc wrappers that exit on error.
↓ open down ↓ 94 lines elided ↑ open up ↑
 348  349          if ((fp = fopen(filename, "r")) != NULL) {
 349  350                  (void) fscanf(fp, "%ld", &file_value);
 350  351                  (void) fclose(fp);
 351  352          }
 352  353          return (file_value < 0 ? default_value : file_value);
 353  354  }
 354  355  
 355  356  static void
 356  357  read_dumphdr(void)
 357  358  {
 358      -        if (filemode)
      359 +        if (filemode || rflag)
 359  360                  dumpfd = Open(dumpfile, O_RDONLY, 0644);
 360  361          else
 361  362                  dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644);
 362  363          endoff = llseek(dumpfd, -DUMP_OFFSET, SEEK_END) & -DUMP_OFFSET;
 363  364          Pread(dumpfd, &dumphdr, sizeof (dumphdr), endoff);
 364  365          Pread(dumpfd, &datahdr, sizeof (datahdr), endoff + sizeof (dumphdr));
 365  366  
 366  367          pagesize = dumphdr.dump_pagesize;
 367  368  
 368  369          if (dumphdr.dump_magic != DUMP_MAGIC)
↓ open down ↓ 32 lines elided ↑ open up ↑
 401  402           */
 402  403          Pread(dumpfd, &corehdr, sizeof (dumphdr_t), dumphdr.dump_start);
 403  404  
 404  405          corehdr.dump_flags &= ~DF_VALID;
 405  406          dumphdr.dump_flags &= ~DF_VALID;
 406  407  
 407  408          if (memcmp(&corehdr, &dumphdr, sizeof (dumphdr_t)) != 0) {
 408  409                  /*
 409  410                   * Clear valid bit so we don't complain on every invocation.
 410  411                   */
 411      -                if (!filemode)
      412 +                if (!filemode && !rflag)
 412  413                          Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff);
 413  414                  logprint(SC_SL_ERR | SC_EXIT_ERR,
 414  415                      "initial dump header corrupt");
 415  416          }
 416  417  }
 417  418  
 418  419  static void
 419  420  check_space(int csave)
 420  421  {
 421  422          struct statvfs fsb;
↓ open down ↓ 231 lines elided ↑ open up ↑
 653  654                  Pwrite(corefd, inbuf, nb, coreoff);
 654  655          }
 655  656  
 656  657          free(inbuf);
 657  658          Pwrite(corefd, &corehdr, sizeof (corehdr), corehdr.dump_start);
 658  659  
 659  660          /*
 660  661           * Write out the modified dump header to the dump device.
 661  662           * The dump device has been processed, so DF_VALID is clear.
 662  663           */
 663      -        if (!filemode)
      664 +        if (!filemode && !rflag)
 664  665                  Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff);
 665  666  
 666  667          (void) close(corefd);
 667  668  }
 668  669  
 669  670  /*
 670  671   * compressed streams
 671  672   */
 672  673  typedef struct blockhdr blockhdr_t;
 673  674  typedef struct block block_t;
↓ open down ↓ 741 lines elided ↑ open up ↑
1415 1416                      (pgcnt_t)zpages, (pgcnt_t)zpages * 100 /
1416 1417                      dumphdr.dump_npages);
1417 1418  
1418 1419          if (saved != dumphdr.dump_npages)
1419 1420                  logprint(SC_SL_WARN, "bad data after page %ld", saved);
1420 1421  
1421 1422          /*
1422 1423           * Write out the modified dump headers.
1423 1424           */
1424 1425          Pwrite(corefd, &corehdr, sizeof (corehdr), 0);
1425      -        if (!filemode)
     1426 +        if (!filemode && !rflag)
1426 1427                  Pwrite(dumpfd, &dumphdr, sizeof (dumphdr), endoff);
1427 1428  
1428 1429          (void) close(corefd);
1429 1430  }
1430 1431  
1431 1432  /*
1432 1433   * When the system panics, the kernel saves all undelivered messages (messages
1433 1434   * that never made it out to syslogd(1M)) in the dump.  At a mimimum, the
1434 1435   * panic message itself will always fall into this category.  Upon reboot,
1435 1436   * the syslog startup script runs savecore -m to recover these messages.
↓ open down ↓ 88 lines elided ↑ open up ↑
1524 1525  }
1525 1526  
1526 1527  static void
1527 1528  stack_retrieve(char *stack)
1528 1529  {
1529 1530          summary_dump_t sd;
1530 1531          offset_t dumpoff = -(DUMP_OFFSET + DUMP_LOGSIZE +
1531 1532              DUMP_ERPTSIZE);
1532 1533          dumpoff -= DUMP_SUMMARYSIZE;
1533 1534  
1534      -        dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644);
     1535 +        if (rflag)
     1536 +                dumpfd = Open(dumpfile, O_RDONLY, 0644);
     1537 +        else
     1538 +                dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644);
1535 1539          dumpoff = llseek(dumpfd, dumpoff, SEEK_END) & -DUMP_OFFSET;
1536 1540  
1537 1541          Pread(dumpfd, &sd, sizeof (summary_dump_t), dumpoff);
1538 1542          dumpoff += sizeof (summary_dump_t);
1539 1543  
1540 1544          if (sd.sd_magic == 0) {
1541 1545                  *stack = '\0';
1542 1546                  return;
1543 1547          }
1544 1548  
↓ open down ↓ 116 lines elided ↑ open up ↑
1661 1665          rl.rlim_cur = rl.rlim_max;
1662 1666          (void) setrlimit(RLIMIT_NOFILE, &rl);
1663 1667  
1664 1668          openlog(progname, LOG_ODELAY, LOG_AUTH);
1665 1669  
1666 1670          (void) defopen("/etc/dumpadm.conf");
1667 1671          savedir = defread("DUMPADM_SAVDIR=");
1668 1672          if (savedir != NULL)
1669 1673                  savedir = strdup(savedir);
1670 1674  
1671      -        while ((c = getopt(argc, argv, "Lvcdmf:")) != EOF) {
     1675 +        while ((c = getopt(argc, argv, "Lvcdmf:r")) != EOF) {
1672 1676                  switch (c) {
1673 1677                  case 'L':
1674 1678                          livedump++;
1675 1679                          break;
1676 1680                  case 'v':
1677 1681                          verbose++;
1678 1682                          break;
1679 1683                  case 'c':
1680 1684                          cflag++;
1681 1685                          break;
1682 1686                  case 'd':
1683 1687                          disregard_valid_flag++;
1684 1688                          break;
1685 1689                  case 'm':
1686 1690                          mflag++;
1687 1691                          break;
     1692 +                case 'r':
     1693 +                        rflag++;
     1694 +                        break;
1688 1695                  case 'f':
1689 1696                          dumpfile = optarg;
1690 1697                          filebounds = getbounds(dumpfile);
1691 1698                          break;
1692 1699                  case '?':
1693 1700                          usage();
1694 1701                  }
1695 1702          }
1696 1703  
1697 1704          /*
↓ open down ↓ 4 lines elided ↑ open up ↑
1702 1709                  (void) fprintf(stderr, "%s: %s %s\n", progname,
1703 1710                      gettext("you must be root to use"), progname);
1704 1711                  exit(1);
1705 1712          }
1706 1713  
1707 1714          interactive = isatty(STDOUT_FILENO);
1708 1715  
1709 1716          if (cflag && livedump)
1710 1717                  usage();
1711 1718  
     1719 +        if (rflag && (cflag || mflag || livedump))
     1720 +                usage();
     1721 +
1712 1722          if (dumpfile == NULL || livedump)
1713 1723                  dumpfd = Open("/dev/dump", O_RDONLY, 0444);
1714 1724  
1715 1725          if (dumpfile == NULL) {
1716 1726                  dumpfile = Zalloc(MAXPATHLEN);
1717 1727                  if (ioctl(dumpfd, DIOCGETDEV, dumpfile) == -1) {
1718 1728                          have_dumpfile = B_FALSE;
1719 1729                          logprint(SC_SL_NONE | SC_IF_ISATTY | SC_EXIT_ERR,
1720 1730                              "no dump device configured");
1721 1731                  }
↓ open down ↓ 23 lines elided ↑ open up ↑
1745 1755                  csave = 1;
1746 1756  
1747 1757          read_dumphdr();
1748 1758  
1749 1759          /*
1750 1760           * We want this message to go to the log file, but not the console.
1751 1761           * There's no good way to do that with the existing syslog facility.
1752 1762           * We could extend it to handle this, but there doesn't seem to be
1753 1763           * a general need for it, so we isolate the complexity here instead.
1754 1764           */
1755      -        if (dumphdr.dump_panicstring[0] != '\0') {
     1765 +        if (dumphdr.dump_panicstring[0] != '\0' && !rflag) {
1756 1766                  int logfd = Open("/dev/conslog", O_WRONLY, 0644);
1757 1767                  log_ctl_t lc;
1758 1768                  struct strbuf ctl, dat;
1759 1769                  char msg[DUMP_PANICSIZE + 100];
1760 1770                  char fmt[] = "reboot after panic: %s";
1761 1771                  uint32_t msgid;
1762 1772  
1763 1773                  STRLOG_MAKE_MSGID(fmt, msgid);
1764 1774  
1765 1775                  /* LINTED: E_SEC_SPRINTF_UNBOUNDED_COPY */
↓ open down ↓ 28 lines elided ↑ open up ↑
1794 1804          /*
1795 1805           * We have a valid dump on a dump device and know as much about
1796 1806           * it as we're going to at this stage.  Raise an event for
1797 1807           * logging and so that FMA can open a case for this panic.
1798 1808           * Avoid this step for FMA-initiated panics - FMA will replay
1799 1809           * ereports off the dump device independently of savecore and
1800 1810           * will make a diagnosis, so we don't want to open two cases
1801 1811           * for the same event.  Also avoid raising an event for a
1802 1812           * livedump, or when we inflating a compressed dump.
1803 1813           */
1804      -        if (!fm_panic && !livedump && !filemode)
     1814 +        if (!fm_panic && !livedump && !filemode && !rflag)
1805 1815                  raise_event(SC_EVENT_DUMP_PENDING, NULL);
1806 1816  
1807 1817          logprint(SC_SL_WARN, "System dump time: %s",
1808 1818              ctime(&dumphdr.dump_crashtime));
1809 1819  
1810 1820          /*
1811 1821           * Option -c is designed for use from svc-dumpadm where we know
1812 1822           * that dumpadm -n is in effect but run savecore -c just to
1813 1823           * get the above dump_pending_on_device event raised.  If it is run
1814 1824           * interactively then just print further panic details.
↓ open down ↓ 35 lines elided ↑ open up ↑
1850 1860                      "Saving compressed system crash dump in %s/%s",
1851 1861                      savedir, corefile);
1852 1862  
1853 1863                  copy_crashfile(corefile);
1854 1864  
1855 1865                  /*
1856 1866                   * Raise a fault management event that indicates the system
1857 1867                   * has panicked. We know a reasonable amount about the
1858 1868                   * condition at this time, but the dump is still compressed.
1859 1869                   */
1860      -                if (!livedump && !fm_panic)
     1870 +                if (!livedump && !fm_panic && !rflag)
1861 1871                          raise_event(SC_EVENT_DUMP_AVAILABLE, NULL);
1862 1872  
1863 1873                  if (metrics_size > 0) {
1864 1874                          int sec = (gethrtime() - startts) / 1000 / 1000 / 1000;
1865 1875                          FILE *mfile = fopen(METRICSFILE, "a");
1866 1876                          char *metrics = Zalloc(metrics_size + 1);
1867 1877  
1868 1878                          Pread(dumpfd, metrics, metrics_size, endoff +
1869 1879                              sizeof (dumphdr) + sizeof (datahdr));
1870 1880  
↓ open down ↓ 48 lines elided ↑ open up ↑
1919 1929                              "%s already exists: remove with "
1920 1930                              "'rm -f %s/{unix,vmcore}.%ld'",
1921 1931                              corefile, savedir, bounds);
1922 1932  
1923 1933                  logprint(SC_SL_ERR,
1924 1934                      "saving system crash dump in %s/{unix,vmcore}.%ld",
1925 1935                      savedir, bounds);
1926 1936  
1927 1937                  build_corefile(namelist, corefile);
1928 1938  
1929      -                if (!livedump && !filemode && !fm_panic)
     1939 +                if (!livedump && !filemode && !fm_panic && !rflag)
1930 1940                          raise_event(SC_EVENT_DUMP_AVAILABLE, NULL);
1931 1941  
1932 1942                  if (access(METRICSFILE, F_OK) == 0) {
1933 1943                          int sec = (gethrtime() - startts) / 1000 / 1000 / 1000;
1934 1944                          FILE *mfile = fopen(METRICSFILE, "a");
1935 1945  
1936 1946                          if (sec < 1)
1937 1947                                  sec = 1;
1938 1948  
1939 1949                          if (mfile == NULL) {
↓ open down ↓ 62 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX