Print this page
2594 implement graceful shutdown for local zones in zoneadm

@@ -19,10 +19,11 @@
  * CDDL HEADER END
  */
 
 /*
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
  */
 
 /*
  * zoneadm is a command interpreter for zone administration.  It is all in
  * C (i.e., no lex/yacc), and all the argument passing is argc/argv based.

@@ -128,10 +129,11 @@
 
 #define SHELP_HELP      "help"
 #define SHELP_BOOT      "boot [-- boot_arguments]"
 #define SHELP_HALT      "halt"
 #define SHELP_READY     "ready"
+#define SHELP_SHUTDOWN  "shutdown [-r [-- boot_arguments]]"
 #define SHELP_REBOOT    "reboot [-- boot_arguments]"
 #define SHELP_LIST      "list [-cipv]"
 #define SHELP_VERIFY    "verify"
 #define SHELP_INSTALL   "install [brand-specific args]"
 #define SHELP_UNINSTALL "uninstall [-F] [brand-specific args]"

@@ -150,10 +152,11 @@
 
 
 static int help_func(int argc, char *argv[]);
 static int ready_func(int argc, char *argv[]);
 static int boot_func(int argc, char *argv[]);
+static int shutdown_func(int argc, char *argv[]);
 static int halt_func(int argc, char *argv[]);
 static int reboot_func(int argc, char *argv[]);
 static int list_func(int argc, char *argv[]);
 static int verify_func(int argc, char *argv[]);
 static int install_func(int argc, char *argv[]);

@@ -177,10 +180,11 @@
 static struct cmd cmdtab[] = {
         { CMD_HELP,             "help",         SHELP_HELP,     help_func },
         { CMD_BOOT,             "boot",         SHELP_BOOT,     boot_func },
         { CMD_HALT,             "halt",         SHELP_HALT,     halt_func },
         { CMD_READY,            "ready",        SHELP_READY,    ready_func },
+        { CMD_SHUTDOWN,         "shutdown",     SHELP_SHUTDOWN, shutdown_func },
         { CMD_REBOOT,           "reboot",       SHELP_REBOOT,   reboot_func },
         { CMD_LIST,             "list",         SHELP_LIST,     list_func },
         { CMD_VERIFY,           "verify",       SHELP_VERIFY,   verify_func },
         { CMD_INSTALL,          "install",      SHELP_INSTALL,  install_func },
         { CMD_UNINSTALL,        "uninstall",    SHELP_UNINSTALL,

@@ -230,10 +234,14 @@
                 return (gettext("Halts specified zone, bypassing shutdown "
                     "scripts and removing runtime\n\tresources of the zone."));
         case CMD_READY:
                 return (gettext("Prepares a zone for running applications but "
                     "does not start any user\n\tprocesses in the zone."));
+        case CMD_SHUTDOWN:
+                return (gettext("Gracefully shutdown the zone or reboot if "
+                    "the '-r' option is specified.\n\t"
+                    "See zoneadm(1m) for valid boot arguments."));
         case CMD_REBOOT:
                 return (gettext("Restarts the zone (equivalent to a halt / "
                     "boot sequence).\n\tFails if the zone is not active.  "
                     "See zoneadm(1m) for valid boot\n\targuments."));
         case CMD_LIST:

@@ -1532,10 +1540,11 @@
                 (void) strlcpy(authname, ZONE_CLONEFROM_AUTH, MAXAUTHS);
                 break;
         case CMD_BOOT:
         case CMD_HALT:
         case CMD_READY:
+        case CMD_SHUTDOWN:
         case CMD_REBOOT:
         case CMD_SYSBOOT:
         case CMD_VERIFY:
         case CMD_INSTALL:
         case CMD_UNINSTALL:

@@ -1594,10 +1603,14 @@
                 switch (cmd_num) {
                 case CMD_HALT:
                         zerror(gettext("use %s to %s this zone."), "halt(1M)",
                             cmd_to_str(cmd_num));
                         break;
+                case CMD_SHUTDOWN:
+                        zerror(gettext("use %s to %s this zone."),
+                            "shutdown(1M)", cmd_to_str(cmd_num));
+                        break;
                 case CMD_REBOOT:
                         zerror(gettext("use %s to %s this zone."),
                             "reboot(1M)", cmd_to_str(cmd_num));
                         break;
                 default:

@@ -1823,10 +1836,79 @@
         zarg.cmd = Z_HALT;
         return ((zonecfg_call_zoneadmd(target_zone, &zarg, locale,
             B_TRUE) == 0) ?  Z_OK : Z_ERR);
 }
 
+static int
+shutdown_func(int argc, char *argv[])
+{
+        zone_cmd_arg_t zarg;
+        int arg;
+        boolean_t reboot = B_FALSE;
+
+        zarg.cmd = Z_SHUTDOWN;
+
+        if (zonecfg_in_alt_root()) {
+                zerror(gettext("cannot shut down zone in alternate root"));
+                return (Z_ERR);
+        }
+
+        optind = 0;
+        while ((arg = getopt(argc, argv, "?r")) != EOF) {
+                switch (arg) {
+                case '?':
+                        sub_usage(SHELP_SHUTDOWN, CMD_SHUTDOWN);
+                        return (optopt == '?' ? Z_OK : Z_USAGE);
+                case 'r':
+                        reboot = B_TRUE;
+                        break;
+                default:
+                        sub_usage(SHELP_SHUTDOWN, CMD_SHUTDOWN);
+                        return (Z_USAGE);
+                }
+        }
+
+        zarg.bootbuf[0] = '\0';
+        for (; optind < argc; optind++) {
+                if (strlcat(zarg.bootbuf, argv[optind],
+                    sizeof (zarg.bootbuf)) >= sizeof (zarg.bootbuf)) {
+                        zerror(gettext("Boot argument list too long"));
+                        return (Z_ERR);
+                }
+                if (optind < argc - 1)
+                        if (strlcat(zarg.bootbuf, " ", sizeof (zarg.bootbuf)) >=
+                            sizeof (zarg.bootbuf)) {
+                                zerror(gettext("Boot argument list too long"));
+                                return (Z_ERR);
+                        }
+        }
+
+        /*
+         * zoneadmd should be the one to decide whether or not to proceed,
+         * so even though it seems that the third parameter below should
+         * perhaps be B_TRUE, it really shouldn't be.
+         */
+        if (sanity_check(target_zone, CMD_SHUTDOWN, B_TRUE, B_FALSE, B_FALSE)
+            != Z_OK)
+                return (Z_ERR);
+
+        if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != Z_OK)
+                return (Z_ERR);
+
+        if (reboot) {
+                if (sanity_check(target_zone, CMD_BOOT, B_FALSE, B_FALSE,
+                    B_FALSE) != Z_OK)
+                        return (Z_ERR);
+
+                zarg.cmd = Z_BOOT;
+                if (zonecfg_call_zoneadmd(target_zone, &zarg, locale,
+                    B_TRUE) != Z_OK)
+                        return (Z_ERR);
+        }
+        return (Z_OK);
+}
+
 static int
 reboot_func(int argc, char *argv[])
 {
         zone_cmd_arg_t zarg;
         int arg;