svcs.c revision 1f6eb0216cb17ca5fdff9563329f1dda47c8b801
/*
* 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 usr/src/OPENSOLARIS.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 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]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* svcs - display attributes of service instances
*
* We have two output formats and six instance selection mechanisms. The
* primary output format is a line of attributes (selected by -o), possibly
* followed by process description lines (if -p is specified), for each
* instance selected. The columns available to display are described by the
* struct column columns array. The columns to actually display are kept in
* the opt_columns array as indicies into the columns array. The selection
* mechanisms available for this format are service FMRIs (selects all child
* instances), instance FMRIs, instance FMRI glob patterns, instances with
* a certain restarter (-R), dependencies of instances (-d), and dependents of
* instances (-D). Since the lines must be sorted (per -sS), we'll just stick
* each into a data structure and print them in order when we're done. To
* avoid listing the same instance twice (when -d and -D aren't given), we'll
* use a hash table of FMRIs to record that we've listed (added to the tree)
* an instance.
*
* The secondary output format (-l "long") is a paragraph of text for the
* services or instances selected. Not needing to be sorted, it's implemented
* by just calling print_detailed() for each FMRI given.
*/
#include "svcs.h"
/* Get the byteorder macros to ease sorting. */
#include <inttypes.h>
#include <sys/contract.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <libcontract.h>
#include <libcontract_priv.h>
#include <libintl.h>
#include <libscf.h>
#include <libscf_priv.h>
#include <libuutil.h>
#include <locale.h>
#include <procfs.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <time.h>
#ifndef TEXT_DOMAIN
#define TEXT_DOMAIN "SUNW_OST_OSCMD"
#endif /* TEXT_DOMAIN */
#define LEGACY_UNKNOWN "unknown"
/* Flags for pg_get_single_val() */
#define EMPTY_OK 0x01
#define MULTI_OK 0x02
/*
* An AVL-storable node for output lines and the keys to sort them by.
*/
struct avl_string {
char *key;
char *str;
};
/*
* For lists of parsed restarter FMRIs.
*/
struct pfmri_list {
const char *scope;
const char *service;
const char *instance;
struct pfmri_list *next;
};
/*
* Globals
*/
scf_handle_t *h;
static scf_propertygroup_t *g_pg;
static scf_property_t *g_prop;
static scf_value_t *g_val;
static uu_avl_pool_t *lines_pool;
int exit_status;
static ssize_t max_scf_type_length;
static char *common_name_buf; /* Sized for maximal length value. */
char *locale; /* Current locale. */
/*
* Pathname storage for path generated from the fmri.
* Used for reading the ctid and (start) pid files for an inetd service.
*/
/* Options */
static int opt_cnum = 0;
static int opt_processes = 0; /* Print processes? */
static int opt_snum = 0;
static int opt_nstate_shown = 0; /* Will nstate be shown? */
static int opt_verbose = 0;
/* Minimize string constants. */
static const char * const scf_property_state = SCF_PROPERTY_STATE;
static const char * const scf_property_next_state = SCF_PROPERTY_NEXT_STATE;
static const char * const scf_property_contract = SCF_PROPERTY_CONTRACT;
/*
* Utility functions
*/
/*
* For unexpected libscf errors. The ending newline is necessary to keep
* uu_die() from appending the errno error.
*/
#ifndef NDEBUG
void
{
}
#else
void
scfdie(void)
{
scf_strerror(scf_error()));
}
#endif
void *
{
void *ptr;
return (ptr);
}
char *
safe_strdup(const char *str)
{
char *cp;
return (cp);
}
/*
* FMRI hashtable. For uniquifing listings.
*/
struct ht_elem {
const char *fmri;
};
static struct ht_elem **ht_buckets;
static uint_t ht_buckets_num;
static void
ht_init()
{
ht_buckets_num = 8;
ht_num = 0;
}
static uint_t
ht_hash_fmri(const char *fmri)
{
uint_t h = 0, g;
const char *p, *k;
/* All FMRIs begin with svc:/, so skip that part. */
/*
*/
for (p = k; *p != '\0'; ++p) {
h = (h << 4) + *p;
if ((g = (h & 0xf0000000)) != 0) {
h ^= (g >> 24);
h ^= g;
}
}
return (h);
}
static void
ht_grow()
{
struct ht_elem **new_ht_buckets;
int i;
for (i = 0; i < ht_buckets_num; ++i) {
uint_t h;
}
}
}
/*
* Add an FMRI to the hash table. Returns 1 if it was already there,
* 0 otherwise.
*/
static int
{
uint_t h;
h = ht_hash_fmri(fmri);
return (1);
}
/* Grow when average chain length is over 3. */
ht_grow();
++ht_num;
return (0);
}
/*
* Convenience libscf wrapper functions.
*/
/*
* Get the single value of the named property in the given property group,
* which must have type ty, and put it in *vp. If ty is SCF_TYPE_ASTRING, vp
* is taken to be a char **, and sz is the size of the buffer. sz is unused
* otherwise. Return 0 on success, -1 if the property doesn't exist, has the
* wrong type, or doesn't have a single value. If flags has EMPTY_OK, don't
* complain if the property has no values (but return nonzero). If flags has
* MULTI_OK and the property has multiple values, succeed with E2BIG.
*/
int
{
char *buf;
int ret = -1, r;
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
goto out;
}
if (scf_error() == SCF_ERROR_TYPE_MISMATCH)
goto misconfigured;
scfdie();
}
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
goto out;
goto misconfigured;
break;
}
goto misconfigured;
default:
scfdie();
}
}
switch (ty) {
case SCF_TYPE_ASTRING:
break;
case SCF_TYPE_BOOLEAN:
break;
case SCF_TYPE_COUNT:
break;
case SCF_TYPE_INTEGER:
break;
case SCF_TYPE_TIME: {
break;
}
case SCF_TYPE_USTRING:
break;
default:
#ifndef NDEBUG
#endif
abort();
}
if (r != SCF_SUCCESS)
scfdie();
goto out;
scfdie();
out:
return (ret);
}
static scf_snapshot_t *
{
snap = scf_snapshot_create(h);
scfdie();
return (snap);
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
return (NULL);
}
/*
* As pg_get_single_val(), except look the property group up in an
* instance. If "use_running" is set, and the running snapshot exists,
* do a composed lookup there. Otherwise, do an (optionally composed)
* lookup on the current values. Note that lookups using snapshots are
* always composed.
*/
int
int use_running, int composed)
{
int r;
if (use_running)
if (composed || use_running)
else
if (snap)
if (r == -1)
return (-1);
return (r);
}
static int
{
uint8_t b;
if (inst_get_single_val(inst,
SCF_TYPE_BOOLEAN, &b, 0, 0, 0, 0) != 0)
return (-1);
return (b ? 1 : 0);
}
/*
* Get a string property from the restarter property group of the given
* instance. Return an empty string on normal problems.
*/
static void
{
*buf = '\0';
}
static int
{
int r;
return (r == 0 ? 0 : -1);
}
static int
{
}
/*
* Generic functions
*/
/*
* Return an array of pids associated with the given contract id.
* Returned pids are added to the end of the pidsp array.
*/
static void
{
uint_t m;
int fd;
int r, err;
if (fd < 0)
return;
if (err != 0) {
return;
}
assert(r == 0);
if (m == 0) {
return;
}
*np += m;
}
static int
{
uint64_t c;
int r;
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
return (ENOENT);
}
scfdie();
if (ty != SCF_TYPE_COUNT)
return (EINVAL);
scfdie();
for (;;) {
if (r == -1)
scfdie();
if (r == 0)
break;
if (scf_value_get_count(val, &c) != 0)
scfdie();
}
return (0);
}
/*
* given string. Restarter string must be in canonified form.
* Returns 0 for success; -1 otherwise.
*/
static int
{
char *fmri_buf;
char *fmri_buf_canonified = NULL;
int ret = -1;
return (-1);
/* Get restarter */
goto out;
(max_scf_fmri_length + 1)) < 0)
goto out;
ret = 0;
out:
if (fmri_buf_canonified)
return (ret);
}
/*
* Common code that is used by ctids_by_restarter and pids_by_restarter.
* Checks for a common restarter and if one is available, it generates
* the appropriate filename using wip->fmri and stores that in the
* global genfmri_filename.
*
* If a restarter specific action is available, then restarter_spec
* is set to 1. If a restarter specific action is not available, then
* restarter_spec is set to 0 and a -1 is returned.
*
* Returns:
* 0 if success: restarter specific action found and filename generated
* -1 if restarter specific action not found,
* if restarter specific action found but an error was encountered
* during the generation of the wip->fmri based filename
*/
static int
int *restarter_specp)
{
int ret = -1;
int r;
/* Check for inetd specific restarter */
*restarter_specp = 0;
return (ret);
}
*restarter_specp = 1;
/* Get the ctid filename associated with this instance */
switch (r) {
case 0:
break;
case -1:
/*
* Unable to get filename from fmri. Print warning
* and return failure with no ctids.
*/
"FMRI is too long\n"), fmri);
return (ret);
case -2:
/*
* The directory didn't exist, so no contracts.
* Return failure with no ctids.
*/
return (ret);
default:
abort();
}
return (0);
}
/*
* Get or print a contract id using a restarter specific action.
*
* If the print_flag is not set, this routine gets the single contract
* id associated with this instance.
* If the print flag is set, then print each contract id found.
*
* Returns:
* 0 if success: restarter specific action found and used with no error
* -1 if restarter specific action not found
* -1 if restarter specific action found, but there was a failure
* -1 if print flag is not set and no contract id is found or multiple
* contract ids were found
* E2BIG if print flag is not set, MULTI_OK bit in flag is set and multiple
* contract ids were found
*/
static int
void (*callback_ctid)(uint64_t))
{
int ret = -1;
int fscanf_ret;
int rest_ret;
/* Check if callbacks are needed and were passed in */
if (print_flag) {
return (ret);
}
/* Check for restarter specific action and generation of filename */
if (rest_ret != 0)
return (rest_ret);
/*
* If fopen fails, then ctid file hasn't been created yet.
* If print_flag is set, this is ok; otherwise fail.
*/
if (print_flag)
return (0);
goto out;
}
if (print_flag) {
/*
* Print all contract ids that are found.
* First callback to print ctid header.
*/
/* fscanf may not set errno, so be sure to clear it first */
errno = 0;
/* Callback to print contract id */
callback_ctid(*cp);
errno = 0;
}
/* EOF is not a failure when no errno. */
}
(void) putchar('\n');
ret = 0;
} else {
/* Must find 1 ctid or fail */
/* If 2nd ctid found - fail */
} else {
/* Success - found only 1 ctid */
ret = 0;
}
}
}
out:
return (ret);
}
/*
* Get the process ids associated with an instance using a restarter
* specific action.
*
* Returns:
* 0 if success: restarter specific action found and used with no error
* -1 restarter specific action not found or if failure
*/
static int
{
uint64_t c;
int fscanf_ret;
int rest_ret;
/* Check for restarter specific action and generation of filename */
if (rest_ret != 0)
return (rest_ret);
/*
* If fopen fails with ENOENT then the ctid file hasn't been
* created yet so return success.
* For all other errors - fail with uu_die.
*/
return (0);
}
/* fscanf may not set errno, so be sure to clear it first */
errno = 0;
if (c == 0) {
fmri);
}
errno = 0;
}
/* EOF is not a failure when no errno. */
}
return (0);
}
static int
{
int ret;
int restarter_spec;
/* Use the restarter specific get pids routine, if available. */
if (restarter_spec == 1)
return (ret);
scfdie();
*np = 0;
ret = 0;
} else {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
ret = -1;
}
return (ret);
}
static int
{
char path[100];
int fd;
if (fd < 0)
return (-1);
return (0);
}
/*
* Column sprint and sortkey functions
*/
struct column {
const char *name;
int width;
/*
* This function should write the value for the column into buf, and
* grow or allocate buf accordingly. It should always write at least
* width bytes, blanking unused bytes with spaces. If the field is
* greater than the column width we allow it to overlap other columns.
* In particular, it shouldn't write any null bytes. (Though an extra
* null byte past the end is currently tolerated.) If the property
* group is non-NULL, then we are dealing with a legacy service.
*/
void (*sprint)(char **, scf_walkinfo_t *);
int sortkey_width;
/*
* This function should write sortkey_width bytes into buf which will
* cause memcmp() to sort it properly. (Unlike sprint() above,
* however, an extra null byte may overrun the buffer.) The second
* argument controls whether the results are sorted in forward or
* reverse order.
*/
void (*get_sortkey)(char *, int, scf_walkinfo_t *);
};
static void
{
int i;
for (i = 0; i < len; ++i)
}
/* CTID */
#define CTID_COLUMN_WIDTH 6
static void
{
int r;
uint64_t c;
int restarter_spec;
/*
* Use the restarter specific get pids routine, if available.
* Only check for non-legacy services (wip->pg == 0).
*/
} else {
if (restarter_spec == 0) {
/* No restarter specific routine */
}
}
if (r == 0)
else if (r == E2BIG)
else
if (*buf)
}
#define CTID_SORTKEY_WIDTH (sizeof (uint64_t))
static void
{
int r;
uint64_t c;
int restarter_spec;
/*
* Use the restarter specific get pids routine, if available.
* Only check for non-legacy services (wip->pg == 0).
*/
SCF_TYPE_COUNT, &c, 0, EMPTY_OK);
} else {
if (restarter_spec == 0) {
/* No restarter specific routine */
scf_property_contract, &c, EMPTY_OK);
}
}
if (r == 0) {
/*
* Use the id itself, but it must be big-endian for this to
* work.
*/
c = BE_64(c);
} else {
}
if (reverse)
}
/* DESC */
#define DESC_COLUMN_WIDTH 100
static void
{
char *x;
char *newbuf;
if (common_name_buf == NULL)
common_name_buf[0] = '-';
1, 1) == -1 &&
1, 1) == -1) {
common_name_buf[0] = '-';
}
/*
* Collapse multi-line tm_common_name values into a single line.
*/
for (x = common_name_buf; *x != '\0'; x++)
if (*x == '\n')
*x = ' ';
else
if (*buf)
}
/* ARGSUSED */
static void
{
}
/* State columns (STATE, NSTATE, S, N, SN, STA, NSTA) */
static char
state_to_char(const char *state)
{
return ('u');
return ('0');
return ('1');
return ('m');
return ('d');
return ('D');
return ('L');
return ('?');
}
/* Return true if inst is transitioning. */
static int
{
sizeof (nstate_name));
}
/* ARGSUSED */
static void
{
/*
* Lower numbers are printed first, so these are arranged from least
* interesting ("legacy run") to most interesting (unknown).
*/
sizeof (state_name));
*buf = 2;
*buf = 3;
*buf = 4;
*buf = 5;
*buf = 1;
*buf = 6;
else
*buf = 7;
} else
*buf = 0;
if (reverse)
}
static void
{
char *newbuf;
state_name, sizeof (state_name));
/* Don't print blank fields, to ease parsing. */
if (state_name[0] == '\0') {
state_name[0] = '-';
}
/* Append an asterisk if nstate is valid. */
}
} else
if (*buf)
}
static void
{
}
static void
{
char *newbuf;
next_state_name, sizeof (next_state_name));
/* Don't print blank fields, to ease parsing. */
if (next_state_name[0] == '\0' ||
blank = 1;
} else
blank = 1;
if (blank) {
next_state_name[0] = '-';
}
if (*buf)
}
static void
{
}
static void
{
char tmp[3];
state_name, sizeof (state_name));
else
} else {
tmp[0] = 'L';
}
3, tmp);
if (*buf)
}
static void
{
char tmp[2];
nstate_name, sizeof (nstate_name));
tmp[0] = '-';
else
} else
tmp[0] = '-';
2, tmp);
if (*buf)
}
static void
{
char tmp[3];
state_name, sizeof (state_name));
nstate_name, sizeof (nstate_name));
else
} else {
tmp[0] = 'L';
}
3, tmp);
if (*buf)
}
/* ARGSUSED */
static void
{
}
static const char *
state_abbrev(const char *state)
{
return ("UN");
return ("OFF");
return ("ON");
return ("MNT");
return ("DIS");
return ("DGD");
return ("LRC");
return ("?");
}
static void
{
char sta[5];
state_name, sizeof (state_name));
else
if (*buf)
}
static void
{
state_name, sizeof (state_name));
else
"-");
else
if (*buf)
}
/* FMRI */
#define FMRI_COLUMN_WIDTH 50
static void
{
char *newbuf;
scfdie();
} else {
sizeof (SCF_FMRI_LEGACY_PREFIX) - 1,
max_scf_fmri_length + 1 -
(sizeof (SCF_FMRI_LEGACY_PREFIX) - 1), 0) != 0)
}
else
if (*buf)
}
static void
{
if (reverse)
}
/* Component columns */
#define COMPONENT_COLUMN_WIDTH 20
static void
{
scfdie();
if (*buf)
}
static void
{
if (reverse)
}
static void
{
char *newbuf;
max_scf_name_length + 1) < 0)
scfdie();
} else {
}
else
if (*buf)
}
static void
{
if (reverse)
}
/* INST */
static void
{
max_scf_name_length + 1) < 0)
scfdie();
} else {
tmp[0] = '-';
}
if (*buf)
}
static void
{
if (reverse)
}
/* STIME */
#define STIME_COLUMN_WIDTH 8
#define FORMAT_TIME "%k:%M:%S"
#define FORMAT_DATE "%b_%d "
#define FORMAT_YEAR "%Y "
/*
* sprint_stime() will allocate a new buffer and snprintf the services's
* state timestamp. If the timestamp is unavailable for some reason
* a '-' is given instead.
*/
static void
{
int r;
SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0);
} else {
}
if (r != 0) {
/*
* There's something amiss with our service
* so we'll print a '-' for STIME.
*/
} else {
/* tv should be valid so we'll format it */
/*
* Print time if started within the past 24 hours, print date
* if within the past 12 months or, finally, print year if
* started greater than 12 months ago.
*/
} else {
}
}
if (*buf)
}
/* ARGSUSED */
static void
{
int r;
SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0);
else
if (r == 0) {
/* Stick it straight into the buffer. */
} else {
}
if (reverse)
}
/*
* Information about columns which can be displayed. If you add something,
* check MAX_COLUMN_NAME_LENGTH_STR & update description_of_column() below.
*/
1, sortkey_nstate },
1, sortkey_state },
};
#define MAX_COLUMN_NAME_LENGTH_STR "6"
/*
* Necessary thanks to gettext() & xgettext.
*/
static const char *
description_of_column(int c)
{
const char *s = NULL;
switch (c) {
case 0:
s = gettext("contract ID for service (see contract(4))");
break;
case 1:
s = gettext("human-readable description of the service");
break;
case 2:
s = gettext("Fault Managed Resource Identifier for service");
break;
case 3:
s = gettext("portion of the FMRI indicating service instance");
break;
case 4:
s = gettext("abbreviation for next state (if in transition)");
break;
case 5:
s = gettext("abbreviation for next state (if in transition)");
break;
case 6:
s = gettext("name for next state (if in transition)");
break;
case 7:
s = gettext("abbreviation for current state");
break;
case 8:
s = gettext("name for scope associated with service");
break;
case 9:
s = gettext("abbreviation for current state and next state");
break;
case 10:
s = gettext("portion of the FMRI representing service name");
break;
case 11:
s = gettext("abbreviation for current state");
break;
case 12:
s = gettext("name for current state");
break;
case 13:
s = gettext("time of last state change");
break;
}
return (s);
}
static void
{
"Usage: %1$s [-aHpv] [-o col[,col ... ]] [-R restarter] "
"[-sS col] [<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"
" %1$s -?\n"), progname);
if (do_exit)
}
static void
print_help(const char *progname)
{
int i;
"\t-a list all service instances rather than "
"only those that are enabled\n"
"\t-d list dependencies of the specified service(s)\n"
"\t-D list dependents of the specified service(s)\n"
"\t-H omit header line from output\n"
"\t-l list detailed information about the specified service(s)\n"
"\t-o list only the specified columns in the output\n"
"\t-p list process IDs and names associated with each service\n"
"\t-R list only those services with the specified restarter\n"
"\t-s sort output in ascending order by the specified column(s)\n"
"\t-S sort output in descending order by the specified column(s)\n"
"\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"
"\n\t"
"Services can be specified using an FMRI, abbreviation, or fnmatch(5)\n"
"\n"
"\t%1$s [opts] network/*mail\n"
"\t%1$s [opts] smtp:sendmail\n"
"\t%1$s [opts] smtp\n"
"\t%1$s [opts] sendmail\n"
"\n\t"
"Columns for output or sorting can be specified using these names:\n"
"\n"), progname);
for (i = 0; i < ncolumns; i++) {
}
}
/*
* A getsubopt()-like function which returns an index into the columns table.
* On success, *optionp is set to point to the next sub-option, or the
* terminating null if there are none.
*/
static int
getcolumnopt(char **optionp)
{
int i;
*cp = '\0';
for (i = 0; i < ncolumns; ++i) {
else
return (i);
}
}
return (-1);
}
static void
{
int i;
for (i = 0; i < opt_cnum; ++i) {
*cp++ = ' ';
}
/* Trim the trailing whitespace */
--cp;
while (*cp == ' ')
--cp;
}
/*
* Long listing (-l) functions.
*/
static int
pidcmp(const void *l, const void *r)
{
return (-1);
return (1);
return (0);
}
/*
* This is the strlen() of the longest label ("description"), plus intercolumn
* space.
*/
/*
* Callback routine to print header for contract id.
* Called by ctids_by_restarter and print_detailed.
*/
static void
{
}
/*
* Callback routine to print a contract id.
* Called by ctids_by_restarter and print_detailed.
*/
static void
{
}
static void
{
uint64_t c;
uint_t i, n;
EMPTY_OK) != 0)
return;
return;
for (i = 0; i < n; ++i) {
pids[i]);
(void) putchar('\n');
}
}
/*
* Determines the state of a dependency. If the FMRI specifies a file, then we
* fake up a state based on whether we can access the file.
*/
static void
{
char *lfmri;
/*
* Check for file:// dependencies
*/
const char *msg;
msg = "online";
msg = "absent";
else
msg = "unknown";
return;
}
/*
* scf_parse_file_fmri() may have overwritten part of the string, so
* copy it back.
*/
return;
}
return;
}
/* instance: get state */
inst = scf_instance_create(h);
scfdie();
else {
switch (scf_error()) {
break;
case SCF_ERROR_NOT_FOUND:
break;
default:
scfdie();
}
}
return;
}
/*
* service: If only one instance, use that state. Otherwise, say
* "multiple".
*/
scfdie();
SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
switch (scf_error()) {
goto out;
case SCF_ERROR_NOT_FOUND:
goto out;
default:
scfdie();
}
}
scfdie();
case 0:
goto out;
case 1:
break;
default:
scfdie();
}
/* Get the state in case this is the only instance. */
case 0:
break;
case 1:
/* Nope, multiple instances. */
goto out;
default:
scfdie();
}
out:
}
static void
{
int i, j, k;
scfdie();
SCF_PG_APP_DEFAULT) == -1)
scfdie();
/*
* Format for output:
* pg (pgtype)
* description
* description
*/
int tmpl = 0;
scfdie();
max_scf_name_length) < 0)
scfdie();
SCF_PG_APP_DEFAULT, pt, 0) == 0)
tmpl = 1;
else
tmpl = 0;
}
scfdie();
max_scf_name_length) < 0)
scfdie();
scfdie();
if ((tmpl == 1) &&
0) != 0))
tmpl = 0;
if (tmpl == 1 &&
continue;
scfdie();
max_scf_value_length + 1) < 0)
scfdie();
(void) printf("\"");
(void) putc('\\',
stdout);
}
(void) printf("\"");
} else {
}
}
(void) printf("\n");
if (k == -1)
scfdie();
&desc) > 0) {
}
}
if (j == -1)
scfdie();
}
if (i == -1)
scfdie();
}
static void
{
char *val_buf;
int i;
scfdie();
SCF_SUCCESS ||
ty != SCF_TYPE_FMRI)
return;
/* Print the grouping */
else
(void) putchar('?');
(void) putchar('/');
else
(void) putchar('?');
/* Print the dependency entities. */
scfdie();
char state[MAX_SCF_STATE_STRING_SZ];
max_scf_value_length + 1) < 0)
scfdie();
(void) putchar(' ');
/* Print the state. */
state[0] = '-';
}
if (i == -1)
scfdie();
(void) putchar('\n');
}
/* ARGSUSED */
static int
{
char *buf;
char *timebuf;
int ret;
uint64_t c;
int restarter_spec;
int restarter_ret;
const char * const fmt = "%-*s%s\n";
rpg = scf_pg_create(h);
scfdie();
if (first_paragraph)
first_paragraph = 0;
else
(void) putchar('\n');
if (common_name_buf == NULL)
== 0)
== 0)
/*
* Synthesize an 'enabled' property that hides the enabled_ovr
* implementation from the user. If the service has been temporarily
* set to a state other than its permanent value, alert the user with
* a '(temporary)' message.
*/
if (temp != -1) {
else
gettext("false"));
} else if (perm != -1) {
}
/*
* Property values may be longer than max_scf_fmri_length, but these
* shouldn't be, so we'll just reuse buf. The user can use svcprop if
* he suspects something fishy.
*/
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
}
if (rpg) {
buf);
break;
}
gettext("state_time"),
timebuf);
}
buf);
}
else
/*
* Use the restarter specific routine to print the ctids, if available.
* If restarter specific action is available and it fails, then die.
*/
if (restarter_spec == 1) {
if (restarter_ret != 0)
goto restarter_common;
}
if (rpg) {
scfdie();
0) {
/* Callback to print ctid header */
scfdie();
for (;;) {
if (ret == -1)
scfdie();
if (ret == 0)
break;
if (scf_value_get_count(g_val, &c) != 0)
scfdie();
/* Callback to print contract id. */
}
(void) putchar('\n');
} else {
if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
scfdie();
}
} else {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
}
} else {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
}
/* Dependencies. */
scfdie();
scfdie();
if (ret == -1)
scfdie();
if (opt_processes)
/* "application" type property groups */
if (opt_verbose == 1)
return (0);
}
/*
* Append a one-lined description of each process in inst's contract(s) and
* return the augmented string.
*/
static char *
{
uint_t i, n = 0;
return (line);
} else {
/* Legacy services */
scfdie();
}
if (n == 0)
return (line);
for (i = 0; i < n; ++i) {
continue;
/*
* Print time if started within the past 24 hours, print date
* if within the past 12 months, print year if started greater
* than 12 months ago.
*/
else
}
return (line);
}
/*ARGSUSED*/
static int
{
struct avl_string *lp;
char *cp;
int i;
/*
* If the user has specified a restarter, check for a match first
*/
if (restarters != NULL) {
struct pfmri_list *rest;
int match;
char *restarter_fmri;
/* legacy services don't have restarters */
return (0);
return (0);
}
match = 0;
match = 1;
}
if (!match)
return (0);
}
/* It was already there. */
return (0);
}
for (i = 0; i < opt_cnum; ++i) {
}
cp--;
while (*cp == ' ')
cp--;
/* If we're supposed to list the processes, too, do that now. */
if (opt_processes)
/* Create the sort key. */
for (i = 0; i < opt_snum; ++i) {
int j = opt_sort[i] & 0xff;
}
/* Insert into AVL tree. */
return (0);
}
static int
{
return (0);
}
/*
* Service FMRI selection: Lookup and call list_instance() for the instances.
* Instance FMRI selection: Lookup and call list_instance().
*
* Note: This is shoehorned into a walk_dependencies() callback prototype so
* it can be used in list_dependencies.
*/
static int
{
char *fmri;
int ret;
NULL) != SCF_SUCCESS) {
if (complain)
return (0);
}
/*
* Yes, this invalidates *_name, but we only care whether they're NULL
* or not.
*/
if (complain)
return (0);
}
/* instance */
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
if (complain)
"Instance \"%s\" does not exist.\n"),
return (0);
}
}
/* service: Walk the instances. */
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
if (complain)
return (0);
}
iter = scf_iter_create(h);
scfdie();
scfdie();
return (0);
}
max_scf_fmri_length + 1) <= 0)
scfdie();
}
if (ret == -1)
scfdie();
return (0);
}
/*
* Dependency selection: Straightforward since each instance lists the
* services it depends on.
*/
static void
{
char *dep;
scfdie();
scfdie();
/* Ignore exclude_any dependencies. */
SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
continue;
}
scfdie();
if (ty != SCF_TYPE_ASTRING)
continue;
if (scf_error() != SCF_ERROR_CONSTRAINT_VIOLATED)
scfdie();
continue;
}
max_scf_value_length + 1) < 0)
scfdie();
continue;
SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
continue;
}
scfdie();
max_scf_value_length + 1) < 0)
scfdie();
goto out;
}
if (vret == -1)
scfdie();
}
if (ret == -1)
scfdie();
out:
}
static int
{
return (0);
}
/*
* Dependent selection: The "providing" service's or instance's FMRI is parsed
* into the provider_* variables, the instances are walked, and any instance
* which lists an FMRI which parses to these components is selected. This is
* inefficient in the face of multiple operands, but that should be uncommon.
*/
static char *provider_scope;
static char *provider_svc;
static char *provider_inst; /* NULL for services */
/*ARGSUSED*/
static int
{
char *cfmri;
return (0);
}
return (0);
}
/*
* If the user has specified an instance, then also match dependencies
* on the service itself.
*/
/* Stop on matches. */
return (*matchp);
}
static int
{
/* Only proceed if this instance depends on provider_*. */
int match = 0;
if (match)
return (0);
}
/*ARGSUSED*/
static int
{
char *save;
int ret;
max_scf_fmri_length) <= 0 ||
max_scf_fmri_length) <= 0)
scfdie();
max_scf_fmri_length) <= 0)
scfdie();
uu_warn);
return (ret);
}
/*
* main() & helpers
*/
static void
{
int i;
++opt_snum;
for (i = 0; i < ncolumns; ++i) {
break;
}
if (i < ncolumns)
else
}
static void
add_restarter(const char *fmri)
{
char *cfmri;
const char *pg_name;
struct pfmri_list *rest;
"instance.\n"), fmri);
restarters = rest;
return;
err:
}
/* ARGSUSED */
static int
{
const struct avl_string *l = l_arg;
const struct avl_string *r = r_arg;
}
/* ARGSUSED */
static int
print_line(void *e, void *private)
{
struct avl_string *lp = e;
return (UU_WALK_NEXT);
}
int
{
int i, n;
char *columns_str = NULL;
char *cp;
const char *progname;
int err;
int show_all = 0;
int show_header = 1;
const char * const options = "aHpvo:R:s:S:dDl?x";
if (locale) {
}
(void) textdomain(TEXT_DOMAIN);
scfdie();
/*
* opt_mode is the mode of operation. 0 for plain, 'd' for
* dependencies, 'D' for dependents, and 'l' for detailed (long). We
* need to know now so we know which options are valid.
*/
opt_mode = 0;
switch (opt) {
case '?':
if (optopt == '?') {
return (UU_EXIT_OK);
} else {
/* NOTREACHED */
}
case 'd':
case 'D':
case 'l':
if (opt_mode != 0)
break;
case 'x':
if (opt_mode != 0)
break;
default:
break;
}
}
sortkey_sz = 0;
switch (opt) {
case 'a':
if (opt_mode != 0)
show_all = 1;
break;
case 'H':
show_header = 0;
break;
case 'p':
if (opt_mode == 'x')
opt_processes = 1;
break;
case 'v':
opt_verbose = 1;
break;
case 'o':
break;
case 'R':
break;
case 's':
case 'S':
if (opt_mode != 0)
break;
case 'd':
case 'D':
case 'l':
case 'x':
break;
case '?':
/* NOTREACHED */
default:
assert(0);
abort();
}
}
/*
* -a is only meaningful when given no arguments
*/
h = scf_handle_create(SCF_VERSION);
if (h == NULL)
scfdie();
if (scf_handle_bind(h) == -1)
scfdie();
/*
* If we're in long mode, take care of it now before we deal with the
* sorting and the columns, since we won't use them anyway.
*/
if (opt_mode == 'l') {
if (argc == 0)
}
return (exit_status);
}
if (opt_mode == 'x') {
return (exit_status);
}
if (opt_snum == 0) {
/* Default sort. */
add_sort_column("state", 0);
add_sort_column("stime", 0);
add_sort_column("fmri", 0);
}
if (columns_str == NULL) {
if (!opt_verbose)
else
safe_strdup("state,nstate,stime,ctid,fmri");
}
/* Decode columns_str into opt_columns. */
line_sz = 0;
opt_cnum = 1;
if (*cp == ',')
++opt_cnum;
if (opt_columns == NULL)
for (n = 0; *columns_str != '\0'; ++n) {
i = getcolumnopt(&columns_str);
if (i == -1)
opt_nstate_shown = 1;
opt_columns[n] = i;
}
uu_strerror(uu_error()));
switch (opt_mode) {
case 0:
ht_init();
/* Always show all FMRIs when given arguments or restarters */
show_all = 1;
&exit_status, uu_warn)) != 0) {
}
break;
case 'd':
if (argc == 0)
&exit_status, uu_warn)) != 0) {
}
break;
case 'D':
if (argc == 0)
}
break;
default:
assert(0);
abort();
}
if (show_header)
print_header();
return (exit_status);
}