0N/A/*
0N/A * CDDL HEADER START
0N/A *
0N/A * The contents of this file are subject to the terms of the
0N/A * Common Development and Distribution License (the "License").
0N/A * You may not use this file except in compliance with the License.
0N/A *
0N/A * You can obtain a copy of the license at CDDL.LICENSE.
0N/A * See the License for the specific language governing permissions
0N/A * and limitations under the License.
0N/A *
0N/A * When distributing Covered Code, include this CDDL HEADER in each
0N/A * file and include the License file at CDDL.LICENSE.
0N/A * If applicable, add the following below this CDDL HEADER, with the
0N/A * fields enclosed by brackets "[]" replaced with your own identifying
0N/A * information: Portions Copyright [yyyy] [name of copyright owner]
0N/A *
0N/A * CDDL HEADER END
0N/A */
0N/A
0N/A/*
0N/A * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
0N/A * Use is subject to license terms.
0N/A */
0N/A
0N/A#pragma ident "@(#)scfdot.c 1.23 05/10/18 SMI"
0N/A
0N/A/*
0N/A * Generate a dot file for the SMF dependency graph on this machine.
0N/A *
0N/A * We operate in two modes: with and without -L. Without -L, we print nodes
0N/A * for each instance and edges for each dependency. Fortunately dot allows
0N/A * forward references, so we can do this in one pass. Options are
0N/A *
0N/A * -s width,height Size, in inches, that the graph should be limited to.
0N/A *
0N/A * -l legend.ps PostScript file which should be used as the legend.
0N/A *
0N/A * -x opts Simplify the graph. opts should be a comma-separated
0N/A * list of
0N/A *
0N/A * omit_net_deps Omit most of the dependencies on
0N/A * network/loopback and network/physical. (See
0N/A * allowable_net_dep().)
0N/A *
0N/A * consolidate_inetd_svcs Consolidate services which only depend on
0N/A * network/inetd into a single node.
0N/A *
0N/A * consolidate_rpcbind_svcs Consolidate services which only depend on
0N/A * network/inetd and rpc/bind into a single node.
0N/A *
0N/A * Other hard-coded graph settings (rankdir, nodesep, margin) were intended
0N/A * for a 42" plotter.
0N/A *
0N/A * -L causes the program to print a dot file for use as a legend. It
0N/A * currently consists of eight nodes which demonstrate the color scheme and
0N/A * the dependency types. The nodes are enclosed in a box which is labeled
0N/A * "legend".
0N/A */
0N/A
0N/A#include <sys/types.h>
0N/A#include <sys/param.h>
0N/A#include <sys/utsname.h>
0N/A#include <assert.h>
0N/A#include <libscf.h>
0N/A#include <stdio.h>
0N/A#include <stdlib.h>
0N/A#include <strings.h>
0N/A#include <time.h>
0N/A#include <unistd.h>
0N/A
0N/A/* Private libscf function */
0N/Aextern int scf_parse_svc_fmri(char *fmri, const char **scope,
0N/A const char **service, const char **instance, const char **propertygroup,
0N/A const char **property);
0N/A
0N/A
0N/A/*
0N/A * We color nodes by FMRI and enabledness. For each class we specify
0N/A * a foreground color, which will be the color of the text and the outline,
0N/A * and a background color, which will fill the node.
0N/A *
0N/A * In this scheme we'll color services who's FMRIs begin with "system" orange,
0N/A * "network" blue, "milestone" green, and other services light gray. For each
0N/A * category we'll color the disabled services a faded shade of their enabled
0N/A * counterparts. The foreground of the enabled services will all be black,
0N/A * and the backgrounds will be
0N/A */
0N/A
0N/A#define ORANGE "#ED9B4F"
0N/A#define BLUE "#A3B8CB"
0N/A#define GREEN "#C5D5A9"
0N/A#define GRAY "#EDEFF2"
0N/A
0N/A/*
0N/A * To make disabled services appear faded, I took the above colors and halved
0N/A * their saturation.
0N/A */
0N/A
0N/A#define LTBLACK "#808080"
0N/A
0N/A#define LTORANGE "#EDC39C"
0N/A#define LTBLUE "#B7C1CB"
0N/A#define LTGREEN "#CDD5C0"
0N/A#define LTGRAY "#F0F1F2"
0N/A
0N/Astatic const struct coloring {
0N/A const char *cat;
0N/A const char *colors[2][2];
0N/A} category_colors[] = {
0N/A { "system/", { { "black", ORANGE }, { LTBLACK, LTORANGE } } },
0N/A { "network/", { { "black", BLUE }, { LTBLACK, LTBLUE } } },
0N/A { "milestone/", { { "black", GREEN }, { LTBLACK, LTGREEN } } },
0N/A { NULL, { { "black", GRAY }, { LTBLACK, LTGRAY } } },
0N/A};
0N/A
0N/A/* Graph simplification options, for use with getsubopt(). */
0N/Astatic const char * const x_opts[] = {
0N/A "omit_net_deps",
0N/A "consolidate_inetd_svcs",
0N/A "consolidate_rpcbind_svcs",
0N/A NULL
0N/A};
0N/A
0N/Astatic int omit_net_deps = 0;
0N/Astatic int consolidate_inetd_svcs = 0;
0N/Astatic int consolidate_rpcbind_svcs = 0;
0N/A
0N/A/* Consolidation strings */
0N/Astatic char *inetd_svcs, *rpcbind_svcs;
0N/Astatic size_t inetd_svcs_sz, rpcbind_svcs_sz;
0N/A
0N/A
0N/Astatic scf_handle_t *h;
0N/A
0N/A/* Scratch libscf objects, to save time. */
0N/Astatic scf_service_t *g_svc;
0N/Astatic scf_instance_t *g_inst;
0N/Astatic scf_snapshot_t *g_snap;
0N/Astatic scf_propertygroup_t *g_pg;
0N/Astatic scf_property_t *g_prop;
0N/Astatic scf_value_t *g_val;
0N/Astatic scf_iter_t *g_institer, *g_pgiter, *g_valiter;
0N/A
0N/Astatic ssize_t max_fmri_len, max_name_len, max_value_len;
0N/A
0N/A
0N/Astatic void
0N/Astrappend(const char *str, char **bufp, size_t *bufszp)
0N/A{
0N/A size_t str_sz;
0N/A
0N/A str_sz = strlen(str);
0N/A
0N/A if (strlen(*bufp) + str_sz + 1 > *bufszp) {
0N/A *bufszp += str_sz;
0N/A *bufp = realloc(*bufp, *bufszp);
0N/A if (*bufp == NULL) {
0N/A perror("realloc");
0N/A exit(1);
0N/A }
0N/A }
0N/A
0N/A (void) strcat(*bufp, str);
0N/A}
0N/A
0N/Astatic void
0N/Ascfdie_lineno(int lineno)
0N/A{
0N/A (void) fprintf(stderr, "%s:%d: Unexpected libscf error: %s.\n",
0N/A __FILE__, lineno, scf_strerror(scf_error()));
0N/A exit(1);
0N/A}
0N/A
0N/A#define scfdie() scfdie_lineno(__LINE__)
0N/A
0N/A/*
0N/A * Return 1 if inst is enabled, 0 otherwise. Uses g_pg, g_prop, and g_val.
0N/A */
0N/Astatic int
0N/Ais_enabled(scf_instance_t *inst)
0N/A{
0N/A uint8_t b;
0N/A
0N/A if (scf_instance_get_pg(inst, SCF_PG_GENERAL, g_pg) != 0) {
0N/A if (scf_error() != SCF_ERROR_NOT_FOUND)
0N/A scfdie();
0N/A return (0);
0N/A }
0N/A
0N/A if (scf_pg_get_property(g_pg, SCF_PROPERTY_ENABLED, g_prop) != 0) {
0N/A if (scf_error() != SCF_ERROR_NOT_FOUND)
0N/A scfdie();
0N/A return (0);
0N/A }
0N/A
0N/A if (scf_property_get_value(g_prop, g_val) != 0) {
0N/A switch (scf_error()) {
0N/A case SCF_ERROR_NOT_FOUND:
0N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
0N/A return (0);
0N/A
0N/A default:
0N/A scfdie();
0N/A }
0N/A }
0N/A
0N/A if (scf_value_get_boolean(g_val, &b) != 0) {
0N/A if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
0N/A scfdie();
0N/A return (0);
0N/A }
0N/A
0N/A return (b != 0);
0N/A}
0N/A
0N/A/*
0N/A * Fill in buf with the restarter of instance i. Uses g_pg, g_prop, and
0N/A * g_val.
0N/A */
0N/Astatic void
0N/Aget_restarter(scf_instance_t *i, char *buf, size_t bufsz)
0N/A{
0N/A if (scf_instance_get_pg_composed(i, NULL, SCF_PG_GENERAL, g_pg) != 0)
0N/A scfdie();
0N/A
0N/A buf[0] = '\0';
0N/A if (scf_pg_get_property(g_pg, SCF_PROPERTY_RESTARTER, g_prop) != 0) {
0N/A if (scf_error() != SCF_ERROR_NOT_FOUND)
0N/A scfdie();
0N/A return;
0N/A }
0N/A
0N/A if (scf_property_get_value(g_prop, g_val) != 0) {
0N/A switch (scf_error()) {
0N/A case SCF_ERROR_NOT_FOUND:
0N/A case SCF_ERROR_CONSTRAINT_VIOLATED:
0N/A return;
0N/A
0N/A default:
0N/A scfdie();
0N/A }
0N/A }
0N/A
0N/A if (scf_value_get_astring(g_val, buf, bufsz) < 0) {
0N/A if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
0N/A scfdie();
0N/A }
0N/A}
0N/A
0N/Astatic void
0N/Ausage(const char *argv0, int help, FILE *stream)
0N/A{
0N/A (void) fprintf(stream,
0N/A "Usage: %1$s [-s width,height] [-l legend.ps] [-x opts]\n"
0N/A " %1$s -L\n", argv0);
0N/A if (help) {
0N/A const char * const *opt;
0N/A
0N/A (void) fprintf(stream,
0N/A "Where opts is a comma-separated list of\n");
0N/A
0N/A for (opt = &x_opts[0]; *opt != NULL; ++opt)
0N/A (void) fprintf(stream, "\t%s\n", *opt);
0N/A }
0N/A exit(help ? 0 : 2);
0N/A}
0N/A
0N/A/*
0N/A * Make name suitable for dot.
0N/A */
0N/Astatic void
0N/Aclean_name(char *name)
0N/A{
0N/A for (; *name != '\0'; ++name) {
0N/A if (*name == '-')
0N/A *name = '_';
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Print an edge for a dependency. port should be the name of the dependency
0N/A * group.
0N/A */
0N/Astatic void
0N/Aprint_dependency(const char *from, const char *port, const char *to,
0N/A const char *opts, int weight)
0N/A{
0N/A (void) printf("\"%s\":%s:e -> \"%s\"", from, port, to);
0N/A
0N/A if (weight != 1 || (opts != NULL && opts[0] != '\0')) {
0N/A (void) fputs(" [", stdout);
0N/A if (opts != NULL && opts[0] != '\0') {
0N/A (void) fputs(opts, stdout);
0N/A (void) putchar(',');
0N/A }
0N/A if (weight != 1)
0N/A (void) printf("weight=%d,", weight);
0N/A (void) putchar(']');
0N/A }
0N/A
0N/A (void) puts(";");
0N/A}
0N/A
0N/A/*
0N/A * Choose a coloring for the given service. Returns a pointer to an array of
0N/A * two string pointers, the first being the text color and the second being
0N/A * the fill color.
0N/A */
0N/Astatic const char * const *
0N/Achoose_color(const char *fmri, int enabled)
0N/A{
0N/A const struct coloring *cp;
0N/A
0N/A if (strncmp(fmri, "svc:/", sizeof ("svc:/") - 1) == 0)
0N/A fmri += sizeof ("svc:/") - 1;
0N/A
0N/A for (cp = &category_colors[0]; cp->cat != NULL; ++cp) {
0N/A if (strncmp(fmri, cp->cat, strlen(cp->cat)) == 0)
0N/A break;
0N/A }
0N/A
0N/A return (cp->colors[enabled ? 0 : 1]);
0N/A}
0N/A
0N/A/*
0N/A * Print a node for a service. dependencies should either be an empty string
0N/A * or a string of "<dependency port name> dependency name" strings joined by
0N/A * pipes ("|").
0N/A */
0N/Astatic void
0N/Aprint_service_node(const char *fmri, const char *label,
0N/A const char *dependencies, const char * const *colors)
0N/A{
0N/A const char *fg = colors[0];
0N/A const char *bg = colors[1];
0N/A
0N/A if (dependencies[0] != '\0')
0N/A (void) printf("\"%s\" [shape=record,color=\"%s\",style=filled,"
0N/A "fillcolor=\"%s\",fontcolor=\"%s\","
0N/A "label=\"{<foo> %s | {%s}}\"];\n", fmri, fg, bg, fg, label,
0N/A dependencies);
0N/A else
0N/A (void) printf("\"%s\" [shape=record,color=\"%s\",style=filled,"
0N/A "fillcolor=\"%s\",fontcolor=\"%s\",label=\"%s\"];\n",
0N/A fmri, fg, bg, fg, label);
0N/A}
0N/A
0N/A/*
0N/A * Print some fake service nodes in a box to demonstrate the coloring and the
0N/A * dependency types.
0N/A */
0N/Astatic void
0N/Aprint_legend()
0N/A{
0N/A const char *fmri;
0N/A
0N/A (void) printf("digraph legend {\n"
0N/A "node [fontname=\"Helvetica\",fontsize=11];\n"
0N/A "ranksep=\"2\";\n"
0N/A "rankdir=LR;\n");
0N/A
0N/A (void) printf("\nsubgraph clusterlegend {\n"
0N/A "label=\"legend\";\n"
0N/A "color=\"black\";\n");
0N/A
0N/A (void) putchar('\n');
0N/A
0N/A fmri = "svc:/system/disabled:default";
0N/A print_service_node(fmri, fmri + 5, "<dg>dependency_group",
0N/A choose_color(fmri, 0));
0N/A fmri = "svc:/system/enabled:default";
0N/A print_service_node(fmri, fmri + 5, "", choose_color(fmri, 1));
0N/A print_dependency("svc:/system/disabled:default", "dg",
0N/A "svc:/system/enabled:default", "label=\"require_all\",style=bold",
0N/A 10);
0N/A
0N/A fmri = "svc:/network/disabled:default";
0N/A print_service_node(fmri, fmri + 5, "<dg>dependency_group",
0N/A choose_color(fmri, 0));
0N/A fmri = "svc:/network/enabled:default";
0N/A print_service_node(fmri, fmri + 5, "", choose_color(fmri, 1));
0N/A print_dependency("svc:/network/disabled:default", "dg",
0N/A "svc:/network/enabled:default", "label=\"require_any\"", 1);
0N/A
0N/A fmri = "svc:/milestone/disabled:default";
0N/A print_service_node(fmri, fmri + 5, "<dg>dependency_group",
0N/A choose_color(fmri, 0));
0N/A fmri = "svc:/milestone/enabled:default";
0N/A print_service_node(fmri, fmri + 5, "", choose_color(fmri, 1));
0N/A print_dependency("svc:/milestone/disabled:default", "dg",
0N/A "svc:/milestone/enabled:default",
0N/A "label=\"optional_all\",style=dashed", 1);
0N/A
0N/A fmri = "svc:/other/disabled:default";
0N/A print_service_node(fmri, fmri + 5, "<dg>dependency_group",
0N/A choose_color(fmri, 0));
0N/A fmri = "svc:/other/enabled:default";
0N/A print_service_node(fmri, fmri + 5, "", choose_color(fmri, 1));
0N/A print_dependency("svc:/other/disabled:default", "dg",
0N/A "svc:/other/enabled:default",
0N/A "label=\"exclude_all\",arrowtail=odot", 1);
0N/A
0N/A (void) printf("}\n}\n");
0N/A}
0N/A
0N/A/*
0N/A * Return true if we shouldn't omit fmri's dependency on network/loopback or
0N/A * network/physical, even under -x omit_net_deps. (Otherwise they'd have no
0N/A * dependents, which would produce a bad graph.)
0N/A */
0N/Astatic int
0N/Aallowable_net_dep(const char *fmri)
0N/A{
0N/A return (strcmp(fmri, "svc:/system/identity:node") == 0 ||
0N/A strcmp(fmri, "svc:/system/identity:domain") == 0 ||
0N/A strcmp(fmri, "svc:/network/initial:default") == 0 ||
0N/A strcmp(fmri, "svc:/milestone/single-user:default") == 0 ||
0N/A strcmp(fmri, "svc:/network/inetd:default") == 0 ||
0N/A strcmp(fmri, "svc:/network/http:apache2") == 0);
0N/A}
0N/A
0N/A/* dependency name accumulator */
0N/Astatic char *allpgs;
0N/Astatic size_t allpgs_sz;
0N/A
0N/Astatic void
0N/Aadd_dep(const char *str)
0N/A{
0N/A /* Append sprintf("<%s> %s|", str, str) */
0N/A strappend("<", &allpgs, &allpgs_sz);
0N/A strappend(str, &allpgs, &allpgs_sz);
0N/A strappend("> ", &allpgs, &allpgs_sz);
0N/A strappend(str, &allpgs, &allpgs_sz);
0N/A strappend("|", &allpgs, &allpgs_sz);
0N/A}
0N/A
0N/Astatic char *fmri, *dep_fmri; /* max_fmri_len + 1 long */
0N/Astatic char *instname, *pgname; /* max_name_len + 1 long */
0N/Astatic char *depname, *depname_copy, *grouping; /* max_value_len + 1 long */
0N/A
0N/A/*
0N/A * For the given instance, generate a node and the appropriate edges.
0N/A */
0N/Astatic int
0N/Aprocess_instance(scf_instance_t *i, const char *svcname)
0N/A{
0N/A int enabled;
0N/A int ndeps;
0N/A int inetd_svc;
0N/A int non_rpcbind;
0N/A
0N/A scf_snapshot_t *running; /* NULL or == g_snap */
0N/A
0N/A assert(i);
0N/A
0N/A /*
0N/A * Node generation: Collect the name, restarter, dependency names, and
0N/A * enabled status and call print_service_node(). The dependency names
0N/A * will be accumulated in allpgs.
0N/A */
0N/A
0N/A if (scf_instance_get_name(i, instname, max_name_len + 1) == -1) {
0N/A (void) fprintf(stderr, "instance_get_name() failed: %s",
0N/A scf_strerror(scf_error()));
0N/A return (-1);
0N/A }
0N/A
0N/A (void) snprintf(fmri, max_fmri_len + 1, "svc:/%s:%s", svcname,
0N/A instname);
0N/A
0N/A ndeps = 0;
0N/A allpgs[0] = '\0';
0N/A inetd_svc = 0;
0N/A
0N/A get_restarter(i, depname, max_value_len + 1);
0N/A
0N/A if (depname[0] != '\0') {
0N/A ++ndeps;
0N/A add_dep("restarter");
0N/A inetd_svc = (strstr(depname, "network/inetd:default") != NULL);
0N/A }
0N/A
0N/A if (scf_instance_get_snapshot(i, "running", g_snap) == 0) {
0N/A running = g_snap;
0N/A } else {
0N/A if (scf_error() != SCF_ERROR_NOT_FOUND)
0N/A scfdie();
0N/A running = NULL;
0N/A }
0N/A
0N/A if (scf_iter_instance_pgs_typed_composed(g_pgiter, i, running,
0N/A SCF_GROUP_DEPENDENCY) != 0)
0N/A scfdie();
0N/A
0N/A non_rpcbind = 0;
0N/A
0N/A while (scf_iter_next_pg(g_pgiter, g_pg) > 0) {
0N/A if (scf_pg_get_property(g_pg, SCF_PROPERTY_ENTITIES, NULL) !=
0N/A 0) {
0N/A if (scf_error() == SCF_ERROR_NOT_FOUND)
0N/A continue;
0N/A scfdie();
0N/A }
0N/A
0N/A ++ndeps;
0N/A
0N/A if (scf_pg_get_name(g_pg, pgname, max_name_len + 1) < 0)
0N/A scfdie();
0N/A
0N/A if (!non_rpcbind && strcmp(pgname, "rpcbind") != 0)
0N/A non_rpcbind = 1;
0N/A
0N/A clean_name(pgname);
0N/A add_dep(pgname);
0N/A }
0N/A
0N/A if (consolidate_inetd_svcs && inetd_svc && ndeps == 1) {
0N/A strappend(fmri + sizeof ("svc:/") - 1, &inetd_svcs,
0N/A &inetd_svcs_sz);
0N/A strappend("\\n", &inetd_svcs, &inetd_svcs_sz);
0N/A return (0);
0N/A }
0N/A
0N/A if (consolidate_rpcbind_svcs && inetd_svc && non_rpcbind == 0 &&
0N/A ndeps == 2) {
0N/A /*
0N/A * Exclude network/rpc/meta and rpc/smserver since they have
0N/A * dependents.
0N/A */
0N/A if (strcmp(svcname, "network/rpc/meta") != 0 &&
0N/A strcmp(svcname, "network/rpc/smserver") != 0) {
0N/A strappend(fmri + sizeof ("svc:/") - 1, &rpcbind_svcs,
0N/A &rpcbind_svcs_sz);
0N/A strappend("\\n", &rpcbind_svcs, &rpcbind_svcs_sz);
0N/A return (0);
0N/A }
0N/A }
0N/A
0N/A if (allpgs[0] != '\0')
0N/A allpgs[strlen(allpgs) - 1] = '\0'; /* nuke trailing | */
0N/A
0N/A enabled = is_enabled(i);
0N/A
0N/A print_service_node(fmri, fmri + sizeof ("svc:/") - 1, allpgs,
0N/A choose_color(fmri, enabled));
0N/A
0N/A /*
0N/A * Edges: One for the restarter, if it is not the default (svc.startd)
0N/A * (denoted by an empty string), and one for each dependency service.
0N/A * Remember that dependency groups can name multiple services, and
0N/A * each service can have multiple instances.
0N/A */
0N/A
0N/A if (depname[0] != '\0')
0N/A print_dependency(fmri, "restarter", depname, "", 1);
0N/A
0N/A if (scf_iter_instance_pgs_typed_composed(g_pgiter, i, running,
0N/A SCF_GROUP_DEPENDENCY) != 0)
0N/A scfdie();
0N/A
0N/A for (;;) {
0N/A int r;
0N/A
0N/A r = scf_iter_next_pg(g_pgiter, g_pg);
0N/A if (r == 0)
0N/A break;
0N/A if (r < 0)
0N/A scfdie();
0N/A
0N/A if (scf_pg_get_name(g_pg, pgname, max_name_len + 1) < 0)
0N/A scfdie();
0N/A clean_name(pgname);
0N/A
0N/A /* The grouping will dictate how we draw the edge */
0N/A if (scf_pg_get_property(g_pg, SCF_PROPERTY_GROUPING, g_prop) !=
0N/A 0)
0N/A scfdie();
0N/A
0N/A if (scf_property_get_value(g_prop, g_val) != 0)
0N/A scfdie();
0N/A
0N/A if (scf_value_get_astring(g_val, grouping, max_value_len + 1) <
0N/A 0)
0N/A scfdie();
0N/A
0N/A /* ENTITIES holds the FMRIs of the dependencies */
0N/A if (scf_pg_get_property(g_pg, SCF_PROPERTY_ENTITIES, g_prop) !=
0N/A 0)
0N/A scfdie();
0N/A
0N/A if (scf_iter_property_values(g_valiter, g_prop) != 0)
0N/A scfdie();
0N/A
0N/A for (;;) {
0N/A const char *sname, *iname;
0N/A int weight = 1;
0N/A char opts[100];
0N/A size_t l;
0N/A
0N/A r = scf_iter_next_value(g_valiter, g_val);
0N/A if (r == 0)
0N/A break;
0N/A if (r < 0)
0N/A scfdie();
0N/A
0N/A if (scf_value_get_astring(g_val, depname,
0N/A max_value_len + 1) < 0)
0N/A scfdie();
0N/A
0N/A (void) strcpy(depname_copy, depname);
0N/A
0N/A /*
0N/A * This will fail if the dependency is on a file:,
0N/A * which is legitimate, but we'll skip.
0N/A *
0N/A * This function leaves sname & iname pointing into
0N/A * depname_copy, which may be modified.
0N/A */
0N/A if (scf_parse_svc_fmri(depname_copy, NULL, &sname,
0N/A &iname, NULL, NULL) != 0)
0N/A continue;
0N/A
0N/A if (scf_handle_decode_fmri(h, depname, NULL, g_svc,
0N/A g_inst, NULL, NULL, 0) != 0) {
0N/A if (scf_error() != SCF_ERROR_NOT_FOUND)
0N/A scfdie();
0N/A continue;
0N/A }
0N/A
0N/A if (omit_net_deps &&
0N/A (strcmp(sname, "network/loopback") == 0 ||
0N/A strcmp(sname, "network/physical") == 0) &&
0N/A !allowable_net_dep(fmri))
0N/A continue;
0N/A
0N/A opts[0] = '\0';
0N/A l = 0;
0N/A if (strcmp(grouping, SCF_DEP_OPTIONAL_ALL) == 0) {
0N/A l = strlcat(opts, "style=dashed,",
0N/A sizeof (opts));
0N/A } else if (strcmp(grouping, SCF_DEP_EXCLUDE_ALL) == 0) {
0N/A l = strlcat(opts, "arrowtail=odot,",
0N/A sizeof (opts));
0N/A } else if (strcmp(grouping, SCF_DEP_REQUIRE_ALL) == 0) {
0N/A weight += 2;
0N/A l = strlcat(opts, "style=bold,", sizeof (opts));
0N/A } else if (strcmp(grouping, SCF_DEP_REQUIRE_ANY) == 0) {
0N/A weight += 1;
0N/A }
0N/A
0N/A if (l >= sizeof (opts)) {
0N/A (void) fputs("opts[] is too small.\n", stderr);
0N/A exit(1);
0N/A }
0N/A
0N/A if (opts[0] != '\0')
0N/A opts[strlen(opts) - 1] = '\0';
0N/A
0N/A if (iname == NULL) {
0N/A /*
0N/A * This is a service dependency. Look up the
0N/A * service and output edges connecting that
0N/A * service node to each of its instances.
0N/A */
0N/A
0N/A if (scf_iter_service_instances(g_institer,
0N/A g_svc) != 0)
0N/A scfdie();
0N/A
0N/A for (;;) {
0N/A int i = 0;
0N/A
0N/A r = scf_iter_next_instance(g_institer,
0N/A g_inst);
0N/A if (r == 0)
0N/A break;
0N/A if (r < 0)
0N/A scfdie();
0N/A
0N/A if (scf_instance_to_fmri(g_inst,
0N/A dep_fmri, max_fmri_len + 1) == -1)
0N/A scfdie();
0N/A
0N/A if (enabled && is_enabled(g_inst))
0N/A i = 2;
0N/A
0N/A print_dependency(fmri, pgname,
0N/A dep_fmri, opts, weight + i);
0N/A }
0N/A } else {
0N/A if (enabled && is_enabled(g_inst))
0N/A weight += 2;
0N/A
0N/A print_dependency(fmri, pgname, depname, opts,
0N/A weight);
0N/A }
0N/A }
0N/A }
0N/A
0N/A return (0);
0N/A}
0N/A
0N/A/*
0N/A * If requested, print the legend. Otherwise print some graph settings and
0N/A * call process_instance() for each service instance in the repository.
0N/A */
0N/Aint
0N/Amain(int argc, char **argv)
0N/A{
0N/A struct utsname utn;
0N/A int r;
0N/A time_t now;
0N/A char timebuf[30];
0N/A scf_scope_t *scope;
0N/A scf_service_t *svc;
0N/A scf_instance_t *inst;
0N/A scf_iter_t *svciter, *institer;
0N/A char *svcname;
0N/A
0N/A char *size = NULL;
0N/A char *legendfile = NULL;
0N/A
0N/A for (;;) {
0N/A int o = getopt(argc, argv, "s:l:x:L?");
0N/A if (o == -1)
0N/A break;
0N/A
0N/A switch (o) {
0N/A case 's':
0N/A size = optarg;
0N/A break;
0N/A
0N/A case 'l':
0N/A legendfile = optarg;
0N/A break;
0N/A
0N/A case 'x':
0N/A while (*optarg != '\0') {
0N/A char *valp;
0N/A int so;
0N/A
0N/A so = getsubopt(&optarg, (char * const *)x_opts,
0N/A &valp);
0N/A if (so == -1 || valp != NULL)
0N/A usage(argv[0], 0, stderr);
0N/A
0N/A switch (so) {
0N/A case 0:
0N/A omit_net_deps = 1;
0N/A break;
0N/A
0N/A case 1:
0N/A consolidate_inetd_svcs = 1;
0N/A break;
0N/A
0N/A case 2:
0N/A consolidate_rpcbind_svcs = 1;
0N/A break;
0N/A
0N/A default:
0N/A abort();
0N/A }
0N/A }
0N/A break;
0N/A
0N/A case 'L':
0N/A print_legend();
0N/A return (0);
0N/A
0N/A case '?':
0N/A usage(argv[0], optopt == '?', stdout);
0N/A
0N/A default:
0N/A usage(argv[0], 0, stderr);
0N/A }
0N/A }
0N/A
0N/A r = uname(&utn);
0N/A assert(r >= 0);
0N/A
0N/A now = time(NULL);
0N/A (void) cftime(timebuf, NULL, &now);
0N/A
0N/A (void) printf("digraph scf {\n");
0N/A (void) printf("label=\"%s %s %s\\n%s\";\n", utn.sysname, utn.version,
0N/A utn.machine, timebuf);
0N/A (void) printf("node [shape=box,fontname=\"Helvetica\",fontsize=11];\n");
0N/A if (size != NULL)
0N/A (void) printf("size=\"%s\";\n", size);
0N/A (void) printf("ranksep=\"2\";\n"
0N/A "rankdir=LR;\n"
0N/A "margin=1;\n");
0N/A
0N/A if (legendfile != NULL)
0N/A /*
0N/A * The legend is just a node with the given PostScript as its
0N/A * shape. dot will put it on the highest rank. It usually
0N/A * appears too close to another node (system/zones, in
0N/A * particular); avoid that with a sufficiently large margin.
0N/A * (See expand.awk .)
0N/A */
0N/A (void) printf("\n/* legend */\n"
0N/A "legend [shape=epsf,shapefile=\"%s\",label=\"\"];\n",
0N/A legendfile);
0N/A
0N/A (void) putchar('\n');
0N/A
0N/A h = scf_handle_create(SCF_VERSION);
0N/A if (scf_handle_bind(h) != 0)
0N/A scfdie();
0N/A
0N/A if ((scope = scf_scope_create(h)) == NULL ||
0N/A (svc = scf_service_create(h)) == NULL ||
0N/A (g_svc = scf_service_create(h)) == NULL ||
0N/A (inst = scf_instance_create(h)) == NULL ||
0N/A (g_inst = scf_instance_create(h)) == NULL ||
0N/A (svciter = scf_iter_create(h)) == NULL ||
0N/A (institer = scf_iter_create(h)) == NULL ||
0N/A (g_institer = scf_iter_create(h)) == NULL ||
0N/A (g_pgiter = scf_iter_create(h)) == NULL ||
0N/A (g_valiter = scf_iter_create(h)) == NULL ||
0N/A (g_pg = scf_pg_create(h)) == NULL ||
0N/A (g_prop = scf_property_create(h)) == NULL ||
0N/A (g_val = scf_value_create(h)) == NULL)
0N/A scfdie();
0N/A
0N/A if ((max_name_len = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH)) < 0 ||
0N/A (max_value_len = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH)) < 0 ||
0N/A (max_fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH)) < 0)
0N/A scfdie();
0N/A
0N/A allpgs_sz = 100;
0N/A inetd_svcs_sz = 100;
0N/A rpcbind_svcs_sz = 100;
0N/A
0N/A if ((svcname = malloc(max_name_len + 1)) == NULL ||
0N/A (instname = malloc(max_name_len + 1)) == NULL ||
0N/A (pgname = malloc(max_name_len + 1)) == NULL ||
0N/A (depname = malloc(max_value_len + 1)) == NULL ||
0N/A (depname_copy = malloc(max_value_len + 1)) == NULL ||
0N/A (grouping = malloc(max_value_len + 1)) == NULL ||
0N/A (fmri = malloc(max_fmri_len + 1)) == NULL ||
0N/A (dep_fmri = malloc(max_fmri_len + 1)) == NULL ||
0N/A (allpgs = malloc(allpgs_sz)) == NULL ||
0N/A (inetd_svcs = malloc(inetd_svcs_sz)) == NULL ||
0N/A (rpcbind_svcs = malloc(rpcbind_svcs_sz)) == NULL) {
0N/A perror("malloc");
0N/A exit(1);
0N/A }
0N/A
0N/A inetd_svcs[0] = '\0';
0N/A rpcbind_svcs[0] = '\0';
0N/A
0N/A if (scf_handle_get_scope(h, SCF_SCOPE_LOCAL, scope) != 0)
0N/A scfdie();
0N/A
0N/A if (scf_iter_scope_services(svciter, scope) != 0)
0N/A scfdie();
0N/A
0N/A for (;;) {
0N/A r = scf_iter_next_service(svciter, svc);
0N/A if (r == 0)
0N/A break;
0N/A if (r != 1)
0N/A scfdie();
0N/A
0N/A if (scf_iter_service_instances(institer, svc) != 0)
0N/A scfdie();
0N/A
0N/A if (scf_service_get_name(svc, svcname, max_name_len + 1) < 0)
0N/A scfdie();
0N/A
0N/A if (strcmp(svcname, "system/svc/restarter") == 0)
0N/A /* Otherwise this shows up as an unconnected node. */
0N/A continue;
0N/A
0N/A for (;;) {
0N/A r = scf_iter_next_instance(institer, inst);
0N/A if (r == 0)
0N/A break;
0N/A if (r != 1)
0N/A scfdie();
0N/A
0N/A if (process_instance(inst, svcname) != 0) {
0N/A (void) fputs("process_instance() failed",
0N/A stderr);
0N/A exit(1);
0N/A }
0N/A }
0N/A }
0N/A
0N/A if (inetd_svcs[0] != '\0') {
0N/A print_service_node("inetd_services", inetd_svcs,
0N/A "<restarter> restarter", choose_color("network/", 1));
0N/A print_dependency("inetd_services", "restarter",
0N/A "svc:/network/inetd:default", "", 1);
0N/A }
0N/A
0N/A if (rpcbind_svcs[0] != '\0') {
0N/A print_service_node("rpcbind_services", rpcbind_svcs,
0N/A "<restarter> restarter | <rpcbind> rpcbind",
0N/A choose_color("network/", 1));
0N/A print_dependency("rpcbind_services", "restarter",
0N/A "svc:/network/inetd:default", "", 1);
0N/A print_dependency("rpcbind_services", "rpcbind",
0N/A "svc:/network/rpc/bind:default", "", 1);
0N/A }
0N/A
0N/A (void) printf("}\n");
0N/A return (0);
0N/A}