Print this page
    
OS-2288 there is no dialup, only zuul
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/cmd/halt/halt.c
          +++ new/usr/src/cmd/halt/halt.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  11   11   * and limitations under the License.
  12   12   *
  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 2010 Sun Microsystems, Inc.  All rights reserved.
  23   23   * Use is subject to license terms.
  24   24   * Copyright 2011 Joyent, Inc.  All rights reserved.
  25   25   */
  26   26  
  27   27  /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
  28   28  /*        All Rights Reserved   */
  29   29  
  30   30  /*
  31   31   * University Copyright- Copyright (c) 1982, 1986, 1988
  32   32   * The Regents of the University of California
  33   33   * All Rights Reserved
  34   34   *
  35   35   * University Acknowledgment- Portions of this document are derived from
  36   36   * software developed by the University of California, Berkeley, and its
  37   37   * contributors.
  38   38   * Portions contributed by Juergen Keil, <jk@tools.de>.
  39   39   */
  40   40  
  41   41  
  42   42  /*
  43   43   * Common code for halt(1M), poweroff(1M), and reboot(1M).  We use
  44   44   * argv[0] to determine which behavior to exhibit.
  45   45   */
  46   46  
  47   47  #include <stdio.h>
  48   48  #include <procfs.h>
  49   49  #include <sys/types.h>
  50   50  #include <sys/elf.h>
  51   51  #include <sys/systeminfo.h>
  52   52  #include <sys/stat.h>
  53   53  #include <sys/uadmin.h>
  54   54  #include <sys/mntent.h>
  55   55  #include <sys/mnttab.h>
  56   56  #include <sys/mount.h>
  57   57  #include <sys/fs/ufs_mount.h>
  58   58  #include <alloca.h>
  59   59  #include <assert.h>
  60   60  #include <errno.h>
  61   61  #include <fcntl.h>
  62   62  #include <libgen.h>
  63   63  #include <libscf.h>
  64   64  #include <libscf_priv.h>
  65   65  #include <limits.h>
  66   66  #include <locale.h>
  67   67  #include <libintl.h>
  68   68  #include <syslog.h>
  69   69  #include <signal.h>
  70   70  #include <strings.h>
  71   71  #include <unistd.h>
  72   72  #include <stdlib.h>
  73   73  #include <stdio.h>
  74   74  #include <strings.h>
  75   75  #include <time.h>
  76   76  #include <wait.h>
  77   77  #include <ctype.h>
  78   78  #include <utmpx.h>
  79   79  #include <pwd.h>
  80   80  #include <zone.h>
  81   81  #include <spawn.h>
  82   82  
  83   83  #include <libzfs.h>
  84   84  #if defined(__i386)
  85   85  #include <libgrubmgmt.h>
  86   86  #endif
  87   87  
  88   88  #if !defined(TEXT_DOMAIN)
  89   89  #define TEXT_DOMAIN     "SYS_TEST"
  90   90  #endif
  91   91  
  92   92  #if defined(__sparc)
  93   93  #define CUR_ELFDATA     ELFDATA2MSB
  94   94  #elif defined(__i386)
  95   95  #define CUR_ELFDATA     ELFDATA2LSB
  96   96  #endif
  97   97  
  98   98  static libzfs_handle_t *g_zfs;
  99   99  
 100  100  extern int audit_halt_setup(int, char **);
 101  101  extern int audit_halt_success(void);
 102  102  extern int audit_halt_fail(void);
 103  103  
 104  104  extern int audit_reboot_setup(void);
 105  105  extern int audit_reboot_success(void);
 106  106  extern int audit_reboot_fail(void);
 107  107  
 108  108  static char *cmdname;   /* basename(argv[0]), the name of the command */
 109  109  
 110  110  typedef struct ctidlist_struct {
 111  111          ctid_t ctid;
 112  112          struct ctidlist_struct *next;
 113  113  } ctidlist_t;
 114  114  
 115  115  static ctidlist_t *ctidlist = NULL;
 116  116  static ctid_t startdct = -1;
 117  117  
 118  118  #define FMRI_STARTD_CONTRACT \
 119  119          "svc:/system/svc/restarter:default/:properties/restarter/contract"
 120  120  
 121  121  #define BEADM_PROG      "/usr/sbin/beadm"
 122  122  #define BOOTADM_PROG    "/sbin/bootadm"
 123  123  #define ZONEADM_PROG    "/usr/sbin/zoneadm"
 124  124  
 125  125  /*
 126  126   * The length of FASTBOOT_MOUNTPOINT must be less than MAXPATHLEN.
 127  127   */
 128  128  #define FASTBOOT_MOUNTPOINT     "/tmp/.fastboot.root"
 129  129  
 130  130  /*
 131  131   * Fast Reboot related variables
 132  132   */
 133  133  static char     fastboot_mounted[MAXPATHLEN];
 134  134  
 135  135  #if defined(__i386)
 136  136  static grub_boot_args_t fbarg;
 137  137  static grub_boot_args_t *fbarg_used;
 138  138  static int fbarg_entnum = GRUB_ENTRY_DEFAULT;
 139  139  #endif  /* __i386 */
 140  140  
 141  141  static int validate_ufs_disk(char *, char *);
 142  142  static int validate_zfs_pool(char *, char *);
 143  143  
 144  144  static pid_t
 145  145  get_initpid()
 146  146  {
 147  147          static int init_pid = -1;
 148  148  
 149  149          if (init_pid == -1) {
 150  150                  if (zone_getattr(getzoneid(), ZONE_ATTR_INITPID, &init_pid,
 151  151                      sizeof (init_pid)) != sizeof (init_pid)) {
 152  152                          assert(errno == ESRCH);
 153  153                          init_pid = -1;
 154  154                  }
 155  155          }
 156  156          return (init_pid);
 157  157  }
 158  158  
 159  159  /*
 160  160   * Quiesce or resume init using /proc.  When stopping init, we can't send
 161  161   * SIGTSTP (since init ignores it) or SIGSTOP (since the kernel won't permit
 162  162   * it).
 163  163   */
 164  164  static int
 165  165  direct_init(long command)
 166  166  {
 167  167          char ctlfile[MAXPATHLEN];
 168  168          pid_t pid;
 169  169          int ctlfd;
 170  170  
 171  171          assert(command == PCDSTOP || command == PCRUN);
 172  172          if ((pid = get_initpid()) == -1) {
 173  173                  return (-1);
 174  174          }
 175  175  
 176  176          (void) snprintf(ctlfile, sizeof (ctlfile), "/proc/%d/ctl", pid);
 177  177          if ((ctlfd = open(ctlfile, O_WRONLY)) == -1)
 178  178                  return (-1);
 179  179  
 180  180          if (command == PCDSTOP) {
 181  181                  if (write(ctlfd, &command, sizeof (long)) == -1) {
 182  182                          (void) close(ctlfd);
 183  183                          return (-1);
 184  184                  }
 185  185          } else {        /* command == PCRUN */
 186  186                  long cmds[2];
 187  187                  cmds[0] = command;
 188  188                  cmds[1] = 0;
 189  189                  if (write(ctlfd, cmds, sizeof (cmds)) == -1) {
 190  190                          (void) close(ctlfd);
 191  191                          return (-1);
 192  192                  }
 193  193          }
 194  194          (void) close(ctlfd);
 195  195          return (0);
 196  196  }
 197  197  
 198  198  static void
 199  199  stop_startd()
 200  200  {
 201  201          scf_handle_t *h;
 202  202          scf_property_t *prop = NULL;
 203  203          scf_value_t *val = NULL;
 204  204          uint64_t uint64;
 205  205  
 206  206          if ((h = scf_handle_create(SCF_VERSION)) == NULL)
 207  207                  return;
 208  208  
 209  209          if ((scf_handle_bind(h) != 0) ||
 210  210              ((prop = scf_property_create(h)) == NULL) ||
 211  211              ((val = scf_value_create(h)) == NULL))
 212  212                  goto out;
 213  213  
 214  214          if (scf_handle_decode_fmri(h, FMRI_STARTD_CONTRACT,
 215  215              NULL, NULL, NULL, NULL, prop, SCF_DECODE_FMRI_EXACT) != 0)
 216  216                  goto out;
 217  217  
 218  218          if (scf_property_is_type(prop, SCF_TYPE_COUNT) != 0 ||
 219  219              scf_property_get_value(prop, val) != 0 ||
 220  220              scf_value_get_count(val, &uint64) != 0)
 221  221                  goto out;
 222  222  
 223  223          startdct = (ctid_t)uint64;
 224  224          (void) sigsend(P_CTID, startdct, SIGSTOP);
 225  225  
 226  226  out:
 227  227          scf_property_destroy(prop);
 228  228          scf_value_destroy(val);
 229  229          scf_handle_destroy(h);
 230  230  }
 231  231  
 232  232  static void
 233  233  continue_startd()
 234  234  {
 235  235          if (startdct != -1)
 236  236                  (void) sigsend(P_CTID, startdct, SIGCONT);
 237  237  }
 238  238  
 239  239  #define FMRI_RESTARTER_PROP "/:properties/general/restarter"
 240  240  #define FMRI_CONTRACT_PROP "/:properties/restarter/contract"
 241  241  
 242  242  static int
 243  243  save_ctid(ctid_t ctid)
 244  244  {
 245  245          ctidlist_t *next;
 246  246  
 247  247          for (next = ctidlist; next != NULL; next = next->next)
 248  248                  if (next->ctid == ctid)
 249  249                          return (-1);
 250  250  
 251  251          next = (ctidlist_t *)malloc(sizeof (ctidlist_t));
 252  252          if (next == NULL)
 253  253                  return (-1);
 254  254  
 255  255          next->ctid = ctid;
 256  256          next->next = ctidlist;
 257  257          ctidlist = next;
 258  258          return (0);
 259  259  }
 260  260  
 261  261  static void
 262  262  stop_delegates()
 263  263  {
 264  264          ctid_t ctid;
 265  265          scf_handle_t *h;
 266  266          scf_scope_t *sc = NULL;
 267  267          scf_service_t *svc = NULL;
 268  268          scf_instance_t *inst = NULL;
 269  269          scf_snapshot_t *snap = NULL;
 270  270          scf_snapshot_t *isnap = NULL;
 271  271          scf_propertygroup_t *pg = NULL;
 272  272          scf_property_t *prop = NULL;
 273  273          scf_value_t *val = NULL;
 274  274          scf_iter_t *siter = NULL;
 275  275          scf_iter_t *iiter = NULL;
 276  276          char *fmri;
 277  277          ssize_t length;
 278  278  
 279  279          uint64_t uint64;
 280  280          ssize_t bytes;
 281  281  
 282  282          length = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
 283  283          if (length <= 0)
 284  284                  return;
 285  285  
 286  286          length++;
 287  287          fmri = alloca(length * sizeof (char));
 288  288  
 289  289          if ((h = scf_handle_create(SCF_VERSION)) == NULL)
 290  290                  return;
 291  291  
 292  292          if (scf_handle_bind(h) != 0) {
 293  293                  scf_handle_destroy(h);
 294  294                  return;
 295  295          }
 296  296  
 297  297          if ((sc = scf_scope_create(h)) == NULL ||
 298  298              (svc = scf_service_create(h)) == NULL ||
 299  299              (inst = scf_instance_create(h)) == NULL ||
 300  300              (snap = scf_snapshot_create(h)) == NULL ||
 301  301              (pg = scf_pg_create(h)) == NULL ||
 302  302              (prop = scf_property_create(h)) == NULL ||
 303  303              (val = scf_value_create(h)) == NULL ||
 304  304              (siter = scf_iter_create(h)) == NULL ||
 305  305              (iiter = scf_iter_create(h)) == NULL)
 306  306                  goto out;
 307  307  
 308  308          if (scf_handle_get_scope(h, SCF_SCOPE_LOCAL, sc) != 0)
 309  309                  goto out;
 310  310  
 311  311          if (scf_iter_scope_services(siter, sc) != 0)
 312  312                  goto out;
 313  313  
 314  314          while (scf_iter_next_service(siter, svc) == 1) {
 315  315  
 316  316                  if (scf_iter_service_instances(iiter, svc) != 0)
 317  317                          continue;
 318  318  
 319  319                  while (scf_iter_next_instance(iiter, inst) == 1) {
 320  320  
 321  321                          if ((scf_instance_get_snapshot(inst, "running",
 322  322                              snap)) != 0)
 323  323                                  isnap = NULL;
 324  324                          else
 325  325                                  isnap = snap;
 326  326  
 327  327                          if (scf_instance_get_pg_composed(inst, isnap,
 328  328                              SCF_PG_GENERAL, pg) != 0)
 329  329                                  continue;
 330  330  
 331  331                          if (scf_pg_get_property(pg, SCF_PROPERTY_RESTARTER,
 332  332                              prop) != 0 ||
 333  333                              scf_property_get_value(prop, val) != 0)
 334  334                                  continue;
 335  335  
 336  336                          bytes = scf_value_get_astring(val, fmri, length);
 337  337                          if (bytes <= 0 || bytes >= length)
 338  338                                  continue;
 339  339  
 340  340                          if (strlcat(fmri, FMRI_CONTRACT_PROP, length) >=
 341  341                              length)
 342  342                                  continue;
 343  343  
 344  344                          if (scf_handle_decode_fmri(h, fmri, NULL, NULL,
 345  345                              NULL, NULL, prop, SCF_DECODE_FMRI_EXACT) != 0)
 346  346                                  continue;
 347  347  
 348  348                          if (scf_property_is_type(prop, SCF_TYPE_COUNT) != 0 ||
 349  349                              scf_property_get_value(prop, val) != 0 ||
 350  350                              scf_value_get_count(val, &uint64) != 0)
 351  351                                  continue;
 352  352  
 353  353                          ctid = (ctid_t)uint64;
 354  354                          if (save_ctid(ctid) == 0) {
 355  355                                  (void) sigsend(P_CTID, ctid, SIGSTOP);
 356  356                          }
 357  357                  }
 358  358          }
 359  359  out:
 360  360          scf_scope_destroy(sc);
 361  361          scf_service_destroy(svc);
 362  362          scf_instance_destroy(inst);
 363  363          scf_snapshot_destroy(snap);
 364  364          scf_pg_destroy(pg);
 365  365          scf_property_destroy(prop);
 366  366          scf_value_destroy(val);
 367  367          scf_iter_destroy(siter);
 368  368          scf_iter_destroy(iiter);
 369  369  
 370  370          (void) scf_handle_unbind(h);
 371  371          scf_handle_destroy(h);
 372  372  }
 373  373  
 374  374  static void
 375  375  continue_delegates()
 376  376  {
 377  377          ctidlist_t *next;
 378  378          for (next = ctidlist; next != NULL; next = next->next)
 379  379                  (void) sigsend(P_CTID, next->ctid, SIGCONT);
 380  380  }
 381  381  
 382  382  #define FMRI_GDM "svc:/application/graphical-login/gdm:default"
 383  383  #define GDM_STOP_TIMEOUT        10      /* Give gdm 10 seconds to shut down */
 384  384  
 385  385  /*
 386  386   * If gdm is running, try to stop gdm.
 387  387   * Returns  0 on success, -1 on failure.
 388  388   */
 389  389  static int
 390  390  stop_gdm()
 391  391  {
 392  392          char *gdm_state = NULL;
 393  393          int retry = 0;
 394  394  
 395  395          /*
 396  396           * If gdm is running, try to stop gdm.
 397  397           */
 398  398          while ((gdm_state = smf_get_state(FMRI_GDM)) != NULL &&
 399  399              strcmp(gdm_state, SCF_STATE_STRING_ONLINE) == 0 &&
 400  400              retry++ < GDM_STOP_TIMEOUT) {
 401  401  
 402  402                  free(gdm_state);
 403  403  
 404  404                  /*
 405  405                   * Only need to disable once.
 406  406                   */
 407  407                  if (retry == 1 &&
 408  408                      smf_disable_instance(FMRI_GDM, SMF_TEMPORARY) != 0) {
 409  409                          (void) fprintf(stderr,
 410  410                              gettext("%s: Failed to stop %s: %s.\n"),
 411  411                              cmdname, FMRI_GDM, scf_strerror(scf_error()));
 412  412                          return (-1);
 413  413                  }
 414  414                  (void) sleep(1);
 415  415          }
 416  416  
 417  417          if (retry >= GDM_STOP_TIMEOUT) {
 418  418                  (void) fprintf(stderr, gettext("%s: Failed to stop %s.\n"),
 419  419                      cmdname, FMRI_GDM);
 420  420                  return (-1);
 421  421          }
 422  422  
 423  423          return (0);
 424  424  }
 425  425  
 426  426  
 427  427  static void
 428  428  stop_restarters()
 429  429  {
 430  430          stop_startd();
 431  431          stop_delegates();
 432  432  }
 433  433  
 434  434  static void
 435  435  continue_restarters()
 436  436  {
 437  437          continue_startd();
 438  438          continue_delegates();
 439  439  }
 440  440  
 441  441  /*
 442  442   * Copy an array of strings into buf, separated by spaces.  Returns 0 on
 443  443   * success.
 444  444   */
 445  445  static int
 446  446  gather_args(char **args, char *buf, size_t buf_sz)
 447  447  {
 448  448          if (strlcpy(buf, *args, buf_sz) >= buf_sz)
 449  449                  return (-1);
 450  450  
 451  451          for (++args; *args != NULL; ++args) {
 452  452                  if (strlcat(buf, " ", buf_sz) >= buf_sz)
 453  453                          return (-1);
 454  454                  if (strlcat(buf, *args, buf_sz) >= buf_sz)
 455  455                          return (-1);
 456  456          }
 457  457  
 458  458          return (0);
 459  459  }
 460  460  
 461  461  /*
 462  462   * Halt every zone on the system.  We are committed to doing a shutdown
 463  463   * even if something goes wrong here. If something goes wrong, we just
 464  464   * continue with the shutdown.  Return non-zero if we need to wait for zones to
 465  465   * halt later on.
 466  466   */
 467  467  static int
 468  468  halt_zones()
 469  469  {
 470  470          pid_t pid;
 471  471          zoneid_t *zones;
 472  472          size_t nz = 0, old_nz;
 473  473          int i;
 474  474          char zname[ZONENAME_MAX];
 475  475  
 476  476          /*
 477  477           * Get a list of zones. If the number of zones changes in between the
 478  478           * two zone_list calls, try again.
 479  479           */
 480  480  
 481  481          for (;;) {
 482  482                  (void) zone_list(NULL, &nz);
 483  483                  if (nz == 1)
 484  484                          return (0);
 485  485                  old_nz = nz;
 486  486                  zones = calloc(sizeof (zoneid_t), nz);
 487  487                  if (zones == NULL) {
 488  488                          (void) fprintf(stderr,
 489  489                              gettext("%s: Could not halt zones"
 490  490                              " (out of memory).\n"), cmdname);
 491  491                          return (0);
 492  492                  }
 493  493  
 494  494                  (void) zone_list(zones, &nz);
 495  495                  if (old_nz == nz)
 496  496                          break;
 497  497                  free(zones);
 498  498          }
 499  499  
 500  500          if (nz == 2) {
 501  501                  (void) fprintf(stderr, gettext("%s: Halting 1 zone.\n"),
 502  502                      cmdname);
 503  503          } else {
 504  504                  (void) fprintf(stderr, gettext("%s: Halting %i zones.\n"),
 505  505                      cmdname, nz - 1);
 506  506          }
 507  507  
 508  508          for (i = 0; i < nz; i++) {
 509  509                  if (zones[i] == GLOBAL_ZONEID)
 510  510                          continue;
 511  511                  if (getzonenamebyid(zones[i], zname, sizeof (zname)) < 0) {
 512  512                          /*
 513  513                           * getzonenamebyid should only fail if we raced with
 514  514                           * another process trying to shut down the zone.
 515  515                           * We assume this happened and ignore the error.
 516  516                           */
 517  517                          if (errno != EINVAL) {
 518  518                                  (void) fprintf(stderr,
 519  519                                      gettext("%s: Unexpected error while "
 520  520                                      "looking up zone %ul: %s.\n"),
 521  521                                      cmdname, zones[i], strerror(errno));
 522  522                          }
 523  523  
 524  524                          continue;
 525  525                  }
 526  526                  pid = fork();
 527  527                  if (pid < 0) {
 528  528                          (void) fprintf(stderr,
 529  529                              gettext("%s: Zone \"%s\" could not be"
 530  530                              " halted (could not fork(): %s).\n"),
 531  531                              cmdname, zname, strerror(errno));
 532  532                          continue;
 533  533                  }
 534  534                  if (pid == 0) {
 535  535                          (void) execl(ZONEADM_PROG, ZONEADM_PROG,
 536  536                              "-z", zname, "halt", NULL);
 537  537                          (void) fprintf(stderr,
 538  538                              gettext("%s: Zone \"%s\" could not be halted"
 539  539                              " (cannot exec(" ZONEADM_PROG "): %s).\n"),
 540  540                              cmdname, zname, strerror(errno));
 541  541                          exit(0);
 542  542                  }
 543  543          }
 544  544  
 545  545          return (1);
 546  546  }
 547  547  
 548  548  /*
 549  549   * This function tries to wait for all non-global zones to go away.
 550  550   * It will timeout if no progress is made for 5 seconds, or a total of
 551  551   * 30 seconds elapses.
 552  552   */
 553  553  
 554  554  static void
 555  555  check_zones_haltedness()
 556  556  {
 557  557          int t = 0, t_prog = 0;
 558  558          size_t nz = 0, last_nz;
 559  559  
 560  560          do {
 561  561                  last_nz = nz;
 562  562                  (void) zone_list(NULL, &nz);
 563  563                  if (nz == 1)
 564  564                          return;
 565  565  
 566  566                  (void) sleep(1);
 567  567  
 568  568                  if (last_nz > nz)
 569  569                          t_prog = 0;
 570  570  
 571  571                  t++;
 572  572                  t_prog++;
 573  573  
 574  574                  if (t == 10) {
 575  575                          if (nz == 2) {
 576  576                                  (void) fprintf(stderr,
 577  577                                      gettext("%s: Still waiting for 1 zone to "
 578  578                                      "halt. Will wait up to 20 seconds.\n"),
 579  579                                      cmdname);
 580  580                          } else {
 581  581                                  (void) fprintf(stderr,
 582  582                                      gettext("%s: Still waiting for %i zones "
 583  583                                      "to halt. Will wait up to 20 seconds.\n"),
 584  584                                      cmdname, nz - 1);
 585  585                          }
 586  586                  }
 587  587  
 588  588          } while ((t < 30) && (t_prog < 5));
 589  589  }
 590  590  
 591  591  
 592  592  /*
 593  593   * Validate that this is a root disk or dataset
 594  594   * Returns 0 if it is a root disk or dataset;
 595  595   * returns 1 if it is a disk argument or dataset, but not valid or not root;
 596  596   * returns -1 if it is not a valid argument or a disk argument.
 597  597   */
 598  598  static int
 599  599  validate_disk(char *arg, char *mountpoint)
 600  600  {
 601  601          static char root_dev_path[] = "/dev/dsk";
 602  602          char kernpath[MAXPATHLEN];
 603  603          struct stat64 statbuf;
 604  604          int rc = 0;
 605  605  
 606  606          if (strlen(arg) > MAXPATHLEN) {
 607  607                  (void) fprintf(stderr,
 608  608                      gettext("%s: Argument is too long\n"), cmdname);
 609  609                  return (-1);
 610  610          }
 611  611  
 612  612          bcopy(FASTBOOT_MOUNTPOINT, mountpoint, sizeof (FASTBOOT_MOUNTPOINT));
 613  613  
 614  614          if (strstr(arg, mountpoint) == NULL) {
 615  615                  /*
 616  616                   * Do a force umount just in case some other filesystem has
 617  617                   * been mounted there.
 618  618                   */
 619  619                  (void) umount2(mountpoint, MS_FORCE);
 620  620          }
 621  621  
 622  622          /* Create the directory if it doesn't already exist */
 623  623          if (lstat64(mountpoint, &statbuf) != 0) {
 624  624                  if (mkdirp(mountpoint, 0755) != 0) {
 625  625                          (void) fprintf(stderr,
 626  626                              gettext("Failed to create mountpoint %s\n"),
 627  627                              mountpoint);
 628  628                          return (-1);
 629  629                  }
 630  630          }
 631  631  
 632  632          if (strncmp(arg, root_dev_path, strlen(root_dev_path)) == 0) {
 633  633                  /* ufs root disk argument */
 634  634                  rc = validate_ufs_disk(arg, mountpoint);
 635  635          } else {
 636  636                  /* zfs root pool argument */
 637  637                  rc = validate_zfs_pool(arg, mountpoint);
 638  638          }
 639  639  
 640  640          if (rc != 0)
 641  641                  return (rc);
 642  642  
 643  643          (void) snprintf(kernpath, MAXPATHLEN, "%s/platform/i86pc/kernel/unix",
 644  644              mountpoint);
 645  645  
 646  646          if (stat64(kernpath, &statbuf) != 0) {
 647  647                  (void) fprintf(stderr,
 648  648                      gettext("%s: %s is not a root disk or dataset\n"),
 649  649                      cmdname, arg);
 650  650                  return (1);
 651  651          }
 652  652  
 653  653          return (0);
 654  654  }
 655  655  
 656  656  
 657  657  static int
 658  658  validate_ufs_disk(char *arg, char *mountpoint)
 659  659  {
 660  660          struct ufs_args ufs_args = { 0 };
 661  661          char mntopts[MNT_LINE_MAX] = MNTOPT_LARGEFILES;
 662  662  
 663  663          /* perform the mount */
 664  664          ufs_args.flags = UFSMNT_LARGEFILES;
 665  665          if (mount(arg, mountpoint, MS_DATA|MS_OPTIONSTR,
 666  666              MNTTYPE_UFS, &ufs_args, sizeof (ufs_args),
 667  667              mntopts, sizeof (mntopts)) != 0) {
 668  668                  perror(cmdname);
 669  669                  (void) fprintf(stderr,
 670  670                      gettext("%s: Failed to mount %s\n"), cmdname, arg);
 671  671                  return (-1);
 672  672          }
 673  673  
 674  674          return (0);
 675  675  }
 676  676  
 677  677  static int
 678  678  validate_zfs_pool(char *arg, char *mountpoint)
 679  679  {
 680  680          zfs_handle_t *zhp = NULL;
 681  681          char mntopts[MNT_LINE_MAX] = { '\0' };
 682  682          int rc = 0;
 683  683  
 684  684          if ((g_zfs = libzfs_init()) == NULL) {
 685  685                  (void) fprintf(stderr, gettext("Internal error: failed to "
 686  686                      "initialize ZFS library\n"));
 687  687                  return (-1);
 688  688          }
 689  689  
 690  690          /* Try to open the dataset */
 691  691          if ((zhp = zfs_open(g_zfs, arg,
 692  692              ZFS_TYPE_FILESYSTEM | ZFS_TYPE_DATASET)) == NULL)
 693  693                  return (-1);
 694  694  
 695  695          /* perform the mount */
 696  696          if (mount(zfs_get_name(zhp), mountpoint, MS_DATA|MS_OPTIONSTR|MS_RDONLY,
 697  697              MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) {
 698  698                  perror(cmdname);
 699  699                  (void) fprintf(stderr,
 700  700                      gettext("%s: Failed to mount %s\n"), cmdname, arg);
 701  701                  rc = -1;
 702  702          }
 703  703  
 704  704  validate_zfs_err_out:
 705  705          if (zhp != NULL)
 706  706                  zfs_close(zhp);
 707  707  
 708  708          libzfs_fini(g_zfs);
 709  709          return (rc);
 710  710  }
 711  711  
 712  712  /*
 713  713   * Return 0 if not zfs, or is zfs and have successfully constructed the
 714  714   * boot argument; returns non-zero otherwise.
 715  715   * At successful completion fpth contains pointer where mount point ends.
 716  716   * NOTE: arg is supposed to be the resolved path
 717  717   */
 718  718  static int
 719  719  get_zfs_bootfs_arg(const char *arg, const char ** fpth, int *is_zfs,
 720  720                  char *bootfs_arg)
 721  721  {
 722  722          zfs_handle_t *zhp = NULL;
 723  723          zpool_handle_t *zpoolp = NULL;
 724  724          FILE *mtabp = NULL;
 725  725          struct mnttab mnt;
 726  726          char *poolname = NULL;
 727  727          char physpath[MAXPATHLEN];
 728  728          char mntsp[ZPOOL_MAXNAMELEN];
 729  729          char bootfs[ZPOOL_MAXNAMELEN];
 730  730          int rc = 0;
 731  731          size_t mntlen = 0;
 732  732          size_t msz;
 733  733          static char fmt[] = "-B zfs-bootfs=%s,bootpath=\"%s\"";
 734  734  
 735  735          *fpth = arg;
 736  736          *is_zfs = 0;
 737  737  
 738  738          bzero(physpath, sizeof (physpath));
 739  739          bzero(bootfs, sizeof (bootfs));
 740  740  
 741  741          if ((mtabp = fopen(MNTTAB, "r")) == NULL) {
 742  742                  return (-1);
 743  743          }
 744  744  
 745  745          while (getmntent(mtabp, &mnt) == 0) {
 746  746                  if (strstr(arg, mnt.mnt_mountp) == arg &&
 747  747                      (msz = strlen(mnt.mnt_mountp)) > mntlen) {
 748  748                          mntlen = msz;
 749  749                          *is_zfs = strcmp(MNTTYPE_ZFS, mnt.mnt_fstype) == 0;
 750  750                          (void) strlcpy(mntsp, mnt.mnt_special, sizeof (mntsp));
 751  751                  }
 752  752          }
 753  753  
 754  754          (void) fclose(mtabp);
 755  755  
 756  756          if (mntlen > 1)
 757  757                  *fpth += mntlen;
 758  758  
 759  759          if (!*is_zfs)
 760  760                  return (0);
 761  761  
 762  762          if ((g_zfs = libzfs_init()) == NULL)
 763  763                  return (-1);
 764  764  
 765  765          /* Try to open the dataset */
 766  766          if ((zhp = zfs_open(g_zfs, mntsp,
 767  767              ZFS_TYPE_FILESYSTEM | ZFS_TYPE_DATASET)) == NULL) {
 768  768                  (void) fprintf(stderr, gettext("Cannot open %s\n"), mntsp);
 769  769                  rc = -1;
 770  770                  goto validate_zfs_err_out;
 771  771          }
 772  772  
 773  773          (void) strlcpy(bootfs, mntsp, sizeof (bootfs));
 774  774  
 775  775          if ((poolname = strtok(mntsp, "/")) == NULL) {
 776  776                  rc = -1;
 777  777                  goto validate_zfs_err_out;
 778  778          }
 779  779  
 780  780          if ((zpoolp = zpool_open(g_zfs, poolname)) == NULL) {
 781  781                  (void) fprintf(stderr, gettext("Cannot open %s\n"), poolname);
 782  782                  rc = -1;
 783  783                  goto validate_zfs_err_out;
 784  784          }
 785  785  
 786  786          if (zpool_get_physpath(zpoolp, physpath, sizeof (physpath)) != 0) {
 787  787                  (void) fprintf(stderr, gettext("Cannot find phys_path\n"));
 788  788                  rc = -1;
 789  789                  goto validate_zfs_err_out;
 790  790          }
 791  791  
 792  792          /*
 793  793           * For the mirror physpath would contain the list of all
 794  794           * bootable devices, pick up the first one.
 795  795           */
 796  796          (void) strtok(physpath, " ");
 797  797          if (snprintf(bootfs_arg, BOOTARGS_MAX, fmt, bootfs, physpath) >=
 798  798              BOOTARGS_MAX) {
 799  799                  rc = E2BIG;
 800  800                  (void) fprintf(stderr,
 801  801                      gettext("Boot arguments are too long\n"));
 802  802          }
 803  803  
 804  804  validate_zfs_err_out:
 805  805          if (zhp != NULL)
 806  806                  zfs_close(zhp);
 807  807  
 808  808          if (zpoolp != NULL)
 809  809                  zpool_close(zpoolp);
 810  810  
 811  811          libzfs_fini(g_zfs);
 812  812          return (rc);
 813  813  }
 814  814  
 815  815  /*
 816  816   * Validate that the file exists, and is an ELF file.
 817  817   * Returns 0 on success, -1 on failure.
 818  818   */
 819  819  static int
 820  820  validate_unix(char *arg, int *mplen, int *is_zfs, char *bootfs_arg)
 821  821  {
 822  822          const char *location;
 823  823          int class, format;
 824  824          unsigned char ident[EI_NIDENT];
 825  825          char physpath[MAXPATHLEN];
 826  826          int elffd = -1;
 827  827          size_t  sz;
 828  828  
 829  829          if ((sz = resolvepath(arg, physpath, sizeof (physpath) - 1)) ==
 830  830              (size_t)-1) {
 831  831                  (void) fprintf(stderr,
 832  832                      gettext("Cannot resolve path for %s: %s\n"),
 833  833                      arg, strerror(errno));
 834  834                  return (-1);
 835  835          }
 836  836          (void) strlcpy(arg, physpath, sz + 1);
 837  837  
 838  838          if (strlen(arg) > MAXPATHLEN) {
 839  839                  (void) fprintf(stderr,
 840  840                      gettext("%s: New kernel name is too long\n"), cmdname);
 841  841                  return (-1);
 842  842          }
 843  843  
 844  844          if (strncmp(basename(arg), "unix", 4) != 0) {
 845  845                  (void) fprintf(stderr,
 846  846                      gettext("%s: %s: Kernel name must be unix\n"),
 847  847                      cmdname, arg);
 848  848                  return (-1);
 849  849          }
 850  850  
 851  851          if (get_zfs_bootfs_arg(arg, &location, is_zfs, bootfs_arg) != 0)
 852  852                  goto err_out;
 853  853  
 854  854          *mplen = location - arg;
 855  855  
 856  856          if (strstr(location, "/boot/platform") == location) {
 857  857                  /*
 858  858                   * Rebooting to failsafe.
 859  859                   * Clear bootfs_arg and is_zfs flag.
 860  860                   */
 861  861                  bootfs_arg[0] = 0;
 862  862                  *is_zfs = 0;
 863  863          } else if (strstr(location, "/platform") != location) {
 864  864                  (void) fprintf(stderr,
 865  865                      gettext("%s: %s: No /platform in file name\n"),
 866  866                      cmdname, arg);
 867  867                  goto err_out;
 868  868          }
 869  869  
 870  870          if ((elffd = open64(arg, O_RDONLY)) < 0 ||
 871  871              (pread64(elffd, ident, EI_NIDENT, 0) != EI_NIDENT)) {
 872  872                  (void) fprintf(stderr, "%s: %s: %s\n",
 873  873                      cmdname, arg, strerror(errno));
 874  874                  goto err_out;
 875  875          }
 876  876  
 877  877          class = ident[EI_CLASS];
 878  878  
 879  879          if ((class != ELFCLASS32 && class != ELFCLASS64) ||
 880  880              memcmp(&ident[EI_MAG0], ELFMAG, 4) != 0) {
 881  881                  (void) fprintf(stderr,
 882  882                      gettext("%s: %s: Not a valid ELF file\n"), cmdname, arg);
 883  883                  goto err_out;
 884  884          }
 885  885  
 886  886          format = ident[EI_DATA];
 887  887  
 888  888          if (format != CUR_ELFDATA) {
 889  889                  (void) fprintf(stderr, gettext("%s: %s: Invalid data format\n"),
 890  890                      cmdname, arg);
 891  891                  goto err_out;
 892  892          }
 893  893  
 894  894          return (0);
 895  895  
 896  896  err_out:
 897  897          if (elffd >= 0) {
 898  898                  (void) close(elffd);
 899  899                  elffd = -1;
 900  900          }
 901  901          return (-1);
 902  902  }
 903  903  
 904  904  static int
 905  905  halt_exec(const char *path, ...)
 906  906  {
 907  907          pid_t           pid;
 908  908          int             i;
 909  909          int             st;
 910  910          const char      *arg;
 911  911          va_list vp;
 912  912          const char      *argv[256];
 913  913  
 914  914          if ((pid = fork()) == -1) {
 915  915                  return (errno);
 916  916          } else if (pid == 0) {
 917  917                  (void) fclose(stdout);
 918  918                  (void) fclose(stderr);
 919  919  
 920  920                  argv[0] = path;
 921  921                  i = 1;
 922  922  
 923  923                  va_start(vp, path);
 924  924  
 925  925                  do {
 926  926                          arg = va_arg(vp, const char *);
 927  927                          argv[i] = arg;
 928  928                  } while (arg != NULL &&
 929  929                      ++i != sizeof (argv) / sizeof (argv[0]));
 930  930  
 931  931                  va_end(vp);
 932  932  
 933  933                  (void) execve(path, (char * const *)argv, NULL);
 934  934                  (void) fprintf(stderr, gettext("Cannot execute %s: %s\n"),
 935  935                      path, strerror(errno));
 936  936                  exit(-1);
 937  937          } else {
 938  938                  if (waitpid(pid, &st, 0) == pid &&
 939  939                      !WIFSIGNALED(st) && WIFEXITED(st))
 940  940                          st = WEXITSTATUS(st);
 941  941                  else
 942  942                          st = -1;
 943  943          }
 944  944          return (st);
 945  945  }
 946  946  
 947  947  /*
 948  948   * Mount the specified BE.
 949  949   *
 950  950   * Upon success returns zero and copies bename string to mountpoint[]
 951  951   */
 952  952  static int
 953  953  fastboot_bename(const char *bename, char *mountpoint, size_t mpsz)
 954  954  {
 955  955          int rc;
 956  956  
 957  957          /*
 958  958           * Attempt to unmount the BE first in case it's already mounted
 959  959           * elsewhere.
 960  960           */
 961  961          (void) halt_exec(BEADM_PROG, "umount", bename, NULL);
 962  962  
 963  963          if ((rc = halt_exec(BEADM_PROG, "mount", bename, FASTBOOT_MOUNTPOINT,
 964  964              NULL)) != 0)
 965  965                  (void) fprintf(stderr,
 966  966                      gettext("%s: Unable to mount BE \"%s\" at %s\n"),
 967  967                      cmdname, bename, FASTBOOT_MOUNTPOINT);
 968  968          else
 969  969                  (void) strlcpy(mountpoint, FASTBOOT_MOUNTPOINT, mpsz);
 970  970  
 971  971          return (rc);
 972  972  }
 973  973  
 974  974  /*
 975  975   * Returns 0 on successful parsing of the arguments;
 976  976   * returns EINVAL on parsing failures that should abort the reboot attempt;
 977  977   * returns other error code to fall back to regular reboot.
 978  978   */
 979  979  static int
 980  980  parse_fastboot_args(char *bootargs_buf, size_t buf_size,
 981  981      int *is_dryrun, const char *bename)
 982  982  {
 983  983          char mountpoint[MAXPATHLEN];
 984  984          char bootargs_saved[BOOTARGS_MAX];
 985  985          char bootargs_scratch[BOOTARGS_MAX];
 986  986          char bootfs_arg[BOOTARGS_MAX];
 987  987          char unixfile[BOOTARGS_MAX];
 988  988          char *head, *newarg;
 989  989          int buflen;             /* length of the bootargs_buf */
 990  990          int mplen;              /* length of the mount point */
 991  991          int rootlen = 0;        /* length of the root argument */
 992  992          int unixlen = 0;        /* length of the unix argument */
 993  993          int off = 0;            /* offset into the new boot argument */
 994  994          int is_zfs = 0;
 995  995          int rc = 0;
 996  996  
 997  997          bzero(mountpoint, sizeof (mountpoint));
 998  998  
 999  999          /*
1000 1000           * If argc is not 0, buflen is length of the argument being passed in;
1001 1001           * else it is 0 as bootargs_buf has been initialized to all 0's.
1002 1002           */
1003 1003          buflen = strlen(bootargs_buf);
1004 1004  
1005 1005          /* Save a copy of the original argument */
1006 1006          bcopy(bootargs_buf, bootargs_saved, buflen);
1007 1007          bzero(&bootargs_saved[buflen], sizeof (bootargs_saved) - buflen);
1008 1008  
1009 1009          /* Save another copy to be used by strtok */
1010 1010          bcopy(bootargs_buf, bootargs_scratch, buflen);
1011 1011          bzero(&bootargs_scratch[buflen], sizeof (bootargs_scratch) - buflen);
1012 1012          head = &bootargs_scratch[0];
1013 1013  
1014 1014          /* Get the first argument */
1015 1015          newarg = strtok(bootargs_scratch, " ");
1016 1016  
1017 1017          /*
1018 1018           * If this is a dry run request, verify that the drivers can handle
1019 1019           * fast reboot.
1020 1020           */
1021 1021          if (newarg && strncasecmp(newarg, "dryrun", strlen("dryrun")) == 0) {
1022 1022                  *is_dryrun = 1;
1023 1023                  (void) system("/usr/sbin/devfsadm");
1024 1024          }
1025 1025  
1026 1026          /*
1027 1027           * Always perform a dry run to identify all the drivers that
1028 1028           * need to implement devo_reset().
1029 1029           */
1030 1030          if (uadmin(A_SHUTDOWN, AD_FASTREBOOT_DRYRUN,
1031 1031              (uintptr_t)bootargs_saved) != 0) {
1032 1032                  (void) fprintf(stderr, gettext("%s: Not all drivers "
1033 1033                      "have implemented quiesce(9E)\n"
1034 1034                      "\tPlease see /var/adm/messages for drivers that haven't\n"
1035 1035                      "\timplemented quiesce(9E).\n"), cmdname);
1036 1036          } else if (*is_dryrun) {
1037 1037                  (void) fprintf(stderr, gettext("%s: All drivers have "
1038 1038                      "implemented quiesce(9E)\n"), cmdname);
1039 1039          }
1040 1040  
1041 1041          /* Return if it is a true dry run. */
1042 1042          if (*is_dryrun)
1043 1043                  return (rc);
1044 1044  
1045 1045  #if defined(__i386)
1046 1046          /* Read boot args from GRUB menu */
1047 1047          if ((bootargs_buf[0] == 0 || isdigit(bootargs_buf[0])) &&
1048 1048              bename == NULL) {
1049 1049                  /*
1050 1050                   * If no boot arguments are given, or a GRUB menu entry
1051 1051                   * number is provided, process the GRUB menu.
1052 1052                   */
1053 1053                  int entnum;
1054 1054                  if (bootargs_buf[0] == 0)
1055 1055                          entnum = GRUB_ENTRY_DEFAULT;
1056 1056                  else {
1057 1057                          errno = 0;
1058 1058                          entnum = strtoul(bootargs_buf, NULL, 10);
1059 1059                          rc = errno;
1060 1060                  }
1061 1061  
1062 1062                  if (rc == 0 && (rc = grub_get_boot_args(&fbarg, NULL,
1063 1063                      entnum)) == 0) {
1064 1064                          if (strlcpy(bootargs_buf, fbarg.gba_bootargs,
1065 1065                              buf_size) >= buf_size) {
1066 1066                                  grub_cleanup_boot_args(&fbarg);
1067 1067                                  bcopy(bootargs_saved, bootargs_buf, buf_size);
1068 1068                                  rc = E2BIG;
1069 1069                          }
1070 1070                  }
1071 1071                  /* Failed to read GRUB menu, fall back to normal reboot */
1072 1072                  if (rc != 0) {
1073 1073                          (void) fprintf(stderr,
1074 1074                              gettext("%s: Failed to process GRUB menu "
1075 1075                              "entry for fast reboot.\n\t%s\n"),
1076 1076                              cmdname, grub_strerror(rc));
1077 1077                          (void) fprintf(stderr,
1078 1078                              gettext("%s: Falling back to regular reboot.\n"),
1079 1079                              cmdname);
1080 1080                          return (-1);
1081 1081                  }
1082 1082                  /* No need to process further */
1083 1083                  fbarg_used = &fbarg;
1084 1084                  fbarg_entnum = entnum;
1085 1085                  return (0);
1086 1086          }
1087 1087  #endif  /* __i386 */
1088 1088  
1089 1089          /* Zero out the boot argument buffer as we will reconstruct it */
1090 1090          bzero(bootargs_buf, buf_size);
1091 1091          bzero(bootfs_arg, sizeof (bootfs_arg));
1092 1092          bzero(unixfile, sizeof (unixfile));
1093 1093  
1094 1094          if (bename && (rc = fastboot_bename(bename, mountpoint,
1095 1095              sizeof (mountpoint))) != 0)
1096 1096                  return (EINVAL);
1097 1097  
1098 1098  
1099 1099          /*
1100 1100           * If BE is not specified, look for disk argument to construct
1101 1101           * mountpoint; if BE has been specified, mountpoint has already been
1102 1102           * constructed.
1103 1103           */
1104 1104          if (newarg && newarg[0] != '-' && !bename) {
1105 1105                  int tmprc;
1106 1106  
1107 1107                  if ((tmprc = validate_disk(newarg, mountpoint)) == 0) {
1108 1108                          /*
1109 1109                           * The first argument is a valid root argument.
1110 1110                           * Get the next argument.
1111 1111                           */
1112 1112                          newarg = strtok(NULL, " ");
1113 1113                          rootlen = (newarg) ? (newarg - head) : buflen;
1114 1114                          (void) strlcpy(fastboot_mounted, mountpoint,
1115 1115                              sizeof (fastboot_mounted));
1116 1116  
1117 1117                  } else if (tmprc == -1) {
1118 1118                          /*
1119 1119                           * Not a disk argument.  Use / as default root.
1120 1120                           */
1121 1121                          bcopy("/", mountpoint, 1);
1122 1122                          bzero(&mountpoint[1], sizeof (mountpoint) - 1);
1123 1123                  } else {
1124 1124                          /*
1125 1125                           * Disk argument, but not valid or not root.
1126 1126                           * Return failure.
1127 1127                           */
1128 1128                          return (EINVAL);
1129 1129                  }
1130 1130          }
1131 1131  
1132 1132          /*
1133 1133           * Make mountpoint the first part of unixfile.
1134 1134           * If there is not disk argument, and BE has not been specified,
1135 1135           * mountpoint could be empty.
1136 1136           */
1137 1137          mplen = strlen(mountpoint);
1138 1138          bcopy(mountpoint, unixfile, mplen);
1139 1139  
1140 1140          /*
1141 1141           * Look for unix argument
1142 1142           */
1143 1143          if (newarg && newarg[0] != '-') {
1144 1144                  bcopy(newarg, &unixfile[mplen], strlen(newarg));
1145 1145                  newarg = strtok(NULL, " ");
1146 1146                  rootlen = (newarg) ? (newarg - head) : buflen;
1147 1147          } else if (mplen != 0) {
1148 1148                  /*
1149 1149                   * No unix argument, but mountpoint is not empty, use
1150 1150                   * /platform/i86pc/$ISADIR/kernel/unix as default.
1151 1151                   */
1152 1152                  char isa[20];
1153 1153  
1154 1154                  if (sysinfo(SI_ARCHITECTURE_64, isa, sizeof (isa)) != -1)
1155 1155                          (void) snprintf(&unixfile[mplen],
1156 1156                              sizeof (unixfile) - mplen,
1157 1157                              "/platform/i86pc/kernel/%s/unix", isa);
1158 1158                  else if (sysinfo(SI_ARCHITECTURE_32, isa, sizeof (isa)) != -1) {
1159 1159                          (void) snprintf(&unixfile[mplen],
1160 1160                              sizeof (unixfile) - mplen,
1161 1161                              "/platform/i86pc/kernel/unix");
1162 1162                  } else {
1163 1163                          (void) fprintf(stderr,
1164 1164                              gettext("%s: Unknown architecture"), cmdname);
1165 1165                          return (EINVAL);
1166 1166                  }
1167 1167          }
1168 1168  
1169 1169          /*
1170 1170           * We now have the complete unix argument.  Verify that it exists and
1171 1171           * is an ELF file.  Split the argument up into mountpoint and unix
1172 1172           * portions again.  This is necessary to handle cases where mountpoint
1173 1173           * is specified on the command line as part of the unix argument,
1174 1174           * such as this:
1175 1175           *      # reboot -f /.alt/platform/i86pc/kernel/amd64/unix
1176 1176           */
1177 1177          unixlen = strlen(unixfile);
1178 1178          if (unixlen > 0) {
1179 1179                  if (validate_unix(unixfile, &mplen, &is_zfs,
1180 1180                      bootfs_arg) != 0) {
1181 1181                          /* Not a valid unix file */
1182 1182                          return (EINVAL);
1183 1183                  } else {
1184 1184                          int space = 0;
1185 1185                          /*
1186 1186                           * Construct boot argument.
1187 1187                           */
1188 1188                          unixlen = strlen(unixfile);
1189 1189  
1190 1190                          /*
1191 1191                           * mdep cannot start with space because bootadm
1192 1192                           * creates bogus menu entries if it does.
1193 1193                           */
1194 1194                          if (mplen > 0) {
1195 1195                                  bcopy(unixfile, bootargs_buf, mplen);
1196 1196                                  (void) strcat(bootargs_buf, " ");
1197 1197                                  space = 1;
1198 1198                          }
1199 1199                          bcopy(&unixfile[mplen], &bootargs_buf[mplen + space],
1200 1200                              unixlen - mplen);
1201 1201                          (void) strcat(bootargs_buf, " ");
1202 1202                          off += unixlen + space + 1;
1203 1203                  }
1204 1204          } else {
1205 1205                  /* Check to see if root is zfs */
1206 1206                  const char      *dp;
1207 1207                  (void) get_zfs_bootfs_arg("/", &dp, &is_zfs, bootfs_arg);
1208 1208          }
1209 1209  
1210 1210          if (is_zfs && (buflen != 0 || bename != NULL))  {
1211 1211                  /* LINTED E_SEC_SPRINTF_UNBOUNDED_COPY */
1212 1212                  off += sprintf(bootargs_buf + off, "%s ", bootfs_arg);
1213 1213          }
1214 1214  
1215 1215          /*
1216 1216           * Copy the rest of the arguments
1217 1217           */
1218 1218          bcopy(&bootargs_saved[rootlen], &bootargs_buf[off], buflen - rootlen);
1219 1219  
1220 1220          return (rc);
1221 1221  }
1222 1222  
1223 1223  #define MAXARGS         5
1224 1224  
1225 1225  static void
1226 1226  do_archives_update(int do_fast_reboot)
1227 1227  {
1228 1228          int     r, i = 0;
1229 1229          pid_t   pid;
1230 1230          char    *cmd_argv[MAXARGS];
1231 1231  
1232 1232  #if defined(__i386)
1233 1233          {
1234 1234                  /*
1235 1235                   * bootadm will complain and exit if not a grub root, so
1236 1236                   * just skip running it.
1237 1237                   */
1238 1238                  struct stat sb;
1239 1239                  if (stat("/boot/grub/stage2", &sb) == -1)
1240 1240                          return;
1241 1241          }
1242 1242  #endif  /* __i386 */
1243 1243  
1244 1244          cmd_argv[i++] = "/sbin/bootadm";
1245 1245          cmd_argv[i++] = "-ea";
1246 1246          cmd_argv[i++] = "update_all";
1247 1247          if (do_fast_reboot)
1248 1248                  cmd_argv[i++] = "fastboot";
1249 1249          cmd_argv[i] = NULL;
1250 1250  
1251 1251          r = posix_spawn(&pid, cmd_argv[0], NULL, NULL, cmd_argv, NULL);
1252 1252  
1253 1253          /* if posix_spawn fails we emit a warning and continue */
1254 1254  
1255 1255          if (r != 0)
  
    | 
      ↓ open down ↓ | 
    1255 lines elided | 
    
      ↑ open up ↑ | 
  
1256 1256                  (void) fprintf(stderr, gettext("%s: WARNING, unable to start "
1257 1257                      "boot archive update\n"), cmdname);
1258 1258          else
1259 1259                  while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
1260 1260                          ;
1261 1261  }
1262 1262  
1263 1263  int
1264 1264  main(int argc, char *argv[])
1265 1265  {
1266      -        char *ttyn = ttyname(STDERR_FILENO);
1267      -
1268 1266          int qflag = 0, needlog = 1, nosync = 0;
1269 1267          int fast_reboot = 0;
1270 1268          int prom_reboot = 0;
1271 1269          uintptr_t mdep = NULL;
1272 1270          int cmd, fcn, c, aval, r;
1273 1271          const char *usage;
1274 1272          const char *optstring;
1275 1273          zoneid_t zoneid = getzoneid();
1276 1274          int need_check_zones = 0;
1277 1275          char bootargs_buf[BOOTARGS_MAX];
1278 1276          char *bootargs_orig = NULL;
1279 1277          char *bename = NULL;
1280 1278  
1281 1279          const char * const resetting = "/etc/svc/volatile/resetting";
1282 1280  
1283 1281          (void) setlocale(LC_ALL, "");
1284 1282          (void) textdomain(TEXT_DOMAIN);
1285 1283  
1286 1284          cmdname = basename(argv[0]);
1287 1285  
1288 1286          if (strcmp(cmdname, "halt") == 0) {
1289 1287                  (void) audit_halt_setup(argc, argv);
1290 1288                  optstring = "dlnqy";
1291 1289                  usage = gettext("usage: %s [ -dlnqy ]\n");
1292 1290                  cmd = A_SHUTDOWN;
1293 1291                  fcn = AD_HALT;
1294 1292          } else if (strcmp(cmdname, "poweroff") == 0) {
1295 1293                  (void) audit_halt_setup(argc, argv);
1296 1294                  optstring = "dlnqy";
1297 1295                  usage = gettext("usage: %s [ -dlnqy ]\n");
1298 1296                  cmd = A_SHUTDOWN;
1299 1297                  fcn = AD_POWEROFF;
1300 1298          } else if (strcmp(cmdname, "reboot") == 0) {
1301 1299                  (void) audit_reboot_setup();
1302 1300  #if defined(__i386)
1303 1301                  optstring = "dlnqpfe:";
1304 1302                  usage = gettext("usage: %s [ -dlnq(p|fe:) ] [ boot args ]\n");
1305 1303  #else
1306 1304                  optstring = "dlnqfp";
1307 1305                  usage = gettext("usage: %s [ -dlnq(p|f) ] [ boot args ]\n");
1308 1306  #endif
1309 1307                  cmd = A_REBOOT;
1310 1308                  fcn = AD_BOOT;
1311 1309          } else {
1312 1310                  (void) fprintf(stderr,
1313 1311                      gettext("%s: not installed properly\n"), cmdname);
1314 1312                  return (1);
1315 1313          }
1316 1314  
1317 1315          while ((c = getopt(argc, argv, optstring)) != EOF) {
1318 1316                  switch (c) {
1319 1317                  case 'd':
1320 1318                          if (zoneid == GLOBAL_ZONEID)
1321 1319                                  cmd = A_DUMP;
1322 1320                          else {
1323 1321                                  (void) fprintf(stderr,
1324 1322                                      gettext("%s: -d only valid from global"
1325 1323                                      " zone\n"), cmdname);
1326 1324                                  return (1);
1327 1325                          }
1328 1326                          break;
  
    | 
      ↓ open down ↓ | 
    51 lines elided | 
    
      ↑ open up ↑ | 
  
1329 1327                  case 'l':
1330 1328                          needlog = 0;
1331 1329                          break;
1332 1330                  case 'n':
1333 1331                          nosync = 1;
1334 1332                          break;
1335 1333                  case 'q':
1336 1334                          qflag = 1;
1337 1335                          break;
1338 1336                  case 'y':
1339      -                        ttyn = NULL;
     1337 +                        /*
     1338 +                         * Option ignored for backwards compatibility.
     1339 +                         */
1340 1340                          break;
1341 1341                  case 'f':
1342 1342                          fast_reboot = 1;
1343 1343                          break;
1344 1344                  case 'p':
1345 1345                          prom_reboot = 1;
1346 1346                          break;
1347 1347  #if defined(__i386)
1348 1348                  case 'e':
1349 1349                          bename = optarg;
1350 1350                          break;
1351 1351  #endif
1352 1352                  default:
1353 1353                          /*
1354 1354                           * TRANSLATION_NOTE
1355 1355                           * Don't translate the words "halt" or "reboot"
1356 1356                           */
1357 1357                          (void) fprintf(stderr, usage, cmdname);
1358 1358                          return (1);
1359 1359                  }
1360 1360          }
1361 1361  
1362 1362          argc -= optind;
1363 1363          argv += optind;
1364 1364  
1365 1365          if (argc != 0) {
1366 1366                  if (fcn != AD_BOOT) {
1367 1367                          (void) fprintf(stderr, usage, cmdname);
1368 1368                          return (1);
1369 1369                  }
1370 1370  
1371 1371                  /* Gather the arguments into bootargs_buf. */
1372 1372                  if (gather_args(argv, bootargs_buf, sizeof (bootargs_buf)) !=
1373 1373                      0) {
1374 1374                          (void) fprintf(stderr,
1375 1375                              gettext("%s: Boot arguments too long.\n"), cmdname);
1376 1376                          return (1);
1377 1377                  }
1378 1378  
1379 1379                  bootargs_orig = strdup(bootargs_buf);
1380 1380                  mdep = (uintptr_t)bootargs_buf;
1381 1381          } else {
1382 1382                  /*
1383 1383                   * Initialize it to 0 in case of fastboot, the buffer
1384 1384                   * will be used.
1385 1385                   */
1386 1386                  bzero(bootargs_buf, sizeof (bootargs_buf));
1387 1387          }
1388 1388  
1389 1389          if (geteuid() != 0) {
1390 1390                  (void) fprintf(stderr,
1391 1391                      gettext("%s: permission denied\n"), cmdname);
1392 1392                  goto fail;
1393 1393          }
1394 1394  
1395 1395          if (fast_reboot && prom_reboot) {
1396 1396                  (void) fprintf(stderr,
1397 1397                      gettext("%s: -p and -f are mutually exclusive\n"),
1398 1398                      cmdname);
1399 1399                  return (EINVAL);
1400 1400          }
1401 1401          /*
1402 1402           * Check whether fast reboot is the default operating mode
1403 1403           */
1404 1404          if (fcn == AD_BOOT && !fast_reboot && !prom_reboot &&
1405 1405              zoneid == GLOBAL_ZONEID) {
1406 1406                  fast_reboot = scf_is_fastboot_default();
1407 1407  
1408 1408          }
1409 1409  
1410 1410          if (bename && !fast_reboot)     {
1411 1411                  (void) fprintf(stderr, gettext("%s: -e only valid with -f\n"),
1412 1412                      cmdname);
1413 1413                  return (EINVAL);
1414 1414          }
1415 1415  
1416 1416  #if defined(__sparc)
1417 1417          if (fast_reboot) {
1418 1418                  fast_reboot = 2;        /* need to distinguish each case */
1419 1419          }
1420 1420  #endif
1421 1421  
1422 1422          /*
1423 1423           * If fast reboot, do some sanity check on the argument
1424 1424           */
1425 1425          if (fast_reboot == 1) {
1426 1426                  int rc;
1427 1427                  int is_dryrun = 0;
1428 1428  
1429 1429                  if (zoneid != GLOBAL_ZONEID)    {
1430 1430                          (void) fprintf(stderr,
1431 1431                              gettext("%s: Fast reboot only valid from global"
1432 1432                              " zone\n"), cmdname);
1433 1433                          return (EINVAL);
1434 1434                  }
1435 1435  
1436 1436                  rc = parse_fastboot_args(bootargs_buf, sizeof (bootargs_buf),
1437 1437                      &is_dryrun, bename);
1438 1438  
1439 1439                  /*
1440 1440                   * If dry run, or if arguments are invalid, return.
1441 1441                   */
1442 1442                  if (is_dryrun)
1443 1443                          return (rc);
1444 1444                  else if (rc == EINVAL)
1445 1445                          goto fail;
1446 1446                  else if (rc != 0)
1447 1447                          fast_reboot = 0;
1448 1448  
1449 1449                  /*
1450 1450                   * For all the other errors, we continue on in case user
1451 1451                   * user want to force fast reboot, or fall back to regular
1452 1452                   * reboot.
  
    | 
      ↓ open down ↓ | 
    103 lines elided | 
    
      ↑ open up ↑ | 
  
1453 1453                   */
1454 1454                  if (strlen(bootargs_buf) != 0)
1455 1455                          mdep = (uintptr_t)bootargs_buf;
1456 1456          }
1457 1457  
1458 1458  #if 0   /* For debugging */
1459 1459          if (mdep != NULL)
1460 1460                  (void) fprintf(stderr, "mdep = %s\n", (char *)mdep);
1461 1461  #endif
1462 1462  
1463      -        if (fcn != AD_BOOT && ttyn != NULL &&
1464      -            strncmp(ttyn, "/dev/term/", strlen("/dev/term/")) == 0) {
1465      -                /*
1466      -                 * TRANSLATION_NOTE
1467      -                 * Don't translate ``halt -y''
1468      -                 */
1469      -                (void) fprintf(stderr,
1470      -                    gettext("%s: dangerous on a dialup;"), cmdname);
1471      -                (void) fprintf(stderr,
1472      -                    gettext("use ``%s -y'' if you are really sure\n"), cmdname);
1473      -                goto fail;
1474      -        }
1475      -
1476 1463          if (needlog) {
1477 1464                  char *user = getlogin();
1478 1465                  struct passwd *pw;
1479 1466                  char *tty;
1480 1467  
1481 1468                  openlog(cmdname, 0, LOG_AUTH);
1482 1469                  if (user == NULL && (pw = getpwuid(getuid())) != NULL)
1483 1470                          user = pw->pw_name;
1484 1471                  if (user == NULL)
1485 1472                          user = "root";
1486 1473  
1487 1474                  tty = ttyname(1);
1488 1475  
1489 1476                  if (tty == NULL)
1490 1477                          syslog(LOG_CRIT, "initiated by %s", user);
1491 1478                  else
1492 1479                          syslog(LOG_CRIT, "initiated by %s on %s", user, tty);
1493 1480          }
1494 1481  
1495 1482          /*
1496 1483           * We must assume success and log it before auditd is terminated.
1497 1484           */
1498 1485          if (fcn == AD_BOOT)
1499 1486                  aval = audit_reboot_success();
1500 1487          else
1501 1488                  aval = audit_halt_success();
1502 1489  
1503 1490          if (aval == -1) {
1504 1491                  (void) fprintf(stderr,
1505 1492                      gettext("%s: can't turn off auditd\n"), cmdname);
1506 1493                  if (needlog)
1507 1494                          (void) sleep(5); /* Give syslogd time to record this */
1508 1495          }
1509 1496  
1510 1497          (void) signal(SIGHUP, SIG_IGN); /* for remote connections */
1511 1498  
1512 1499          /*
1513 1500           * We start to fork a bunch of zoneadms to halt any active zones.
1514 1501           * This will proceed with halt in parallel until we call
1515 1502           * check_zone_haltedness later on.
1516 1503           */
1517 1504          if (zoneid == GLOBAL_ZONEID && cmd != A_DUMP) {
1518 1505                  if (!qflag)
1519 1506                          need_check_zones = halt_zones();
1520 1507          }
1521 1508  
1522 1509  #if defined(__i386)
1523 1510          /* set new default entry in the GRUB entry */
1524 1511          if (fbarg_entnum != GRUB_ENTRY_DEFAULT) {
1525 1512                  char buf[32];
1526 1513                  (void) snprintf(buf, sizeof (buf), "default=%u", fbarg_entnum);
1527 1514                  (void) halt_exec(BOOTADM_PROG, "set-menu", buf, NULL);
1528 1515          }
1529 1516  #endif  /* __i386 */
1530 1517  
1531 1518          /* if we're dumping, do the archive update here and don't defer it */
1532 1519          if (cmd == A_DUMP && zoneid == GLOBAL_ZONEID && !nosync)
1533 1520                  do_archives_update(fast_reboot);
1534 1521  
1535 1522          /*
1536 1523           * If we're not forcing a crash dump, mark the system as quiescing for
1537 1524           * smf(5)'s benefit, and idle the init process.
1538 1525           */
1539 1526          if (cmd != A_DUMP) {
1540 1527                  if (direct_init(PCDSTOP) == -1) {
1541 1528                          /*
1542 1529                           * TRANSLATION_NOTE
1543 1530                           * Don't translate the word "init"
1544 1531                           */
1545 1532                          (void) fprintf(stderr,
1546 1533                              gettext("%s: can't idle init\n"), cmdname);
1547 1534                          goto fail;
1548 1535                  }
1549 1536  
1550 1537                  if (creat(resetting, 0755) == -1)
1551 1538                          (void) fprintf(stderr,
1552 1539                              gettext("%s: could not create %s.\n"),
1553 1540                              cmdname, resetting);
1554 1541          }
1555 1542  
1556 1543          /*
1557 1544           * Make sure we don't get stopped by a jobcontrol shell
1558 1545           * once we start killing everybody.
1559 1546           */
1560 1547          (void) signal(SIGTSTP, SIG_IGN);
1561 1548          (void) signal(SIGTTIN, SIG_IGN);
1562 1549          (void) signal(SIGTTOU, SIG_IGN);
1563 1550          (void) signal(SIGPIPE, SIG_IGN);
1564 1551          (void) signal(SIGTERM, SIG_IGN);
1565 1552  
1566 1553          /*
1567 1554           * Try to stop gdm so X has a chance to return the screen and
1568 1555           * keyboard to a sane state.
1569 1556           */
1570 1557          if (fast_reboot == 1 && stop_gdm() != 0) {
1571 1558                  (void) fprintf(stderr,
1572 1559                      gettext("%s: Falling back to regular reboot.\n"), cmdname);
1573 1560                  fast_reboot = 0;
1574 1561                  mdep = (uintptr_t)bootargs_orig;
1575 1562          } else if (bootargs_orig) {
1576 1563                  free(bootargs_orig);
1577 1564          }
1578 1565  
1579 1566          if (cmd != A_DUMP) {
1580 1567                  /*
1581 1568                   * Stop all restarters so they do not try to restart services
1582 1569                   * that are terminated.
1583 1570                   */
1584 1571                  stop_restarters();
1585 1572  
1586 1573                  /*
1587 1574                   * Wait a little while for zones to shutdown.
1588 1575                   */
1589 1576                  if (need_check_zones) {
1590 1577                          check_zones_haltedness();
1591 1578  
1592 1579                          (void) fprintf(stderr,
1593 1580                              gettext("%s: Completing system halt.\n"),
1594 1581                              cmdname);
1595 1582                  }
1596 1583          }
1597 1584  
1598 1585          /*
1599 1586           * If we're not forcing a crash dump, give everyone 5 seconds to
1600 1587           * handle a SIGTERM and clean up properly.
1601 1588           */
1602 1589          if (cmd != A_DUMP) {
1603 1590                  int     start, end, delta;
1604 1591  
1605 1592                  (void) kill(-1, SIGTERM);
1606 1593                  start = time(NULL);
1607 1594  
1608 1595                  if (zoneid == GLOBAL_ZONEID && !nosync)
1609 1596                          do_archives_update(fast_reboot);
1610 1597  
1611 1598                  end = time(NULL);
1612 1599                  delta = end - start;
1613 1600                  if (delta < 5)
1614 1601                          (void) sleep(5 - delta);
1615 1602          }
1616 1603  
1617 1604          (void) signal(SIGINT, SIG_IGN);
1618 1605  
1619 1606          if (!nosync) {
1620 1607                  struct utmpx wtmpx;
1621 1608  
1622 1609                  bzero(&wtmpx, sizeof (struct utmpx));
1623 1610                  (void) strcpy(wtmpx.ut_line, "~");
1624 1611                  (void) time(&wtmpx.ut_tv.tv_sec);
1625 1612  
1626 1613                  if (cmd == A_DUMP)
1627 1614                          (void) strcpy(wtmpx.ut_name, "crash dump");
1628 1615                  else
1629 1616                          (void) strcpy(wtmpx.ut_name, "shutdown");
1630 1617  
1631 1618                  (void) updwtmpx(WTMPX_FILE, &wtmpx);
1632 1619                  sync();
1633 1620          }
1634 1621  
1635 1622          if (cmd == A_DUMP && nosync != 0)
1636 1623                  (void) uadmin(A_DUMP, AD_NOSYNC, NULL);
1637 1624  
1638 1625          if (fast_reboot)
1639 1626                  fcn = AD_FASTREBOOT;
1640 1627  
1641 1628          if (uadmin(cmd, fcn, mdep) == -1)
1642 1629                  (void) fprintf(stderr, "%s: uadmin failed: %s\n",
1643 1630                      cmdname, strerror(errno));
1644 1631          else
1645 1632                  (void) fprintf(stderr, "%s: uadmin unexpectedly returned 0\n",
1646 1633                      cmdname);
1647 1634  
1648 1635          do {
1649 1636                  r = remove(resetting);
1650 1637          } while (r != 0 && errno == EINTR);
1651 1638  
1652 1639          if (r != 0 && errno != ENOENT)
1653 1640                  (void) fprintf(stderr, gettext("%s: could not remove %s.\n"),
1654 1641                      cmdname, resetting);
1655 1642  
1656 1643          if (direct_init(PCRUN) == -1) {
1657 1644                  /*
1658 1645                   * TRANSLATION_NOTE
1659 1646                   * Don't translate the word "init"
1660 1647                   */
1661 1648                  (void) fprintf(stderr,
1662 1649                      gettext("%s: can't resume init\n"), cmdname);
1663 1650          }
1664 1651  
1665 1652          continue_restarters();
1666 1653  
1667 1654          if (get_initpid() != -1)
1668 1655                  /* tell init to restate current level */
1669 1656                  (void) kill(get_initpid(), SIGHUP);
1670 1657  
1671 1658  fail:
1672 1659          if (fcn == AD_BOOT)
1673 1660                  (void) audit_reboot_fail();
1674 1661          else
1675 1662                  (void) audit_halt_fail();
1676 1663  
1677 1664          if (fast_reboot == 1) {
1678 1665                  if (bename) {
1679 1666                          (void) halt_exec(BEADM_PROG, "umount", bename, NULL);
1680 1667  
1681 1668                  } else if (strlen(fastboot_mounted) != 0) {
1682 1669                          (void) umount(fastboot_mounted);
1683 1670  #if defined(__i386)
1684 1671                  } else if (fbarg_used != NULL) {
1685 1672                          grub_cleanup_boot_args(fbarg_used);
1686 1673  #endif  /* __i386 */
1687 1674                  }
1688 1675          }
1689 1676  
1690 1677          return (1);
1691 1678  }
  
    | 
      ↓ open down ↓ | 
    206 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX