changeset 13365:b868f9d61081

946 want ability to list services in a zone from the GZ Reviewed by: Eric Schrock <eric.schrock@delphix.com> Reviewed by: Albert Lee <trisk@opensolaris.org> Reviewed by: Garrett D'Amore <garrett@nexenta.com> Approved by: Garrett D'Amore <garrett@nexenta.com>
author Bryan Cantrill <bryan@joyent.com>
date Fri, 28 Jan 2011 00:48:09 -0800
parents b600b8d927a8
children c1720cb807d5
files usr/src/cmd/svc/svcs/Makefile usr/src/cmd/svc/svcs/explain.c usr/src/cmd/svc/svcs/svcs.c usr/src/lib/libscf/common/lowlevel.c usr/src/man/man1/svcs.1
diffstat 5 files changed, 424 insertions(+), 79 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/svc/svcs/Makefile	Sun May 08 09:02:29 2011 -0700
+++ b/usr/src/cmd/svc/svcs/Makefile	Fri Jan 28 00:48:09 2011 -0800
@@ -21,6 +21,8 @@
 #
 # Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
 #
+# Copyright (c) 2011, Joyent, Inc. All rights reserved.
+#
 
 PROG =		svcs
 OBJS =		svcs.o explain.o
@@ -32,7 +34,7 @@
 include ../../Makefile.ctf
 
 POFILE =	$(PROG)_all.po
-LDLIBS +=	-lcontract -lscf -luutil -lumem -lnvpair
+LDLIBS +=	-lcontract -lscf -luutil -lumem -lnvpair -lzonecfg
 CPPFLAGS += -I ../common
 
 lint := LINTFLAGS = -mux
--- a/usr/src/cmd/svc/svcs/explain.c	Sun May 08 09:02:29 2011 -0700
+++ b/usr/src/cmd/svc/svcs/explain.c	Fri Jan 28 00:48:09 2011 -0800
@@ -22,6 +22,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2011, Joyent, Inc. All rights reserved.
  */
 
 /*
@@ -198,6 +199,7 @@
 static char *emsg_invalid_dep;
 
 extern scf_handle_t *h;
+extern char *g_zonename;
 
 /* ARGSUSED */
 static int
@@ -223,7 +225,6 @@
 	return (h);
 }
 
-
 static void
 x_init(void)
 {
@@ -1996,6 +1997,9 @@
 		(void) putchar('\n');
 	}
 
+	if (g_zonename != NULL)
+		(void) printf(gettext("  Zone: %s\n"), g_zonename);
+
 	stime = svcp->stime.tv_sec;
 	tmp = localtime(&stime);
 
@@ -2097,7 +2101,12 @@
 void
 explain(int verbose, int argc, char **argv)
 {
-	/* Initialize globals. */
+	/*
+	 * Initialize globals.  If we have been called before (e.g., for a
+	 * different zone), this will clobber the previous globals -- keeping
+	 * with the proud svcs(1) tradition of not bothering to ever clean
+	 * anything up.
+	 */
 	x_init();
 
 	/* Walk the graph and populate services with inst_t's */
--- a/usr/src/cmd/svc/svcs/svcs.c	Sun May 08 09:02:29 2011 -0700
+++ b/usr/src/cmd/svc/svcs/svcs.c	Fri Jan 28 00:48:09 2011 -0800
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, Joyent, Inc. All rights reserved.
  */
 
 /*
@@ -76,7 +77,8 @@
 #include <stdlib.h>
 #include <strings.h>
 #include <time.h>
-
+#include <libzonecfg.h>
+#include <zone.h>
 
 #ifndef TEXT_DOMAIN
 #define	TEXT_DOMAIN	"SUNW_OST_OSCMD"
@@ -131,6 +133,7 @@
 static int first_paragraph = 1;		/* For -l mode. */
 static char *common_name_buf;		/* Sized for maximal length value. */
 char *locale;				/* Current locale. */
+char *g_zonename;			/* zone being operated upon */
 
 /*
  * Pathname storage for path generated from the fmri.
@@ -146,6 +149,7 @@
 static int opt_snum = 0;
 static int opt_nstate_shown = 0;	/* Will nstate be shown? */
 static int opt_verbose = 0;
+static char *opt_zone;			/* zone selected, if any */
 
 /* Minimize string constants. */
 static const char * const scf_property_state = SCF_PROPERTY_STATE;
@@ -210,13 +214,34 @@
 	struct ht_elem	*next;
 };
 
-static struct ht_elem	**ht_buckets;
-static uint_t		ht_buckets_num;
+static struct ht_elem	**ht_buckets = NULL;
+static uint_t		ht_buckets_num = 0;
 static uint_t		ht_num;
 
 static void
-ht_init()
+ht_free(void)
 {
+	struct ht_elem *elem, *next;
+	int i;
+
+	for (i = 0; i < ht_buckets_num; i++) {
+		for (elem = ht_buckets[i]; elem != NULL; elem = next) {
+			next = elem->next;
+			free((char *)elem->fmri);
+			free(elem);
+		}
+	}
+
+	free(ht_buckets);
+	ht_buckets_num = 0;
+	ht_buckets = NULL;
+}
+
+static void
+ht_init(void)
+{
+	assert(ht_buckets == NULL);
+
 	ht_buckets_num = 8;
 	ht_buckets = safe_malloc(sizeof (*ht_buckets) * ht_buckets_num);
 	bzero(ht_buckets, sizeof (*ht_buckets) * ht_buckets_num);
@@ -334,7 +359,7 @@
 pg_get_single_val(scf_propertygroup_t *pg, const char *propname, scf_type_t ty,
     void *vp, size_t sz, uint_t flags)
 {
-	char *buf;
+	char *buf, root[MAXPATHLEN];
 	size_t buf_sz;
 	int ret = -1, r;
 	boolean_t multi = B_FALSE;
@@ -427,6 +452,21 @@
 	free(buf);
 
 out:
+	if (ret != 0 || g_zonename == NULL ||
+	    (strcmp(propname, SCF_PROPERTY_LOGFILE) != 0 &&
+	    strcmp(propname, SCF_PROPERTY_ALT_LOGFILE) != 0))
+		return (ret);
+
+	/*
+	 * If we're here, we have a log file and we have specified a zone.
+	 * As a convenience, we're going to prepend the zone path to the
+	 * name of the log file.
+	 */
+	root[0] = '\0';
+	(void) zone_get_rootpath(g_zonename, root, sizeof (root));
+	(void) strlcat(root, vp, sizeof (root));
+	(void) snprintf(vp, sz, "%s", root);
+
 	return (ret);
 }
 
@@ -1683,6 +1723,49 @@
 		reverse_bytes(buf, STIME_SORTKEY_WIDTH);
 }
 
+/* ZONE */
+#define	ZONE_COLUMN_WIDTH	16
+/*ARGSUSED*/
+static void
+sprint_zone(char **buf, scf_walkinfo_t *wip)
+{
+	size_t newsize;
+	char *newbuf, *zonename = g_zonename, b[ZONENAME_MAX];
+
+	if (zonename == NULL) {
+		zoneid_t zoneid = getzoneid();
+
+		if (getzonenamebyid(zoneid, b, sizeof (b)) < 0)
+			uu_die(gettext("could not determine zone name"));
+
+		zonename = b;
+	}
+
+	if (strlen(zonename) > ZONE_COLUMN_WIDTH)
+		newsize = (*buf ? strlen(*buf) : 0) + strlen(zonename) + 2;
+	else
+		newsize = (*buf ? strlen(*buf) : 0) + ZONE_COLUMN_WIDTH + 2;
+
+	newbuf = safe_malloc(newsize);
+	(void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
+	    ZONE_COLUMN_WIDTH, zonename);
+
+	if (*buf)
+		free(*buf);
+	*buf = newbuf;
+}
+
+static void
+sortkey_zone(char *buf, int reverse, scf_walkinfo_t *wip)
+{
+	char *tmp = NULL;
+
+	sprint_zone(&tmp, wip);
+	bcopy(tmp, buf, ZONE_COLUMN_WIDTH);
+	free(tmp);
+	if (reverse)
+		reverse_bytes(buf, ZONE_COLUMN_WIDTH);
+}
 
 /*
  * Information about columns which can be displayed.  If you add something,
@@ -1712,6 +1795,8 @@
 		1, sortkey_state },
 	{ "STIME", STIME_COLUMN_WIDTH, sprint_stime,
 		STIME_SORTKEY_WIDTH, sortkey_stime },
+	{ "ZONE", ZONE_COLUMN_WIDTH, sprint_zone,
+		ZONE_COLUMN_WIDTH, sortkey_zone },
 };
 
 #define	MAX_COLUMN_NAME_LENGTH_STR	"6"
@@ -1769,6 +1854,9 @@
 	case 13:
 		s = gettext("time of last state change");
 		break;
+	case 14:
+		s = gettext("name of zone");
+		break;
 	}
 
 	assert(s != NULL);
@@ -1781,11 +1869,11 @@
 {
 	(void) fprintf(f, gettext(
 	    "Usage: %1$s [-aHpv] [-o col[,col ... ]] [-R restarter] "
-	    "[-sS col] [<service> ...]\n"
+	    "[-sS col] [-Z | -z zone ]\n            [<service> ...]\n"
 	    "       %1$s -d | -D [-Hpv] [-o col[,col ... ]] [-sS col] "
-	    "[<service> ...]\n"
-	    "       %1$s -l <service> ...\n"
-	    "       %1$s -x [-v] [<service> ...]\n"
+	    "[-Z | -z zone ]\n            [<service> ...]\n"
+	    "       %1$s -l [-Z | -z zone] <service> ...\n"
+	    "       %1$s -x [-v] [-Z | -z zone] [<service> ...]\n"
 	    "       %1$s -?\n"), progname);
 
 	if (do_exit)
@@ -1816,6 +1904,8 @@
 	"\t-v  list verbose information appropriate to the type of output\n"
 	"\t-x  explain the status of services that might require maintenance,\n"
 	"\t    or explain the status of the specified service(s)\n"
+	"\t-z  from global zone, show services in a specified zone\n"
+	"\t-Z  from global zone, show services in all zones\n"
 	"\n\t"
 	"Services can be specified using an FMRI, abbreviation, or fnmatch(5)\n"
 	"\tpattern, as shown in these examples for svc:/network/smtp:sendmail\n"
@@ -2372,6 +2462,9 @@
 		(void) printf(fmt, DETAILED_WIDTH, gettext("name"),
 		    common_name_buf);
 
+	if (g_zonename != NULL)
+		(void) printf(fmt, DETAILED_WIDTH, gettext("zone"), g_zonename);
+
 	/*
 	 * Synthesize an 'enabled' property that hides the enabled_ovr
 	 * implementation from the user.  If the service has been temporarily
@@ -3310,6 +3403,11 @@
 	return (UU_WALK_NEXT);
 }
 
+/* ARGSUSED */
+static void
+errignore(const char *str, ...)
+{}
+
 int
 main(int argc, char **argv)
 {
@@ -3318,12 +3416,17 @@
 	char *columns_str = NULL;
 	char *cp;
 	const char *progname;
-	int err;
+	int err, missing = 1, ignored, *errarg;
+	uint_t nzents = 0, zent = 0;
+	zoneid_t *zids = NULL;
+	char zonename[ZONENAME_MAX];
+	void (*errfunc)(const char *, ...);
 
 	int show_all = 0;
 	int show_header = 1;
-
-	const char * const options = "aHpvno:R:s:S:dDl?x";
+	int show_zones = 0;
+
+	const char * const options = "aHpvno:R:s:S:dDl?xZz:";
 
 	(void) setlocale(LC_ALL, "");
 
@@ -3451,6 +3554,26 @@
 			assert(opt_mode == optopt);
 			break;
 
+		case 'z':
+			if (getzoneid() != GLOBAL_ZONEID)
+				uu_die(gettext("svcs -z may only be used from "
+				    "the global zone\n"));
+			if (show_zones)
+				argserr(progname);
+
+			opt_zone = optarg;
+			break;
+
+		case 'Z':
+			if (getzoneid() != GLOBAL_ZONEID)
+				uu_die(gettext("svcs -Z may only be used from "
+				    "the global zone\n"));
+			if (opt_zone != NULL)
+				argserr(progname);
+
+			show_zones = 1;
+			break;
+
 		case '?':
 			argserr(progname);
 			/* NOTREACHED */
@@ -3467,21 +3590,118 @@
 	if (show_all && optind != argc)
 		uu_warn(gettext("-a ignored when used with arguments.\n"));
 
+	while (show_zones) {
+		uint_t found;
+
+		if (zone_list(NULL, &nzents) != 0)
+			uu_die(gettext("could not get number of zones"));
+
+		if ((zids = malloc(nzents * sizeof (zoneid_t))) == NULL) {
+			uu_die(gettext("could not allocate array for "
+			    "%d zone IDs"), nzents);
+		}
+
+		found = nzents;
+
+		if (zone_list(zids, &found) != 0)
+			uu_die(gettext("could not get zone list"));
+
+		/*
+		 * If the number of zones has not changed between our calls to
+		 * zone_list(), we're done -- otherwise, we must free our array
+		 * of zone IDs and take another lap.
+		 */
+		if (found == nzents)
+			break;
+
+		free(zids);
+	}
+
+	argc -= optind;
+	argv += optind;
+
+again:
 	h = scf_handle_create(SCF_VERSION);
 	if (h == NULL)
 		scfdie();
 
-	if (scf_handle_bind(h) == -1)
+	if (opt_zone != NULL || zids != NULL) {
+		scf_value_t *zone;
+
+		assert(opt_zone == NULL || zids == NULL);
+
+		if (opt_zone == NULL) {
+			if (getzonenamebyid(zids[zent++],
+			    zonename, sizeof (zonename)) < 0) {
+				uu_warn(gettext("could not get name for "
+				    "zone %d; ignoring"), zids[zent - 1]);
+				goto nextzone;
+			}
+
+			g_zonename = zonename;
+		} else {
+			g_zonename = opt_zone;
+		}
+
+		if ((zone = scf_value_create(h)) == NULL)
+			scfdie();
+
+		if (scf_value_set_astring(zone, g_zonename) != SCF_SUCCESS)
+			scfdie();
+
+		if (scf_handle_decorate(h, "zone", zone) != SCF_SUCCESS)
+			uu_die(gettext("invalid zone '%s'\n"), g_zonename);
+
+		scf_value_destroy(zone);
+	}
+
+	if (scf_handle_bind(h) == -1) {
+		if (g_zonename != NULL) {
+			uu_warn(gettext("Could not bind to repository "
+			    "server for zone %s: %s\n"), g_zonename,
+			    scf_strerror(scf_error()));
+
+			if (!show_zones)
+				return (UU_EXIT_FATAL);
+
+			goto nextzone;
+		}
+
 		uu_die(gettext("Could not bind to repository server: %s.  "
 		    "Exiting.\n"), scf_strerror(scf_error()));
+	}
 
 	if ((g_pg = scf_pg_create(h)) == NULL ||
 	    (g_prop = scf_property_create(h)) == NULL ||
 	    (g_val = scf_value_create(h)) == NULL)
 		scfdie();
 
-	argc -= optind;
-	argv += optind;
+	if (show_zones) {
+		/*
+		 * It's hard to avoid editorializing here, but suffice it to
+		 * say that scf_walk_fmri() takes an error handler, the
+		 * interface to which has been regrettably misdesigned:  the
+		 * handler itself takes exclusively a string -- even though
+		 * scf_walk_fmri() has detailed, programmatic knowledge
+		 * of the error condition at the time it calls its errfunc.
+		 * That is, only the error message and not the error semantics
+		 * are given to the handler.  This is poor interface at best,
+		 * but it is particularly problematic when we are talking to
+		 * multiple repository servers (as when we are iterating over
+		 * all zones) as we do not want to treat failure to find a
+		 * match in one zone as overall failure.  Ideally, we would
+		 * simply ignore SCF_MSG_PATTERN_NOINSTANCE and correctly
+		 * process the others, but alas, no such interface exists --
+		 * and we must settle for instead ignoring all errfunc-called
+		 * errors in the case that we are iterating over all zones...
+		 */
+		errfunc = errignore;
+		errarg = missing ? &missing : &ignored;
+		missing = 0;
+	} else {
+		errfunc = uu_warn;
+		errarg = &exit_status;
+	}
 
 	/*
 	 * If we're in long mode, take care of it now before we deal with the
@@ -3492,87 +3712,100 @@
 			argserr(progname);
 
 		if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE,
-		    print_detailed, NULL, &exit_status, uu_warn)) != 0) {
+		    print_detailed, NULL, errarg, errfunc)) != 0) {
 			uu_warn(gettext("failed to iterate over "
 			    "instances: %s\n"), scf_strerror(err));
 			exit_status = UU_EXIT_FATAL;
 		}
 
-		return (exit_status);
+		goto nextzone;
 	}
 
 	if (opt_mode == 'n') {
 		print_notify_special();
 		if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE,
-		    print_notify, NULL, &exit_status, uu_warn)) != 0) {
+		    print_notify, NULL, errarg, errfunc)) != 0) {
 			uu_warn(gettext("failed to iterate over "
 			    "instances: %s\n"), scf_strerror(err));
 			exit_status = UU_EXIT_FATAL;
 		}
 
-		return (exit_status);
+		goto nextzone;
 	}
 
 	if (opt_mode == 'x') {
 		explain(opt_verbose, argc, argv);
-
-		return (exit_status);
-	}
-
-
-	if (opt_snum == 0) {
-		/* Default sort. */
-		add_sort_column("state", 0);
-		add_sort_column("stime", 0);
-		add_sort_column("fmri", 0);
+		goto nextzone;
 	}
 
 	if (columns_str == NULL) {
-		if (!opt_verbose)
-			columns_str = safe_strdup("state,stime,fmri");
-		else
-			columns_str =
-			    safe_strdup("state,nstate,stime,ctid,fmri");
+		if (opt_snum == 0) {
+			if (show_zones)
+				add_sort_column("zone", 0);
+
+			/* Default sort. */
+			add_sort_column("state", 0);
+			add_sort_column("stime", 0);
+			add_sort_column("fmri", 0);
+		}
+
+		if (!opt_verbose) {
+			columns_str = safe_strdup(show_zones ?
+			    "zone,state,stime,fmri" : "state,stime,fmri");
+		} else {
+			columns_str = safe_strdup(show_zones ?
+			    "zone,state,nstate,stime,ctid,fmri" :
+			    "state,nstate,stime,ctid,fmri");
+		}
 	}
 
-	/* Decode columns_str into opt_columns. */
-	line_sz = 0;
-
-	opt_cnum = 1;
-	for (cp = columns_str; *cp != '\0'; ++cp)
-		if (*cp == ',')
-			++opt_cnum;
-
-	opt_columns = malloc(opt_cnum * sizeof (*opt_columns));
-	if (opt_columns == NULL)
-		uu_die(gettext("Too many columns.\n"));
-
-	for (n = 0; *columns_str != '\0'; ++n) {
-		i = getcolumnopt(&columns_str);
-		if (i == -1)
-			uu_die(gettext("Unknown column \"%s\".\n"),
-			    columns_str);
-
-		if (strcmp(columns[i].name, "N") == 0 ||
-		    strcmp(columns[i].name, "SN") == 0 ||
-		    strcmp(columns[i].name, "NSTA") == 0 ||
-		    strcmp(columns[i].name, "NSTATE") == 0)
-			opt_nstate_shown = 1;
-
-		opt_columns[n] = i;
-		line_sz += columns[i].width + 1;
+	if (opt_columns == NULL) {
+		/* Decode columns_str into opt_columns. */
+		line_sz = 0;
+
+		opt_cnum = 1;
+		for (cp = columns_str; *cp != '\0'; ++cp)
+			if (*cp == ',')
+				++opt_cnum;
+
+		opt_columns = malloc(opt_cnum * sizeof (*opt_columns));
+		if (opt_columns == NULL)
+			uu_die(gettext("Too many columns.\n"));
+
+		for (n = 0; *columns_str != '\0'; ++n) {
+			i = getcolumnopt(&columns_str);
+			if (i == -1)
+				uu_die(gettext("Unknown column \"%s\".\n"),
+				    columns_str);
+
+			if (strcmp(columns[i].name, "N") == 0 ||
+			    strcmp(columns[i].name, "SN") == 0 ||
+			    strcmp(columns[i].name, "NSTA") == 0 ||
+			    strcmp(columns[i].name, "NSTATE") == 0)
+				opt_nstate_shown = 1;
+
+			opt_columns[n] = i;
+			line_sz += columns[i].width + 1;
+		}
+
+		if ((lines_pool = uu_avl_pool_create("lines_pool",
+		    sizeof (struct avl_string), offsetof(struct avl_string,
+		    node), line_cmp, UU_AVL_DEBUG)) == NULL ||
+		    (lines = uu_avl_create(lines_pool, NULL, 0)) == NULL)
+			uu_die(gettext("Unexpected libuutil error: %s\n"),
+			    uu_strerror(uu_error()));
 	}
 
-
-	if ((lines_pool = uu_avl_pool_create("lines_pool",
-	    sizeof (struct avl_string), offsetof(struct avl_string, node),
-	    line_cmp, UU_AVL_DEBUG)) == NULL ||
-	    (lines = uu_avl_create(lines_pool, NULL, 0)) == NULL)
-		uu_die(gettext("Unexpected libuutil error: %s.  Exiting.\n"),
-		    uu_strerror(uu_error()));
-
 	switch (opt_mode) {
 	case 0:
+		/*
+		 * If we already have a hash table (e.g., because we are
+		 * processing multiple zones), destroy it before creating
+		 * a new one.
+		 */
+		if (ht_buckets != NULL)
+			ht_free();
+
 		ht_init();
 
 		/* Always show all FMRIs when given arguments or restarters */
@@ -3582,7 +3815,7 @@
 		if ((err = scf_walk_fmri(h, argc, argv,
 		    SCF_WALK_MULTIPLE | SCF_WALK_LEGACY,
 		    show_all ? list_instance : list_if_enabled, NULL,
-		    &exit_status, uu_warn)) != 0) {
+		    errarg, errfunc)) != 0) {
 			uu_warn(gettext("failed to iterate over "
 			    "instances: %s\n"), scf_strerror(err));
 			exit_status = UU_EXIT_FATAL;
@@ -3595,7 +3828,7 @@
 
 		if ((err = scf_walk_fmri(h, argc, argv,
 		    SCF_WALK_MULTIPLE, list_dependencies, NULL,
-		    &exit_status, uu_warn)) != 0) {
+		    errarg, errfunc)) != 0) {
 			uu_warn(gettext("failed to iterate over "
 			    "instances: %s\n"), scf_strerror(err));
 			exit_status = UU_EXIT_FATAL;
@@ -3631,6 +3864,18 @@
 		abort();
 	}
 
+nextzone:
+	if (show_zones && zent < nzents && exit_status == 0) {
+		scf_handle_destroy(h);
+		goto again;
+	}
+
+	if (show_zones && exit_status == 0)
+		exit_status = missing;
+
+	if (opt_columns == NULL)
+		return (exit_status);
+
 	if (show_header)
 		print_header();
 
--- a/usr/src/lib/libscf/common/lowlevel.c	Sun May 08 09:02:29 2011 -0700
+++ b/usr/src/lib/libscf/common/lowlevel.c	Fri Jan 28 00:48:09 2011 -0800
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, Joyent, Inc. All rights reserved.
  */
 
 /*
@@ -49,7 +50,9 @@
 #include <string.h>
 #include <sys/mman.h>
 #include <sys/sysmacros.h>
+#include <libzonecfg.h>
 #include <unistd.h>
+#include <dlfcn.h>
 
 #define	ENV_SCF_DEBUG		"LIBSCF_DEBUG"
 #define	ENV_SCF_DOORPATH	"LIBSCF_DOORPATH"
@@ -879,6 +882,67 @@
 		}
 		return (0);
 	}
+
+	if (strcmp(name, "zone") == 0) {
+		char zone[MAXPATHLEN], root[MAXPATHLEN], door[MAXPATHLEN];
+		static int (*zone_get_rootpath)(char *, char *, size_t);
+		ssize_t len;
+
+		/*
+		 * In order to be able to set the zone on a handle, we want
+		 * to determine the zone's path, which requires us to call into
+		 * libzonecfg -- but libzonecfg.so links against libscf.so so
+		 * we must not explicitly link to it.  To circumvent the
+		 * circular dependency, we will pull it in here via dlopen().
+		 */
+		if (zone_get_rootpath == NULL) {
+			void *dl = dlopen("libzonecfg.so.1", RTLD_LAZY), *sym;
+
+			if (dl == NULL)
+				return (scf_set_error(SCF_ERROR_NOT_FOUND));
+
+			if ((sym = dlsym(dl, "zone_get_rootpath")) == NULL) {
+				(void) dlclose(dl);
+				return (scf_set_error(SCF_ERROR_INTERNAL));
+			}
+
+			zone_get_rootpath = (int(*)(char *, char *, size_t))sym;
+		}
+
+		if (v == SCF_DECORATE_CLEAR) {
+			(void) pthread_mutex_lock(&handle->rh_lock);
+			handle->rh_doorpath[0] = 0;
+			(void) pthread_mutex_unlock(&handle->rh_lock);
+
+			return (0);
+		}
+
+		if ((len = scf_value_get_astring(v, zone, sizeof (zone))) < 0)
+			return (-1);
+
+		if (len == 0 || len >= sizeof (zone))
+			return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
+
+		if (zone_get_rootpath(zone, root, sizeof (root)) != Z_OK) {
+			if (strcmp(zone, GLOBAL_ZONENAME) == 0) {
+				root[0] = '\0';
+			} else {
+				return (scf_set_error(SCF_ERROR_NOT_FOUND));
+			}
+		}
+
+		if (snprintf(door, sizeof (door), "%s/%s", root,
+		    default_door_path) >= sizeof (door))
+			return (scf_set_error(SCF_ERROR_INTERNAL));
+
+		(void) pthread_mutex_lock(&handle->rh_lock);
+		(void) strlcpy(handle->rh_doorpath, door,
+		    sizeof (handle->rh_doorpath));
+		(void) pthread_mutex_unlock(&handle->rh_lock);
+
+		return (0);
+	}
+
 	return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
 }
 
--- a/usr/src/man/man1/svcs.1	Sun May 08 09:02:29 2011 -0700
+++ b/usr/src/man/man1/svcs.1	Fri Jan 28 00:48:09 2011 -0800
@@ -3,30 +3,30 @@
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH svcs 1 "6 Nov 2008" "SunOS 5.11" "User Commands"
+.TH svcs 1 "17 Apr 2011" "SunOS 5.11" "User Commands"
 .SH NAME
 svcs \- report service status
 .SH SYNOPSIS
 .LP
 .nf
-\fBsvcs\fR [\fB-aHpv?\fR] [\fB-o\fR \fIcol\fR[,\fIcol\fR]]... [\fB-R\fR \fIFMRI-instance\fR]... 
+\fBsvcs\fR [\fB-aHpv?\fR] [\fB-Z\fR | \fB-z\fR \fIzone\fR] [\fB-o\fR \fIcol\fR[,\fIcol\fR]]... [\fB-R\fR \fIFMRI-instance\fR]... 
      [\fB-sS\fR \fIcol\fR]... [\fIFMRI\fR | \fIpattern\fR]...
 .fi
 
 .LP
 .nf
-\fBsvcs\fR {\fB-d\fR | \fB-D\fR} [\fB-Hpv?\fR] [\fB-o\fR \fIcol\fR[,\fIcol\fR]]... [\fB-sS\fR \fIcol\fR]... 
+\fBsvcs\fR {\fB-d\fR | \fB-D\fR} [\fB-Hpv?\fR] [\fB-Z\fR | \fB-z\fR \fIzone\fR] [\fB-o\fR \fIcol\fR[,\fIcol\fR]]... [\fB-sS\fR \fIcol\fR]... 
      [\fIFMRI\fR | \fIpattern\fR] ...
 .fi
 
 .LP
 .nf
-\fBsvcs\fR \fB-l\fR [\fB-v\fR] [\fIFMRI\fR | \fIpattern\fR]...
+\fBsvcs\fR \fB-l\fR [\fB-vZ\fR] [\fB-z\fR \fIzone\fR] [\fIFMRI\fR | \fIpattern\fR]...
 .fi
 
 .LP
 .nf
-\fBsvcs\fR \fB-x\fR [\fB-v\fR] [\fIFMRI\fR]...
+\fBsvcs\fR \fB-x\fR [\fB-v\fR] [\fB-Z\fR | \fB-z\fR \fIzone\fR] [\fIFMRI\fR]...
 .fi
 
 .SH DESCRIPTION
@@ -347,6 +347,31 @@
 .RE
 .RE
 
+.sp
+.ne 2
+.mk
+.na
+\fB-z \fIzone\fR
+.ad
+.RS 20n
+.rt
+Display only the services in the \fIzone\fR.  This option is only applicable
+in the global zone, see \fBzones\fR(5).
+.RE
+
+.sp
+.ne 2
+.mk
+.na
+\fB-Z\fR
+.ad
+.RS 20n
+.rt
+Display services from all zones, with an additional column indicating in which
+zone the service is running.  This option is only applicable in the global
+zone, see \fBzones\fR(5).
+.RE
+
 .SH OPERANDS
 .sp
 .LP
@@ -881,4 +906,4 @@
 .LP
 \fBps\fR(1), \fBsvcprop\fR(1), \fBsvcadm\fR(1M), \fBsvccfg\fR(1M),
 \fBsvc.startd\fR(1M), \fBstat\fR(2), \fBlibscf\fR(3LIB), \fBattributes\fR(5),
-\fBfnmatch\fR(5), \fBsmf\fR(5)
+\fBfnmatch\fR(5), \fBsmf\fR(5), \fBzones\fR(5)