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 /*
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 
  28 /*
  29  * Wrapper function to implement reboot w/ arguments on x86
  30  * platforms. Extract reboot arguments and place them in
  31  * in a transient entry in /[stub]boot/grub/menu.lst
  32  * All other commands are passed through.
  33  */
  34 
  35 #include "lint.h"
  36 #include "mtlib.h"
  37 #include <fcntl.h>
  38 #include <ctype.h>
  39 #include <stdio.h>
  40 #include <stdlib.h>
  41 #include <sys/types.h>
  42 #include <sys/stat.h>
  43 #include <sys/uadmin.h>
  44 #include <unistd.h>
  45 #include <strings.h>
  46 #include <pthread.h>
  47 #include <zone.h>
  48 #include <libscf.h>
  49 #include <thread.h>
  50 #include <dlfcn.h>
  51 #include <atomic.h>
  52 
  53 /*
  54  * Pull in the following three interfaces from libscf without introducing
  55  * a dependency on it, which since libscf depends on libc would be circular:
  56  *
  57  * scf_simple_prop_get
  58  * scf_simple_prop_next_boolean
  59  * scf_simple_prop_free
  60  */
  61 typedef scf_simple_prop_t *(*scf_simple_prop_get_t)(scf_handle_t *,
  62     const char *, const char *, const char *);
  63 static scf_simple_prop_get_t real_scf_simple_prop_get = NULL;
  64 typedef uint8_t *(*scf_simple_prop_next_boolean_t)(scf_simple_prop_t *);
  65 static scf_simple_prop_next_boolean_t real_scf_simple_prop_next_boolean = NULL;
  66 typedef void (*scf_simple_prop_free_t)(scf_simple_prop_t *);
  67 static scf_simple_prop_free_t real_scf_simple_prop_free = NULL;
  68 static mutex_t scf_lock = DEFAULTMUTEX;
  69 
  70 static void
  71 load_scf(void)
  72 {
  73         void *scf_handle = dlopen("libscf.so.1", RTLD_LAZY);
  74         scf_simple_prop_get_t scf_simple_prop_get = (scf_handle == NULL)? NULL :
  75             (scf_simple_prop_get_t)dlsym(scf_handle, "scf_simple_prop_get");
  76         scf_simple_prop_next_boolean_t scf_simple_prop_next_boolean =
  77             (scf_handle == NULL)? NULL :
  78             (scf_simple_prop_next_boolean_t)dlsym(scf_handle,
  79             "scf_simple_prop_next_boolean");
  80         scf_simple_prop_free_t scf_simple_prop_free =
  81             (scf_handle == NULL)? NULL :
  82             (scf_simple_prop_free_t)dlsym(scf_handle, "scf_simple_prop_free");
  83 
  84         lmutex_lock(&scf_lock);
  85         if (real_scf_simple_prop_get == NULL ||
  86             real_scf_simple_prop_next_boolean == NULL ||
  87             real_scf_simple_prop_free == NULL) {
  88                 if (scf_simple_prop_get == NULL)
  89                         real_scf_simple_prop_get = (scf_simple_prop_get_t)(-1);
  90                 else {
  91                         real_scf_simple_prop_get = scf_simple_prop_get;
  92                         scf_handle = NULL;      /* don't dlclose it */
  93                 }
  94                 if (scf_simple_prop_next_boolean == NULL)
  95                         real_scf_simple_prop_next_boolean =
  96                             (scf_simple_prop_next_boolean_t)(-1);
  97                 else {
  98                         real_scf_simple_prop_next_boolean =
  99                             scf_simple_prop_next_boolean;
 100                         scf_handle = NULL;      /* don't dlclose it */
 101                 }
 102                 if (scf_simple_prop_free == NULL)
 103                         real_scf_simple_prop_free =
 104                             (scf_simple_prop_free_t)(-1);
 105                 else {
 106                         real_scf_simple_prop_free = scf_simple_prop_free;
 107                         scf_handle = NULL;      /* don't dlclose it */
 108                 }
 109                 membar_producer();
 110         }
 111         lmutex_unlock(&scf_lock);
 112 
 113         if (scf_handle)
 114                 (void) dlclose(scf_handle);
 115 }
 116 
 117 static void
 118 check_archive_update(void)
 119 {
 120         scf_simple_prop_t *prop = NULL;
 121         boolean_t update_flag = B_FALSE;
 122         char *fmri = "svc:/system/boot-config:default";
 123         uint8_t *ret_val = NULL;
 124 
 125         if (real_scf_simple_prop_get == NULL ||
 126             real_scf_simple_prop_next_boolean == NULL ||
 127             real_scf_simple_prop_free == NULL) {
 128                 load_scf();
 129         }
 130         if (real_scf_simple_prop_get == (scf_simple_prop_get_t)(-1) ||
 131             real_scf_simple_prop_next_boolean ==
 132             (scf_simple_prop_next_boolean_t)(-1) ||
 133             real_scf_simple_prop_free == (scf_simple_prop_free_t)(-1)) {
 134                 return;
 135         }
 136 
 137         prop = real_scf_simple_prop_get(NULL, fmri, "config",
 138             "uadmin_boot_archive_sync");
 139         if (prop) {
 140                 if ((ret_val = real_scf_simple_prop_next_boolean(prop)) !=
 141                     NULL)
 142                         update_flag = (*ret_val == 0) ? B_FALSE :
 143                             B_TRUE;
 144                 real_scf_simple_prop_free(prop);
 145         }
 146 
 147         if (update_flag == B_TRUE)
 148                 (void) system("/sbin/bootadm update-archive");
 149 }
 150 static int
 151 legal_arg(char *bargs)
 152 {
 153         int i;
 154 
 155         for (i = 0; i < BOOTARGS_MAX; i++, bargs++) {
 156                 if (*bargs == 0 && i > 0)
 157                         return (i);
 158                 if (!isprint(*bargs))
 159                         break;
 160         }
 161         return (-1);
 162 }
 163 
 164 static char quote[] = "\'";
 165 
 166 int
 167 uadmin(int cmd, int fcn, uintptr_t mdep)
 168 {
 169         extern int __uadmin(int cmd, int fcn, uintptr_t mdep);
 170         char *bargs, cmdbuf[256];
 171         struct stat sbuf;
 172         char *altroot;
 173 
 174         bargs = (char *)mdep;
 175 
 176         if (geteuid() == 0 && getzoneid() == GLOBAL_ZONEID &&
 177             (cmd == A_SHUTDOWN || cmd == A_REBOOT)) {
 178                 int off = 0;
 179 
 180                 switch (fcn) {
 181                 case AD_IBOOT:
 182                 case AD_SBOOT:
 183                 case AD_SIBOOT:
 184                         /*
 185                          * These functions fabricate appropriate bootargs.
 186                          * If bootargs are passed in, map these functions
 187                          * to AD_BOOT.
 188                          */
 189                         if (bargs == 0) {
 190                                 switch (fcn) {
 191                                 case AD_IBOOT:
 192                                         bargs = "-a";
 193                                         break;
 194                                 case AD_SBOOT:
 195                                         bargs = "-s";
 196                                         break;
 197                                 case AD_SIBOOT:
 198                                         bargs = "-sa";
 199                                         break;
 200                                 }
 201                         }
 202                         /*FALLTHROUGH*/
 203                 case AD_BOOT:
 204                 case AD_FASTREBOOT:
 205                         if (bargs == 0)
 206                                 break;  /* no args */
 207                         if (legal_arg(bargs) < 0)
 208                                 break;  /* bad args */
 209 
 210                         /* avoid cancellation in system() */
 211                         (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
 212                             NULL);
 213 
 214                         /* check for /stubboot */
 215                         if (stat("/stubboot/boot/grub/menu.lst", &sbuf) == 0) {
 216                                 altroot = "-R /stubboot ";
 217                         } else {
 218                                 altroot = "";
 219                         }
 220 
 221                         if (fcn == AD_FASTREBOOT) {
 222                                 char *newarg, *head;
 223                                 char bargs_scratch[BOOTARGS_MAX];
 224 
 225                                 bzero(bargs_scratch, BOOTARGS_MAX);
 226 
 227                                 bcopy(bargs, bargs_scratch, strlen(bargs));
 228                                 head = bargs_scratch;
 229                                 newarg = strtok(bargs_scratch, " ");
 230 
 231                                 if (newarg == NULL || newarg[0] == '-')
 232                                         break;
 233 
 234                                 /* First argument is rootdir */
 235                                 if (strncmp(&newarg[strlen(newarg)-4],
 236                                     "unix", 4) != 0) {
 237                                         newarg = strtok(NULL, " ");
 238                                         off = newarg - head;
 239                                 }
 240 
 241                                 /*
 242                                  * If we are using alternate root via
 243                                  * mountpoint or a different BE, don't
 244                                  * bother to update the temp menu entry.
 245                                  */
 246                                 if (off > 0)
 247                                         break;
 248                         }
 249 
 250                         /* are we rebooting to a GRUB menu entry? */
 251                         if (isdigit(bargs[0])) {
 252                                 int entry = strtol(bargs, NULL, 10);
 253                                 (void) snprintf(cmdbuf, sizeof (cmdbuf),
 254                                     "/sbin/grubadm %s --set-default %d",
 255                                     altroot, entry);
 256                         } else {
 257                                 (void) snprintf(cmdbuf, sizeof (cmdbuf),
 258                                     "/sbin/grubadm --new --default %s"
 259                                     "--set-opts %s%s%s", altroot, quote,
 260                                     &bargs[off], quote);
 261                         }
 262                         (void) system(cmdbuf);
 263                 }
 264                 check_archive_update();
 265         }
 266 
 267         return (__uadmin(cmd, fcn, mdep));
 268 }