Skip to main content

Source code file content

Revision: 1

Modify README slightly so it's not entirely Solaris 10 specific. Remove old snv_24 output examples.
» Project Revision History

» Checkout URL

scfdot-src / scfdot.c

Size: 23361 bytes, 1 line
/*
 * CDDL HEADER START
 *
 * 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 CDDL.LICENSE.
 * 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 CDDL.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]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"@(#)scfdot.c	1.23	05/10/18 SMI"

/*
 * Generate a dot file for the SMF dependency graph on this machine.
 *
 * We operate in two modes: with and without -L.  Without -L, we print nodes
 * for each instance and edges for each dependency.  Fortunately dot allows
 * forward references, so we can do this in one pass.  Options are
 *
 *   -s width,height	Size, in inches, that the graph should be limited to.
 *
 *   -l legend.ps	PostScript file which should be used as the legend.
 *
 *   -x opts		Simplify the graph.  opts should be a comma-separated
 *			list of
 *
 *     omit_net_deps		Omit most of the dependencies on
 *				network/loopback and network/physical.  (See
 *				allowable_net_dep().)
 *
 *     consolidate_inetd_svcs	Consolidate services which only depend on
 *				network/inetd into a single node.
 *
 *     consolidate_rpcbind_svcs  Consolidate services which only depend on
 *				network/inetd and rpc/bind into a single node.
 *
 * Other hard-coded graph settings (rankdir, nodesep, margin) were intended
 * for a 42" plotter.
 *
 * -L causes the program to print a dot file for use as a legend.  It
 * currently consists of eight nodes which demonstrate the color scheme and
 * the dependency types.  The nodes are enclosed in a box which is labeled
 * "legend".
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/utsname.h>
#include <assert.h>
#include <libscf.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>

/* Private libscf function */
extern int scf_parse_svc_fmri(char *fmri, const char **scope,
    const char **service, const char **instance, const char **propertygroup,
    const char **property);


/*
 * We color nodes by FMRI and enabledness.  For each class we specify
 * a foreground color, which will be the color of the text and the outline,
 * and a background color, which will fill the node.
 *
 * In this scheme we'll color services who's FMRIs begin with "system" orange,
 * "network" blue, "milestone" green, and other services light gray.  For each
 * category we'll color the disabled services a faded shade of their enabled
 * counterparts.  The foreground of the enabled services will all be black,
 * and the backgrounds will be
 */

#define	ORANGE		"#ED9B4F"
#define	BLUE		"#A3B8CB"
#define	GREEN		"#C5D5A9"
#define	GRAY		"#EDEFF2"

/*
 * To make disabled services appear faded, I took the above colors and halved
 * their saturation.
 */

#define	LTBLACK		"#808080"

#define	LTORANGE	"#EDC39C"
#define	LTBLUE		"#B7C1CB"
#define	LTGREEN		"#CDD5C0"
#define	LTGRAY		"#F0F1F2"

static const struct coloring {
	const char	*cat;
	const char	*colors[2][2];
} category_colors[] = {
	{ "system/", { { "black", ORANGE }, { LTBLACK, LTORANGE } } },
	{ "network/", { { "black", BLUE }, { LTBLACK, LTBLUE } } },
	{ "milestone/", { { "black", GREEN }, { LTBLACK, LTGREEN } } },
	{ NULL, { { "black", GRAY }, { LTBLACK, LTGRAY } } },
};

/* Graph simplification options, for use with getsubopt(). */
static const char * const x_opts[] = {
	"omit_net_deps",
	"consolidate_inetd_svcs",
	"consolidate_rpcbind_svcs",
	NULL
};

static int omit_net_deps = 0;
static int consolidate_inetd_svcs = 0;
static int consolidate_rpcbind_svcs = 0;

/* Consolidation strings */
static char *inetd_svcs, *rpcbind_svcs;
static size_t inetd_svcs_sz, rpcbind_svcs_sz;


static scf_handle_t *h;

/* Scratch libscf objects, to save time. */
static scf_service_t *g_svc;
static scf_instance_t *g_inst;
static scf_snapshot_t *g_snap;
static scf_propertygroup_t *g_pg;
static scf_property_t *g_prop;
static scf_value_t *g_val;
static scf_iter_t *g_institer, *g_pgiter, *g_valiter;

static ssize_t max_fmri_len, max_name_len, max_value_len;


static void
strappend(const char *str, char **bufp, size_t *bufszp)
{
	size_t str_sz;

	str_sz = strlen(str);

	if (strlen(*bufp) + str_sz + 1 > *bufszp) {
		*bufszp += str_sz;
		*bufp = realloc(*bufp, *bufszp);
		if (*bufp == NULL) {
			perror("realloc");
			exit(1);
		}
	}

	(void) strcat(*bufp, str);
}

static void
scfdie_lineno(int lineno)
{
	(void) fprintf(stderr, "%s:%d: Unexpected libscf error: %s.\n",
	    __FILE__, lineno, scf_strerror(scf_error()));
	exit(1);
}

#define	scfdie()	scfdie_lineno(__LINE__)

/*
 * Return 1 if inst is enabled, 0 otherwise.  Uses g_pg, g_prop, and g_val.
 */
static int
is_enabled(scf_instance_t *inst)
{
	uint8_t b;

	if (scf_instance_get_pg(inst, SCF_PG_GENERAL, g_pg) != 0) {
		if (scf_error() != SCF_ERROR_NOT_FOUND)
			scfdie();
		return (0);
	}

	if (scf_pg_get_property(g_pg, SCF_PROPERTY_ENABLED, g_prop) != 0) {
		if (scf_error() != SCF_ERROR_NOT_FOUND)
			scfdie();
		return (0);
	}

	if (scf_property_get_value(g_prop, g_val) != 0) {
		switch (scf_error()) {
		case SCF_ERROR_NOT_FOUND:
		case SCF_ERROR_CONSTRAINT_VIOLATED:
			return (0);

		default:
			scfdie();
		}
	}

	if (scf_value_get_boolean(g_val, &b) != 0) {
		if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
			scfdie();
		return (0);
	}

	return (b != 0);
}

/*
 * Fill in buf with the restarter of instance i.  Uses g_pg, g_prop, and
 * g_val.
 */
static void
get_restarter(scf_instance_t *i, char *buf, size_t bufsz)
{
	if (scf_instance_get_pg_composed(i, NULL, SCF_PG_GENERAL, g_pg) != 0)
		scfdie();

	buf[0] = '\0';
	if (scf_pg_get_property(g_pg, SCF_PROPERTY_RESTARTER, g_prop) != 0) {
		if (scf_error() != SCF_ERROR_NOT_FOUND)
			scfdie();
		return;
	}

	if (scf_property_get_value(g_prop, g_val) != 0) {
		switch (scf_error()) {
		case SCF_ERROR_NOT_FOUND:
		case SCF_ERROR_CONSTRAINT_VIOLATED:
			return;

		default:
			scfdie();
		}
	}

	if (scf_value_get_astring(g_val, buf, bufsz) < 0) {
		if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
			scfdie();
	}
}

static void
usage(const char *argv0, int help, FILE *stream)
{
	(void) fprintf(stream,
	    "Usage: %1$s [-s width,height] [-l legend.ps] [-x opts]\n"
	    "       %1$s -L\n", argv0);
	if (help) {
		const char * const *opt;

		(void) fprintf(stream,
		    "Where opts is a comma-separated list of\n");

		for (opt = &x_opts[0]; *opt != NULL; ++opt)
			(void) fprintf(stream, "\t%s\n", *opt);
	}
	exit(help ? 0 : 2);
}

/*
 * Make name suitable for dot.
 */
static void
clean_name(char *name)
{
	for (; *name != '\0'; ++name) {
		if (*name == '-')
			*name = '_';
	}
}

/*
 * Print an edge for a dependency.  port should be the name of the dependency
 * group.
 */
static void
print_dependency(const char *from, const char *port, const char *to,
    const char *opts, int weight)
{
	(void) printf("\"%s\":%s:e -> \"%s\"", from, port, to);

	if (weight != 1 || (opts != NULL && opts[0] != '\0')) {
		(void) fputs(" [", stdout);
		if (opts != NULL && opts[0] != '\0') {
			(void) fputs(opts, stdout);
			(void) putchar(',');
		}
		if (weight != 1)
			(void) printf("weight=%d,", weight);
		(void) putchar(']');
	}

	(void) puts(";");
}

/*
 * Choose a coloring for the given service.  Returns a pointer to an array of
 * two string pointers, the first being the text color and the second being
 * the fill color.
 */
static const char * const *
choose_color(const char *fmri, int enabled)
{
	const struct coloring *cp;

	if (strncmp(fmri, "svc:/", sizeof ("svc:/") - 1) == 0)
		fmri += sizeof ("svc:/") - 1;

	for (cp = &category_colors[0]; cp->cat != NULL; ++cp) {
		if (strncmp(fmri, cp->cat, strlen(cp->cat)) == 0)
			break;
	}

	return (cp->colors[enabled ? 0 : 1]);
}

/*
 * Print a node for a service.  dependencies should either be an empty string
 * or a string of "<dependency port name> dependency name" strings joined by
 * pipes ("|").
 */
static void
print_service_node(const char *fmri, const char *label,
    const char *dependencies, const char * const *colors)
{
	const char *fg = colors[0];
	const char *bg = colors[1];

	if (dependencies[0] != '\0')
		(void) printf("\"%s\" [shape=record,color=\"%s\",style=filled,"
		    "fillcolor=\"%s\",fontcolor=\"%s\","
		    "label=\"{<foo> %s | {%s}}\"];\n", fmri, fg, bg, fg, label,
		    dependencies);
	else
		(void) printf("\"%s\" [shape=record,color=\"%s\",style=filled,"
		    "fillcolor=\"%s\",fontcolor=\"%s\",label=\"%s\"];\n",
		    fmri, fg, bg, fg, label);
}

/*
 * Print some fake service nodes in a box to demonstrate the coloring and the
 * dependency types.
 */
static void
print_legend()
{
	const char *fmri;

	(void) printf("digraph legend {\n"
	    "node [fontname=\"Helvetica\",fontsize=11];\n"
	    "ranksep=\"2\";\n"
	    "rankdir=LR;\n");

	(void) printf("\nsubgraph clusterlegend {\n"
	    "label=\"legend\";\n"
	    "color=\"black\";\n");

	(void) putchar('\n');

	fmri = "svc:/system/disabled:default";
	print_service_node(fmri, fmri + 5, "<dg>dependency_group",
	    choose_color(fmri, 0));
	fmri = "svc:/system/enabled:default";
	print_service_node(fmri, fmri + 5, "", choose_color(fmri, 1));
	print_dependency("svc:/system/disabled:default", "dg",
	    "svc:/system/enabled:default", "label=\"require_all\",style=bold",
	    10);

	fmri = "svc:/network/disabled:default";
	print_service_node(fmri, fmri + 5, "<dg>dependency_group",
	    choose_color(fmri, 0));
	fmri = "svc:/network/enabled:default";
	print_service_node(fmri, fmri + 5, "", choose_color(fmri, 1));
	print_dependency("svc:/network/disabled:default", "dg",
	    "svc:/network/enabled:default", "label=\"require_any\"", 1);

	fmri = "svc:/milestone/disabled:default";
	print_service_node(fmri, fmri + 5, "<dg>dependency_group",
	    choose_color(fmri, 0));
	fmri = "svc:/milestone/enabled:default";
	print_service_node(fmri, fmri + 5, "", choose_color(fmri, 1));
	print_dependency("svc:/milestone/disabled:default", "dg",
	    "svc:/milestone/enabled:default",
	    "label=\"optional_all\",style=dashed", 1);

	fmri = "svc:/other/disabled:default";
	print_service_node(fmri, fmri + 5, "<dg>dependency_group",
	    choose_color(fmri, 0));
	fmri = "svc:/other/enabled:default";
	print_service_node(fmri, fmri + 5, "", choose_color(fmri, 1));
	print_dependency("svc:/other/disabled:default", "dg",
	    "svc:/other/enabled:default",
	    "label=\"exclude_all\",arrowtail=odot", 1);

	(void) printf("}\n}\n");
}

/*
 * Return true if we shouldn't omit fmri's dependency on network/loopback or
 * network/physical, even under -x omit_net_deps.  (Otherwise they'd have no
 * dependents, which would produce a bad graph.)
 */
static int
allowable_net_dep(const char *fmri)
{
	return (strcmp(fmri, "svc:/system/identity:node") == 0 ||
	    strcmp(fmri, "svc:/system/identity:domain") == 0 ||
	    strcmp(fmri, "svc:/network/initial:default") == 0 ||
	    strcmp(fmri, "svc:/milestone/single-user:default") == 0 ||
	    strcmp(fmri, "svc:/network/inetd:default") == 0 ||
	    strcmp(fmri, "svc:/network/http:apache2") == 0);
}

/* dependency name accumulator */
static char *allpgs;
static size_t allpgs_sz;

static void
add_dep(const char *str)
{
	/* Append sprintf("<%s> %s|", str, str) */
	strappend("<", &allpgs, &allpgs_sz);
	strappend(str, &allpgs, &allpgs_sz);
	strappend("> ", &allpgs, &allpgs_sz);
	strappend(str, &allpgs, &allpgs_sz);
	strappend("|", &allpgs, &allpgs_sz);
}

static char *fmri, *dep_fmri;			/* max_fmri_len + 1 long */
static char *instname, *pgname;			/* max_name_len + 1 long */
static char *depname, *depname_copy, *grouping;	/* max_value_len + 1 long */

/*
 * For the given instance, generate a node and the appropriate edges.
 */
static int
process_instance(scf_instance_t *i, const char *svcname)
{
	int enabled;
	int ndeps;
	int inetd_svc;
	int non_rpcbind;

	scf_snapshot_t *running;		/* NULL or == g_snap */

	assert(i);

	/*
	 * Node generation: Collect the name, restarter, dependency names, and
	 * enabled status and call print_service_node().  The dependency names
	 * will be accumulated in allpgs.
	 */

	if (scf_instance_get_name(i, instname, max_name_len + 1) == -1) {
		(void) fprintf(stderr, "instance_get_name() failed: %s",
		    scf_strerror(scf_error()));
		return (-1);
	}

	(void) snprintf(fmri, max_fmri_len + 1, "svc:/%s:%s", svcname,
	    instname);

	ndeps = 0;
	allpgs[0] = '\0';
	inetd_svc = 0;

	get_restarter(i, depname, max_value_len + 1);

	if (depname[0] != '\0') {
		++ndeps;
		add_dep("restarter");
		inetd_svc = (strstr(depname, "network/inetd:default") != NULL);
	}

	if (scf_instance_get_snapshot(i, "running", g_snap) == 0) {
		running = g_snap;
	} else {
		if (scf_error() != SCF_ERROR_NOT_FOUND)
			scfdie();
		running = NULL;
	}

	if (scf_iter_instance_pgs_typed_composed(g_pgiter, i, running,
	    SCF_GROUP_DEPENDENCY) != 0)
		scfdie();

	non_rpcbind = 0;

	while (scf_iter_next_pg(g_pgiter, g_pg) > 0) {
		if (scf_pg_get_property(g_pg, SCF_PROPERTY_ENTITIES, NULL) !=
		    0) {
			if (scf_error() == SCF_ERROR_NOT_FOUND)
				continue;
			scfdie();
		}

		++ndeps;

		if (scf_pg_get_name(g_pg, pgname, max_name_len + 1) < 0)
			scfdie();

		if (!non_rpcbind && strcmp(pgname, "rpcbind") != 0)
			non_rpcbind = 1;

		clean_name(pgname);
		add_dep(pgname);
	}

	if (consolidate_inetd_svcs && inetd_svc && ndeps == 1) {
		strappend(fmri + sizeof ("svc:/") - 1, &inetd_svcs,
		    &inetd_svcs_sz);
		strappend("\\n", &inetd_svcs, &inetd_svcs_sz);
		return (0);
	}

	if (consolidate_rpcbind_svcs && inetd_svc && non_rpcbind == 0 &&
	    ndeps == 2) {
		/*
		 * Exclude network/rpc/meta and rpc/smserver since they have
		 * dependents.
		 */
		if (strcmp(svcname, "network/rpc/meta") != 0 &&
		    strcmp(svcname, "network/rpc/smserver") != 0) {
			strappend(fmri + sizeof ("svc:/") - 1, &rpcbind_svcs,
			    &rpcbind_svcs_sz);
			strappend("\\n", &rpcbind_svcs, &rpcbind_svcs_sz);
			return (0);
		}
	}

	if (allpgs[0] != '\0')
		allpgs[strlen(allpgs) - 1] = '\0';	/* nuke trailing | */

	enabled = is_enabled(i);

	print_service_node(fmri, fmri + sizeof ("svc:/") - 1, allpgs,
	    choose_color(fmri, enabled));

	/*
	 * Edges: One for the restarter, if it is not the default (svc.startd)
	 * (denoted by an empty string), and one for each dependency service.
	 * Remember that dependency groups can name multiple services, and
	 * each service can have multiple instances.
	 */

	if (depname[0] != '\0')
		print_dependency(fmri, "restarter", depname, "", 1);

	if (scf_iter_instance_pgs_typed_composed(g_pgiter, i, running,
	    SCF_GROUP_DEPENDENCY) != 0)
		scfdie();

	for (;;) {
		int r;

		r = scf_iter_next_pg(g_pgiter, g_pg);
		if (r == 0)
			break;
		if (r < 0)
			scfdie();

		if (scf_pg_get_name(g_pg, pgname, max_name_len + 1) < 0)
			scfdie();
		clean_name(pgname);

		/* The grouping will dictate how we draw the edge */
		if (scf_pg_get_property(g_pg, SCF_PROPERTY_GROUPING, g_prop) !=
		    0)
			scfdie();

		if (scf_property_get_value(g_prop, g_val) != 0)
			scfdie();

		if (scf_value_get_astring(g_val, grouping, max_value_len + 1) <
		    0)
			scfdie();

		/* ENTITIES holds the FMRIs of the dependencies */
		if (scf_pg_get_property(g_pg, SCF_PROPERTY_ENTITIES, g_prop) !=
		    0)
			scfdie();

		if (scf_iter_property_values(g_valiter, g_prop) != 0)
			scfdie();

		for (;;) {
			const char *sname, *iname;
			int weight = 1;
			char opts[100];
			size_t l;

			r = scf_iter_next_value(g_valiter, g_val);
			if (r == 0)
				break;
			if (r < 0)
				scfdie();

			if (scf_value_get_astring(g_val, depname,
			    max_value_len + 1) < 0)
				scfdie();

			(void) strcpy(depname_copy, depname);

			/*
			 * This will fail if the dependency is on a file:,
			 * which is legitimate, but we'll skip.
			 *
			 * This function leaves sname & iname pointing into
			 * depname_copy, which may be modified.
			 */
			if (scf_parse_svc_fmri(depname_copy, NULL, &sname,
			    &iname, NULL, NULL) != 0)
				continue;

			if (scf_handle_decode_fmri(h, depname, NULL, g_svc,
			    g_inst, NULL, NULL, 0) != 0) {
				if (scf_error() != SCF_ERROR_NOT_FOUND)
					scfdie();
				continue;
			}

			if (omit_net_deps &&
			    (strcmp(sname, "network/loopback") == 0 ||
			    strcmp(sname, "network/physical") == 0) &&
			    !allowable_net_dep(fmri))
				continue;

			opts[0] = '\0';
			l = 0;
			if (strcmp(grouping, SCF_DEP_OPTIONAL_ALL) == 0) {
				l = strlcat(opts, "style=dashed,",
				    sizeof (opts));
			} else if (strcmp(grouping, SCF_DEP_EXCLUDE_ALL) == 0) {
				l = strlcat(opts, "arrowtail=odot,",
				    sizeof (opts));
			} else if (strcmp(grouping, SCF_DEP_REQUIRE_ALL) == 0) {
				weight += 2;
				l = strlcat(opts, "style=bold,", sizeof (opts));
			} else if (strcmp(grouping, SCF_DEP_REQUIRE_ANY) == 0) {
				weight += 1;
			}

			if (l >= sizeof (opts)) {
				(void) fputs("opts[] is too small.\n", stderr);
				exit(1);
			}

			if (opts[0] != '\0')
				opts[strlen(opts) - 1] = '\0';

			if (iname == NULL) {
				/*
				 * This is a service dependency.  Look up the
				 * service and output edges connecting that
				 * service node to each of its instances.
				 */

				if (scf_iter_service_instances(g_institer,
				    g_svc) != 0)
					scfdie();

				for (;;) {
					int i = 0;

					r = scf_iter_next_instance(g_institer,
					    g_inst);
					if (r == 0)
						break;
					if (r < 0)
						scfdie();

					if (scf_instance_to_fmri(g_inst,
					    dep_fmri, max_fmri_len + 1) == -1)
						scfdie();

					if (enabled && is_enabled(g_inst))
						i = 2;

					print_dependency(fmri, pgname,
					    dep_fmri, opts, weight + i);
				}
			} else {
				if (enabled && is_enabled(g_inst))
					weight += 2;

				print_dependency(fmri, pgname, depname, opts,
				    weight);
			}
		}
	}

	return (0);
}

/*
 * If requested, print the legend.  Otherwise print some graph settings and
 * call process_instance() for each service instance in the repository.
 */
int
main(int argc, char **argv)
{
	struct utsname utn;
	int r;
	time_t now;
	char timebuf[30];
	scf_scope_t *scope;
	scf_service_t *svc;
	scf_instance_t *inst;
	scf_iter_t *svciter, *institer;
	char *svcname;

	char *size = NULL;
	char *legendfile = NULL;

	for (;;) {
		int o = getopt(argc, argv, "s:l:x:L?");
		if (o == -1)
			break;

		switch (o) {
		case 's':
			size = optarg;
			break;

		case 'l':
			legendfile = optarg;
			break;

		case 'x':
			while (*optarg != '\0') {
				char *valp;
				int so;

				so = getsubopt(&optarg, (char * const *)x_opts,
				    &valp);
				if (so == -1 || valp != NULL)
					usage(argv[0], 0, stderr);

				switch (so) {
				case 0:
					omit_net_deps = 1;
					break;

				case 1:
					consolidate_inetd_svcs = 1;
					break;

				case 2:
					consolidate_rpcbind_svcs = 1;
					break;

				default:
					abort();
				}
			}
			break;

		case 'L':
			print_legend();
			return (0);

		case '?':
			usage(argv[0], optopt == '?', stdout);

		default:
			usage(argv[0], 0, stderr);
		}
	}

	r = uname(&utn);
	assert(r >= 0);

	now = time(NULL);
	(void) cftime(timebuf, NULL, &now);

	(void) printf("digraph scf {\n");
	(void) printf("label=\"%s %s %s\\n%s\";\n", utn.sysname, utn.version,
	    utn.machine, timebuf);
	(void) printf("node [shape=box,fontname=\"Helvetica\",fontsize=11];\n");
	if (size != NULL)
		(void) printf("size=\"%s\";\n", size);
	(void) printf("ranksep=\"2\";\n"
	    "rankdir=LR;\n"
	    "margin=1;\n");

	if (legendfile != NULL)
		/*
		 * The legend is just a node with the given PostScript as its
		 * shape.  dot will put it on the highest rank.  It usually
		 * appears too close to another node (system/zones, in
		 * particular); avoid that with a sufficiently large margin.
		 * (See expand.awk .)
		 */
		(void) printf("\n/* legend */\n"
		    "legend [shape=epsf,shapefile=\"%s\",label=\"\"];\n",
		    legendfile);

	(void) putchar('\n');

	h = scf_handle_create(SCF_VERSION);
	if (scf_handle_bind(h) != 0)
		scfdie();

	if ((scope = scf_scope_create(h)) == NULL ||
	    (svc = scf_service_create(h)) == NULL ||
	    (g_svc = scf_service_create(h)) == NULL ||
	    (inst = scf_instance_create(h)) == NULL ||
	    (g_inst = scf_instance_create(h)) == NULL ||
	    (svciter = scf_iter_create(h)) == NULL ||
	    (institer = scf_iter_create(h)) == NULL ||
	    (g_institer = scf_iter_create(h)) == NULL ||
	    (g_pgiter = scf_iter_create(h)) == NULL ||
	    (g_valiter = scf_iter_create(h)) == NULL ||
	    (g_pg = scf_pg_create(h)) == NULL ||
	    (g_prop = scf_property_create(h)) == NULL ||
	    (g_val = scf_value_create(h)) == NULL)
		scfdie();

	if ((max_name_len = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH)) < 0 ||
	    (max_value_len = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH)) < 0 ||
	    (max_fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH)) < 0)
		scfdie();

	allpgs_sz = 100;
	inetd_svcs_sz = 100;
	rpcbind_svcs_sz = 100;

	if ((svcname = malloc(max_name_len + 1)) == NULL ||
	    (instname = malloc(max_name_len + 1)) == NULL ||
	    (pgname = malloc(max_name_len + 1)) == NULL ||
	    (depname = malloc(max_value_len + 1)) == NULL ||
	    (depname_copy = malloc(max_value_len + 1)) == NULL ||
	    (grouping = malloc(max_value_len + 1)) == NULL ||
	    (fmri = malloc(max_fmri_len + 1)) == NULL ||
	    (dep_fmri = malloc(max_fmri_len + 1)) == NULL ||
	    (allpgs = malloc(allpgs_sz)) == NULL ||
	    (inetd_svcs = malloc(inetd_svcs_sz)) == NULL ||
	    (rpcbind_svcs = malloc(rpcbind_svcs_sz)) == NULL) {
		perror("malloc");
		exit(1);
	}

	inetd_svcs[0] = '\0';
	rpcbind_svcs[0] = '\0';

	if (scf_handle_get_scope(h, SCF_SCOPE_LOCAL, scope) != 0)
		scfdie();

	if (scf_iter_scope_services(svciter, scope) != 0)
		scfdie();

	for (;;) {
		r = scf_iter_next_service(svciter, svc);
		if (r == 0)
			break;
		if (r != 1)
			scfdie();

		if (scf_iter_service_instances(institer, svc) != 0)
			scfdie();

		if (scf_service_get_name(svc, svcname, max_name_len + 1) < 0)
			scfdie();

		if (strcmp(svcname, "system/svc/restarter") == 0)
			/* Otherwise this shows up as an unconnected node. */
			continue;

		for (;;) {
			r = scf_iter_next_instance(institer, inst);
			if (r == 0)
				break;
			if (r != 1)
				scfdie();

			if (process_instance(inst, svcname) != 0) {
				(void) fputs("process_instance() failed",
				    stderr);
				exit(1);
			}
		}
	}

	if (inetd_svcs[0] != '\0') {
		print_service_node("inetd_services", inetd_svcs,
		    "<restarter> restarter", choose_color("network/", 1));
		print_dependency("inetd_services", "restarter",
		    "svc:/network/inetd:default", "", 1);
	}

	if (rpcbind_svcs[0] != '\0') {
		print_service_node("rpcbind_services", rpcbind_svcs,
		    "<restarter> restarter | <rpcbind> rpcbind",
		    choose_color("network/", 1));
		print_dependency("rpcbind_services", "restarter",
		    "svc:/network/inetd:default", "", 1);
		print_dependency("rpcbind_services", "rpcbind",
		    "svc:/network/rpc/bind:default", "", 1);
	}

	(void) printf("}\n");
	return (0);
}
 
 
Close
loading
Please Confirm
Close