18017 rzd: add boot/reboot operations to zoneadmin module
authorStephen Talley <stephen.talley@oracle.com>
Tue, 15 Mar 2011 15:29:02 -0400
changeset 676 301e75e87e60
parent 675 39b46c15043c
child 677 fbc09f84f958
18017 rzd: add boot/reboot operations to zoneadmin module
usr/src/apis/zoneadmin.xml
usr/src/cmd/rad/mod/zoneadmin/mod_zoneadmin.c
--- a/usr/src/apis/zoneadmin.xml	Tue Mar 15 10:02:32 2011 -0400
+++ b/usr/src/apis/zoneadmin.xml	Tue Mar 15 15:29:02 2011 -0400
@@ -40,6 +40,18 @@
 		if the given zone is invalid
 	    </doc>
 	</value>
+	<value name="INVALID_ZONE_PLATFORM">
+	    <doc>
+                if the given zone's platform is inappropriate for the requested
+                platform-specific operation
+	    </doc>
+	</value>
+	<value name="INVALID_ZONE_STATE">
+	    <doc>
+                if the given zone's state is inappropriate for the requested
+                operation
+	    </doc>
+	</value>
 	<value name="COMMAND_FAILED">
 	    <doc>
                 if a command to effect changes to or retrieve configuration from
@@ -93,6 +105,21 @@
 	    </argument>
 	</method>
 
+	<method name="boot">
+	    <summary>
+		boot the given zone
+	    </summary>
+	    <doc>
+                Boot the given zone.
+	    </doc>
+	    <error typeref="ZoneAdminError"/>
+	    <argument name="zone" type="string">
+		<summary>
+		    the name of the non-global zone
+		</summary>
+	    </argument>
+	</method>
+
 	<method name="close">
 	    <summary>
 		close a rad connection
@@ -134,7 +161,7 @@
 	    <doc>
                 Halt the given zone, gracefully or ungracefully.
 	    </doc>
-	    <error/>
+	    <error typeref="ZoneAdminError"/>
 	    <argument name="zone" type="string">
 		<summary>
 		    the name of the non-global zone
@@ -160,7 +187,7 @@
 		    a token used to read from and write to the new connection
 		</summary>
 	    </result>
-	    <error />
+	    <error typeref="ZoneAdminError"/>
 	    <argument name="zone" type="string">
 		<summary>
 		    the non-global zone to connect to
@@ -181,7 +208,7 @@
 		    a token used to read from and write to the new connection
 		</summary>
 	    </result>
-	    <error />
+	    <error typeref="ZoneAdminError"/>
 	    <argument name="zone" type="string">
 		<summary>
 		    the non-global zone to connect to
@@ -226,6 +253,21 @@
 	    </argument>
 	</method>
 
+	<method name="reboot">
+	    <summary>
+		reboot the given zone
+	    </summary>
+	    <doc>
+                Reboot the given zone.
+	    </doc>
+	    <error typeref="ZoneAdminError"/>
+	    <argument name="zone" type="string">
+		<summary>
+		    the name of the non-global zone
+		</summary>
+	    </argument>
+	</method>
+
 	<method name="write">
 	    <summary>
 		write data
--- a/usr/src/cmd/rad/mod/zoneadmin/mod_zoneadmin.c	Tue Mar 15 10:02:32 2011 -0400
+++ b/usr/src/cmd/rad/mod/zoneadmin/mod_zoneadmin.c	Tue Mar 15 15:29:02 2011 -0400
@@ -48,18 +48,52 @@
 
 static conerr_t fd_to_token(int fd, data_t **token, data_t **error);
 
-static int has_auth(const char *zonename);
+static conerr_t forkexec(const char **argv, data_t **error);
 
 static conerr_t make_error(data_t *errval, data_t **error);
 
 static conerr_t token_to_fd(data_t *arg, int *fd, data_t **error);
 
+static conerr_t validate_zone(const char *zone, const int valid_states[],
+    data_t **error);
+
 /*
  * Static data
  */
 
 static rad_modinfo_t modinfo = { "zoneadmin", "zone connectivity" };
 
+static const int VALID_STATES_BOOT[] = {
+	ZONE_STATE_INSTALLED,
+	ZONE_STATE_READY,
+	ZONE_STATE_SHUTTING_DOWN,
+	ZONE_STATE_DOWN,
+	-1
+};
+
+static const int VALID_STATES_HALT[] = {
+	ZONE_STATE_INSTALLED,
+	ZONE_STATE_READY,
+	ZONE_STATE_RUNNING,
+	ZONE_STATE_SHUTTING_DOWN,
+	-1
+};
+
+static const int VALID_STATES_HALT_GRACEFUL[] = {
+	ZONE_STATE_RUNNING,
+	-1
+};
+
+static const int VALID_STATES_OPEN_RAD[] = {
+	ZONE_STATE_RUNNING,
+	-1
+};
+
+static const int VALID_STATES_REBOOT[] = {
+	ZONE_STATE_RUNNING,
+	-1
+};
+
 /*
  * Extern functions
  */
@@ -104,6 +138,29 @@
 
 /* ARGSUSED */
 conerr_t
+api_ZoneAdmin_invoke_boot(rad_instance_t *inst, adr_method_t *meth,
+    data_t **ret, data_t **args, int count, data_t **error)
+{
+	const char *zone = data_to_string(args[0]);
+
+	conerr_t err = validate_zone(zone, VALID_STATES_BOOT, error);
+	if (err != ce_ok) {
+		return (err);
+	}
+
+	const char *argv[] = {
+		"/usr/bin/pfexec",
+		"/usr/sbin/zoneadm",
+		"-z", zone,
+		"boot",
+		NULL
+	};
+
+	return (forkexec(argv, error));
+}
+
+/* ARGSUSED */
+conerr_t
 api_ZoneAdmin_invoke_close(rad_instance_t *inst, adr_method_t *meth,
     data_t **ret, data_t **args, int count, data_t **error)
 {
@@ -186,10 +243,10 @@
 	const char *zone = data_to_string(args[0]);
 	boolean_t graceful = data_to_boolean(args[1]);
 
-	zone_state_t zstate;
-	if (getzoneidbyname(zone) == GLOBAL_ZONEID ||
-	    zone_get_state((char *)zone, &zstate) != Z_OK) {
-		return (make_error(&e__ZoneAdminErrorCode_INVALID_ZONE, error));
+	conerr_t err = validate_zone(zone, graceful ?
+	    VALID_STATES_HALT_GRACEFUL : VALID_STATES_HALT, error);
+	if (err != ce_ok) {
+		return (err);
 	}
 
 	const char **argv;
@@ -216,17 +273,7 @@
 		argv = cmd;
 	}
 
-	int status;
-	if (rad_forkexec(argv, &status, B_TRUE) == -1) {
-		return (ce_system);
-	}
-
-	if (status != 0) {
-		return (make_error(&e__ZoneAdminErrorCode_COMMAND_FAILED,
-		    error));
-	}
-
-	return (ce_ok);
+	return (forkexec(argv, error));
 }
 
 /* ARGSUSED */
@@ -235,8 +282,10 @@
     data_t **ret, data_t **args, int count, data_t **error)
 {
 	const char *zone = data_to_string(args[0]);
-	if (!has_auth(zone)) {
-		return (ce_priv);
+
+	conerr_t err = validate_zone(zone, NULL, error);
+	if (err != ce_ok) {
+		return (err);
 	}
 
 	int fdm;
@@ -272,17 +321,17 @@
 		(void) closefrom(3);
 
 		const char *argv[] = {
-		    "/usr/bin/pfexec",
-		    "/usr/sbin/zlogin",
-		    "-CE", zone,
-		    NULL
+			"/usr/bin/pfexec",
+			"/usr/sbin/zlogin",
+			"-CE", zone,
+			NULL
 		};
 
 		(void) execv(argv[0], (char *const *)argv);
 		_exit(1);
 	}
 
-	conerr_t err = fd_to_token(fdm, ret, error);
+	err = fd_to_token(fdm, ret, error);
 	if (err != ce_ok) {
 		(void) close(fdm);
 		return (err);
@@ -299,8 +348,9 @@
 	const char *zone = data_to_string(args[0]);
 	const char *user = args[1] == NULL ? NULL : data_to_string(args[1]);
 
-	if (!has_auth(zone)) {
-		return (ce_priv);
+	conerr_t err = validate_zone(zone, VALID_STATES_OPEN_RAD, error);
+	if (err != ce_ok) {
+		return (err);
 	}
 
 	int fd[2];
@@ -336,23 +386,23 @@
 		 */
 		if (user == NULL) {
 			const char *cmd[] = {
-			    "/usr/bin/pfexec",
-			    "/usr/sbin/zlogin",
-			    "-ES", zone,
-			    "/usr/lib/rad/radpipe",
-			    "-u",
-			    NULL
+				"/usr/bin/pfexec",
+				"/usr/sbin/zlogin",
+				"-ES", zone,
+				"/usr/lib/rad/radpipe",
+				"-u",
+				NULL
 			};
 			argv = cmd;
 		} else {
 			const char *cmd[] = {
-			    "/usr/bin/pfexec",
-			    "/usr/sbin/zlogin",
-			    "-ES", zone,
-			    "/usr/bin/su",
-			    user,
-			    "-c", "/usr/lib/rad/radpipe",
-			    NULL
+				"/usr/bin/pfexec",
+				"/usr/sbin/zlogin",
+				"-ES", zone,
+				"/usr/bin/su",
+				user,
+				"-c", "/usr/lib/rad/radpipe",
+				NULL
 			};
 			argv = cmd;
 		}
@@ -363,7 +413,7 @@
 
 	(void) close(fd[1]);
 
-	conerr_t err = fd_to_token(fd[0], ret, error);
+	err = fd_to_token(fd[0], ret, error);
 	if (err != ce_ok) {
 		(void) close(fd[0]);
 		return (err);
@@ -411,6 +461,29 @@
 
 /* ARGSUSED */
 conerr_t
+api_ZoneAdmin_invoke_reboot(rad_instance_t *inst, adr_method_t *meth,
+    data_t **ret, data_t **args, int count, data_t **error)
+{
+	const char *zone = data_to_string(args[0]);
+
+	conerr_t err = validate_zone(zone, VALID_STATES_REBOOT, error);
+	if (err != ce_ok) {
+		return (err);
+	}
+
+	const char *argv[] = {
+		"/usr/bin/pfexec",
+		"/usr/sbin/zoneadm",
+		"-z", zone,
+		"reboot",
+		NULL
+	};
+
+	return (forkexec(argv, error));
+}
+
+/* ARGSUSED */
+conerr_t
 api_ZoneAdmin_invoke_write(rad_instance_t *inst, adr_method_t *meth,
     data_t **ret, data_t **args, int count, data_t **error)
 {
@@ -447,13 +520,20 @@
 	return (*token == NULL ? ce_nomem : ce_ok);
 }
 
-static int
-has_auth(const char *zonename)
+static conerr_t
+forkexec(const char **argv, data_t **error)
 {
-	char authname[MAXAUTHS];
-	(void) snprintf(authname, MAXAUTHS, "%s%s%s",
-	    ZONE_MANAGE_AUTH, KV_OBJECT, zonename);
-	return (chkauthattr(authname, cuserid(NULL)));
+	int status;
+	if (rad_forkexec(argv, &status, B_TRUE) == -1) {
+		return (ce_system);
+	}
+
+	if (status != 0) {
+		return (make_error(&e__ZoneAdminErrorCode_COMMAND_FAILED,
+		    error));
+	}
+
+	return (ce_ok);
 }
 
 static conerr_t
@@ -486,3 +566,46 @@
 
 	return (ce_ok);
 }
+
+static conerr_t
+validate_zone(const char *zone, const int valid_states[], data_t **error)
+{
+	/* Ensure ngz */
+	zone_state_t zstate;
+	if (getzoneidbyname(zone) == GLOBAL_ZONEID ||
+	    zone_get_state((char *)zone, &zstate) != Z_OK) {
+		return (make_error(&e__ZoneAdminErrorCode_INVALID_ZONE, error));
+	}
+
+	/* Ensure auth */
+	char authname[MAXAUTHS];
+	(void) snprintf(authname, MAXAUTHS, "%s%s%s", ZONE_MANAGE_AUTH,
+	    KV_OBJECT, zone);
+	if (!chkauthattr(authname, cuserid(NULL))) {
+		return (ce_priv);
+	}
+
+	/*
+	 * Ensure valid state
+	 *
+	 * There is no guarantee the state won't change between when testing it
+	 * here and when the requested operation is executed, but this will
+	 * validate the common case.
+	 */
+	if (valid_states != NULL) {
+		zone_state_t zstate;
+		if (zone_get_state((char *)zone, &zstate) != Z_OK) {
+			return (ce_system);
+		}
+
+		for (int i = 0; valid_states[i] != zstate; i++) {
+			if (valid_states[i] < 0) {
+				return (make_error(
+				    &e__ZoneAdminErrorCode_INVALID_ZONE_STATE,
+				    error));
+			}
+		}
+	}
+
+	return (ce_ok);
+}