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