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

*** 19,28 **** --- 19,29 ---- * 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,106 **** --- 98,108 ---- #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,119 **** --- 112,122 ---- 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,152 **** 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" }; if (zcmd >= sizeof (zcmdstr) / sizeof (*zcmdstr)) return ("unknown"); else --- 145,156 ---- 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", ! "shutdown" }; if (zcmd >= sizeof (zcmdstr) / sizeof (*zcmdstr)) return ("unknown"); else
*** 984,993 **** --- 988,1124 ---- 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,1068 **** --- 1190,1200 ---- 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,1146 **** /* * 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) { zerror(&logsys, B_FALSE, "invalid command %d", (int)cmd); goto out; } if (kernelcall && (cmd != Z_HALT && cmd != Z_REBOOT)) { --- 1267,1279 ---- /* * Check for validity of command. */ if (cmd != Z_READY && cmd != Z_BOOT && cmd != Z_FORCEBOOT && ! 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,1231 **** --- 1355,1365 ---- (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,1361 **** --- 1486,1496 ---- 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,1451 **** --- 1577,1591 ---- 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,1474 **** --- 1605,1619 ---- */ 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;