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) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
  24  */
  25 
  26 #include <sys/stat.h>
  27 #include <locale.h>
  28 #include <unistd.h>
  29 #include <stdlib.h>
  30 #include <stdio.h>
  31 #include <string.h>
  32 
  33 #include "dconf.h"
  34 #include "minfree.h"
  35 #include "utils.h"
  36 
  37 static const char USAGE[] = "\
  38 Usage: %s [-enuy] [-c kernel | curproc | all ] [-d dump-device | swap ]\n\
  39         [-m min {k|m|%%} ] [-s savecore-dir] [-r root-dir] [-z on|off]\n";
  40 
  41 static const char OPTS[] = "einuyc:d:m:s:r:z:";
  42 
  43 static const char PATH_DEVICE[] = "/dev/dump";
  44 static const char PATH_CONFIG[] = "/etc/dumpadm.conf";
  45 
  46 int
  47 main(int argc, char *argv[])
  48 {
  49         const char *pname = getpname(argv[0]);
  50 
  51         u_longlong_t minf;
  52         struct stat st;
  53         int c;
  54         int dflag = 0;                  /* for checking in use during -d ops */
  55         int eflag = 0;                  /* print estimated dump size */
  56         int dcmode = DC_CURRENT;        /* kernel settings override unless -u */
  57         int modified = 0;               /* have we modified the dump config? */
  58         char *minfstr = NULL;           /* string value of -m argument */
  59         dumpconf_t dc;                  /* current configuration */
  60         int chrooted = 0;
  61         int douuid = 0;
  62 
  63         (void) setlocale(LC_ALL, "");
  64         (void) textdomain(TEXT_DOMAIN);
  65 
  66         /*
  67          * Take an initial lap through argv hunting for -r root-dir,
  68          * so that we can chroot before opening the configuration file.
  69          * We also handle -u and any bad options at this point.
  70          */
  71         while (optind < argc) {
  72                 while ((c = getopt(argc, argv, OPTS)) != (int)EOF) {
  73                         if (c == 'r' && ++chrooted && chroot(optarg) == -1)
  74                                 die(gettext("failed to chroot to %s"), optarg);
  75                         else if (c == 'u')
  76                                 dcmode = DC_OVERRIDE;
  77                         else if (c == '?') {
  78                                 (void) fprintf(stderr, gettext(USAGE), pname);
  79                                 return (E_USAGE);
  80                         }
  81                 }
  82 
  83                 if (optind < argc) {
  84                         warn(gettext("illegal argument -- %s\n"), argv[optind]);
  85                         (void) fprintf(stderr, gettext(USAGE), pname);
  86                         return (E_USAGE);
  87                 }
  88         }
  89 
  90         if (geteuid() != 0)
  91                 die(gettext("you must be root to use %s\n"), pname);
  92 
  93         /*
  94          * If no config file exists yet, we're going to create an empty one,
  95          * so set the modified flag to force writing out the file.
  96          */
  97         if (access(PATH_CONFIG, F_OK) == -1)
  98                 modified++;
  99 
 100         /*
 101          * Now open and read in the initial values from the config file.
 102          * If it doesn't exist, we create an empty file and dc is
 103          * initialized with the default values.
 104          */
 105         if (dconf_open(&dc, PATH_DEVICE, PATH_CONFIG, dcmode) == -1)
 106                 return (E_ERROR);
 107 
 108         /*
 109          * Take another lap through argv, processing options and
 110          * modifying the dumpconf_t as appropriate.
 111          */
 112         for (optind = 1; optind < argc; optind++) {
 113                 while ((c = getopt(argc, argv, OPTS)) != (int)EOF) {
 114                         switch (c) {
 115                         case 'c':
 116                                 if (dconf_str2content(&dc, optarg) == -1)
 117                                         return (E_USAGE);
 118                                 modified++;
 119                                 break;
 120                         case 'd':
 121                                 if (dconf_str2device(&dc, optarg) == -1)
 122                                         return (E_USAGE);
 123                                 dflag++;
 124                                 modified++;
 125                                 break;
 126                         case 'e':
 127                                 eflag++;
 128                                 break;
 129                         case 'i':
 130                                 /* undocumented option */
 131                                 if (chrooted) {
 132                                         warn(gettext("-i and -r cannot be "
 133                                             "used together\n"));
 134                                         return (E_USAGE);
 135                                 }
 136                                 douuid++;
 137                                 break;
 138 
 139                         case 'm':
 140                                 minfstr = optarg;
 141                                 break;
 142 
 143                         case 'n':
 144                                 dc.dc_enable = DC_OFF;
 145                                 modified++;
 146                                 break;
 147 
 148                         case 's':
 149                                 if (stat(optarg, &st) == -1 ||
 150                                     !S_ISDIR(st.st_mode)) {
 151                                         warn(gettext("%s is missing or not a "
 152                                             "directory\n"), optarg);
 153                                         return (E_USAGE);
 154                                 }
 155 
 156                                 if (dconf_str2savdir(&dc, optarg) == -1)
 157                                         return (E_USAGE);
 158                                 modified++;
 159                                 break;
 160 
 161                         case 'y':
 162                                 dc.dc_enable = DC_ON;
 163                                 modified++;
 164                                 break;
 165 
 166                         case 'z':
 167                                 if (dconf_str2csave(&dc, optarg) == -1)
 168                                         return (E_USAGE);
 169                                 modified++;
 170                                 break;
 171                         }
 172                 }
 173         }
 174 
 175         if (eflag) {
 176                 if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'e' &&
 177                     !argv[1][2])
 178                         return (dconf_get_dumpsize(&dc) ? E_SUCCESS : E_ERROR);
 179                 else
 180                         die(gettext("-e cannot be used with other options\n"));
 181         }
 182 
 183         if (douuid)
 184                 return (dconf_write_uuid(&dc) ? E_SUCCESS : E_ERROR);
 185 
 186         if (minfstr != NULL) {
 187                 if (minfree_compute(dc.dc_savdir, minfstr, &minf) == -1)
 188                         return (E_USAGE);
 189                 if (minfree_write(dc.dc_savdir, minf) == -1)
 190                         return (E_ERROR);
 191         }
 192 
 193         if (dcmode == DC_OVERRIDE) {
 194                 /*
 195                  * In override mode, we try to force an update.  If this
 196                  * fails, we re-load the kernel configuration and write that
 197                  * out to the file in order to force the file in sync.
 198                  *
 199                  * We allow the file to be read-only but print a warning to the
 200                  * user that indicates it hasn't been updated.
 201                  */
 202                 if (dconf_update(&dc, 0) == -1)
 203                         (void) dconf_getdev(&dc);
 204                 if (dc.dc_readonly)
 205                         warn(gettext("kernel settings updated, but "
 206                             "%s is read-only\n"), PATH_CONFIG);
 207                 else if (dconf_write(&dc) == -1)
 208                         return (E_ERROR);
 209 
 210         } else if (modified) {
 211                 /*
 212                  * If we're modifying the configuration, then try
 213                  * to update it, and write out the file if successful.
 214                  */
 215                 if (dc.dc_readonly) {
 216                         warn(gettext("failed to update settings: %s is "
 217                             "read-only\n"), PATH_CONFIG);
 218                         return (E_ERROR);
 219                 }
 220 
 221                 if (dconf_update(&dc, dflag) == -1 ||
 222                     dconf_write(&dc) == -1)
 223                         return (E_ERROR);
 224         }
 225 
 226         if (dcmode == DC_CURRENT)
 227                 dconf_print(&dc, stdout);
 228 
 229         if (dconf_close(&dc) == -1)
 230                 warn(gettext("failed to close configuration file"));
 231 
 232         return (E_SUCCESS);
 233 }