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