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.
  */
 
 /*
  * zoneadmd manages zones; one zoneadmd process is launched for each
  * non-global zone on the system.  This daemon juggles four jobs:

@@ -97,10 +98,11 @@
 #include <sys/brand.h>
 #include <sys/contract/process.h>
 #include <sys/ctfs.h>
 #include <libdladm.h>
 #include <sys/dls_mgmt.h>
+#include <libscf.h>
 
 #include <libzonecfg.h>
 #include <zonestat_impl.h>
 #include "zoneadmd.h"
 

@@ -110,10 +112,11 @@
 char default_brand[MAXNAMELEN];
 char brand_name[MAXNAMELEN];
 boolean_t zone_isnative;
 boolean_t zone_iscluster;
 boolean_t zone_islabeled;
+boolean_t shutdown_in_progress;
 static zoneid_t zone_id;
 dladm_handle_t dld_handle = NULL;
 
 static char pre_statechg_hook[2 * MAXPATHLEN];
 static char post_statechg_hook[2 * MAXPATHLEN];

@@ -142,11 +145,12 @@
 z_cmd_name(zone_cmd_t zcmd)
 {
         /* This list needs to match the enum in sys/zone.h */
         static const char *zcmdstr[] = {
                 "ready", "boot", "forceboot", "reboot", "halt",
-                "note_uninstalling", "mount", "forcemount", "unmount"
+                "note_uninstalling", "mount", "forcemount", "unmount",
+                "shutdown"
         };
 
         if (zcmd >= sizeof (zcmdstr) / sizeof (*zcmdstr))
                 return ("unknown");
         else

@@ -984,10 +988,137 @@
                 return (-1);
 
         return (0);
 }
 
+static int
+zone_graceful_shutdown(zlog_t *zlogp)
+{
+        zoneid_t zoneid;
+        pid_t child;
+        char cmdbuf[MAXPATHLEN];
+        brand_handle_t bh = NULL;
+        char zpath[MAXPATHLEN];
+        ctid_t ct;
+        int tmpl_fd;
+        int child_status;
+
+        if (shutdown_in_progress) {
+                zerror(zlogp, B_FALSE, "shutdown already in progress");
+                return (-1);
+        }
+
+        if ((zoneid = getzoneidbyname(zone_name)) == -1) {
+                zerror(zlogp, B_TRUE, "unable to get zoneid");
+                return (-1);
+        }
+
+        /* Get a handle to the brand info for this zone */
+        if ((bh = brand_open(brand_name)) == NULL) {
+                zerror(zlogp, B_FALSE, "unable to determine zone brand");
+                return (-1);
+        }
+
+        if (zone_get_zonepath(zone_name, zpath, sizeof (zpath)) != Z_OK) {
+                zerror(zlogp, B_FALSE, "unable to determine zone path");
+                brand_close(bh);
+                return (-1);
+        }
+
+        /*
+         * If there is a brand 'shutdown' callback, execute it now to give the
+         * brand a chance to cleanup any custom configuration.
+         */
+        (void) strcpy(cmdbuf, EXEC_PREFIX);
+        if (brand_get_shutdown(bh, zone_name, zpath, cmdbuf + EXEC_LEN,
+            sizeof (cmdbuf) - EXEC_LEN) != 0 || strlen(cmdbuf) <= EXEC_LEN) {
+                (void) strcat(cmdbuf, SHUTDOWN_DEFAULT);
+        }
+        brand_close(bh);
+
+        if ((tmpl_fd = init_template()) == -1) {
+                zerror(zlogp, B_TRUE, "failed to create contract");
+                return (-1);
+        }
+
+        if ((child = fork()) == -1) {
+                (void) ct_tmpl_clear(tmpl_fd);
+                (void) close(tmpl_fd);
+                zerror(zlogp, B_TRUE, "failed to fork");
+                return (-1);
+        } else if (child == 0) {
+                (void) ct_tmpl_clear(tmpl_fd);
+                if (zone_enter(zoneid) == -1) {
+                        _exit(errno);
+                }
+                _exit(execl("/bin/sh", "sh", "-c", cmdbuf, (char *)NULL));
+        }
+
+        if (contract_latest(&ct) == -1)
+                ct = -1;
+        (void) ct_tmpl_clear(tmpl_fd);
+        (void) close(tmpl_fd);
+
+        if (waitpid(child, &child_status, 0) != child) {
+                /* unexpected: we must have been signalled */
+                (void) contract_abandon_id(ct);
+                return (-1);
+        }
+
+        (void) contract_abandon_id(ct);
+        if (WEXITSTATUS(child_status) != 0) {
+                errno = WEXITSTATUS(child_status);
+                zerror(zlogp, B_FALSE, "unable to shutdown zone");
+                return (-1);
+        }
+
+        shutdown_in_progress = B_TRUE;
+
+        return (0);
+}
+
+static int
+zone_wait_shutdown(zlog_t *zlogp)
+{
+        zone_state_t zstate;
+        uint64_t *tm = NULL;
+        scf_simple_prop_t *prop = NULL;
+        int timeout;
+        int tries;
+        int rc = -1;
+
+        /* Get default stop timeout from SMF framework */
+        timeout = SHUTDOWN_WAIT;
+        if ((prop = scf_simple_prop_get(NULL, SHUTDOWN_FMRI, "stop",
+            SCF_PROPERTY_TIMEOUT)) != NULL) {
+                if ((tm = scf_simple_prop_next_count(prop)) != NULL) {
+                        if (tm != 0)
+                                timeout = *tm;
+                }
+                scf_simple_prop_free(prop);
+        }
+
+        /* allow time for zone to shutdown cleanly */
+        for (tries = 0; tries < timeout; tries ++) {
+                (void) sleep(1);
+                if (zone_get_state(zone_name, &zstate) == Z_OK &&
+                    zstate == ZONE_STATE_INSTALLED) {
+                        rc = 0;
+                        break;
+                }
+        }
+
+        if (rc != 0)
+                zerror(zlogp, B_FALSE, "unable to shutdown zone");
+
+        shutdown_in_progress = B_FALSE;
+
+        return (rc);
+}
+
+
+
 /*
  * Generate AUE_zone_state for a command that boots a zone.
  */
 static void
 audit_put_record(zlog_t *zlogp, ucred_t *uc, int return_val,

@@ -1059,10 +1190,11 @@
         zlog_t *zlogp;
         zone_cmd_rval_t *rvalp;
         size_t rlen = getpagesize(); /* conservative */
         fs_callback_t cb;
         brand_handle_t bh;
+        boolean_t wait_shut = B_FALSE;
 
         /* LINTED E_BAD_PTR_CAST_ALIGN */
         zargp = (zone_cmd_arg_t *)args;
 
         /*

@@ -1135,12 +1267,13 @@
 
         /*
          * Check for validity of command.
          */
         if (cmd != Z_READY && cmd != Z_BOOT && cmd != Z_FORCEBOOT &&
-            cmd != Z_REBOOT && cmd != Z_HALT && cmd != Z_NOTE_UNINSTALLING &&
-            cmd != Z_MOUNT && cmd != Z_FORCEMOUNT && cmd != Z_UNMOUNT) {
+            cmd != Z_REBOOT && cmd != Z_SHUTDOWN && cmd != Z_HALT &&
+            cmd != Z_NOTE_UNINSTALLING && cmd != Z_MOUNT &&
+            cmd != Z_FORCEMOUNT && cmd != Z_UNMOUNT) {
                 zerror(&logsys, B_FALSE, "invalid command %d", (int)cmd);
                 goto out;
         }
 
         if (kernelcall && (cmd != Z_HALT && cmd != Z_REBOOT)) {

@@ -1222,10 +1355,11 @@
                                 (void) zone_halt(zlogp, B_FALSE, B_FALSE,
                                     zstate);
                                 eventstream_write(Z_EVT_ZONE_BOOTFAILED);
                         }
                         break;
+                case Z_SHUTDOWN:
                 case Z_HALT:
                         if (kernelcall) /* Invalid; can't happen */
                                 abort();
                         /*
                          * We could have two clients racing to halt this

@@ -1352,10 +1486,11 @@
                         if ((rval = zone_halt(zlogp, B_FALSE, B_FALSE, zstate))
                             != 0)
                                 break;
                         eventstream_write(Z_EVT_ZONE_HALTED);
                         break;
+                case Z_SHUTDOWN:
                 case Z_REBOOT:
                 case Z_NOTE_UNINSTALLING:
                 case Z_MOUNT:
                 case Z_UNMOUNT:
                         if (kernelcall) /* Invalid; can't happen */

@@ -1442,10 +1577,15 @@
                                     zstate);
                                 eventstream_write(Z_EVT_ZONE_BOOTFAILED);
                         }
                         boot_args[0] = '\0';
                         break;
+                case Z_SHUTDOWN:
+                        if ((rval = zone_graceful_shutdown(zlogp)) == 0) {
+                                wait_shut = B_TRUE;
+                        }
+                        break;
                 case Z_NOTE_UNINSTALLING:
                 case Z_MOUNT:
                 case Z_UNMOUNT:
                         zerror(zlogp, B_FALSE, "%s operation is invalid "
                             "for zones in state '%s'", z_cmd_name(cmd),

@@ -1465,10 +1605,15 @@
          */
         eventstream_write(Z_EVT_NULL);
 
 out:
         (void) mutex_unlock(&lock);
+
+        /* Wait for the Z_SHUTDOWN commands to complete */
+        if (wait_shut)
+                rval = zone_wait_shutdown(zlogp);
+
         if (kernelcall) {
                 rvalp = NULL;
                 rlen = 0;
         } else {
                 rvalp->rval = rval;