/*
* 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 (c) 2011, Joyent, Inc. All rights reserved.
* Copyright (c) 2015 by Delphix. All rights reserved.
*/
/*
* 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"
#include "notify_params.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 <libnvpair.h>
#include <locale.h>
#include <procfs.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <time.h>
#include <libzonecfg.h>
#include <zone.h>
#ifndef TEXT_DOMAIN
#endif /* TEXT_DOMAIN */
/* Flags for pg_get_single_val() */
/*
* 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;
};
/*
* Globals
*/
scf_handle_t *h;
int exit_status;
/*
* 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_snum = 0;
static int opt_verbose = 0;
/* Minimize string constants. */
/*
* 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 *
{
char *cp;
return (cp);
}
/*
* FMRI hashtable. For uniquifing listings.
*/
struct ht_elem {
const char *fmri;
};
static void
ht_free(void)
{
int i;
for (i = 0; i < ht_buckets_num; i++) {
}
}
ht_buckets_num = 0;
ht_buckets = NULL;
}
static void
ht_init(void)
{
ht_buckets_num = 8;
ht_num = 0;
}
static uint_t
{
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()
{
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
{
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);
/*
* 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';
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;
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 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 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
{
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.
*/
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.
*/
};
static void
{
int i;
for (i = 0; i < len; ++i)
}
/* CTID */
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)
}
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 */
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
{
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
{
state_name, sizeof (state_name));
else
} else {
tmp[0] = 'L';
}
3, tmp);
if (*buf)
}
static void
{
nstate_name, sizeof (nstate_name));
tmp[0] = '-';
else
} else
tmp[0] = '-';
2, tmp);
if (*buf)
}
static void
{
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 *
{
return ("UN");
return ("OFF");
return ("ON");
return ("MNT");
return ("DIS");
return ("DGD");
return ("LRC");
return ("?");
}
static void
{
state_name, sizeof (state_name));
else
if (*buf)
}
static void
{
state_name, sizeof (state_name));
else
"-");
else
if (*buf)
}
/* FMRI */
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 */
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 */
/*
* 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)
}
/* ZONE */
/*ARGSUSED*/
static void
{
if (getzonenamebyid(zoneid, b, sizeof (b)) < 0)
zonename = b;
}
else
if (*buf)
}
static void
{
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 },
};
/*
* 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;
case 14:
s = gettext("name of zone");
break;
}
return (s);
}
static void
{
"Usage: %1$s [-aHpv] [-o col[,col ... ]] [-R restarter] "
"[-sS col] [-Z | -z zone ]\n [<service> ...]\n"
" %1$s -d | -D [-Hpv] [-o col[,col ... ]] [-sS col] "
"[-Z | -z zone ]\n [<service> ...]\n"
" %1$s [-l | -L] [-Z | -z zone] <service> ...\n"
" %1$s -x [-v] [-Z | -z zone] [<service> ...]\n"
" %1$s -?\n"), progname);
if (do_exit)
}
static void
{
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-L list the log file associated with 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"
"\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"
"\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
{
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();
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;
rpg = scf_pg_create(h);
scfdie();
if (first_paragraph)
first_paragraph = 0;
else
(void) putchar('\n');
if (common_name_buf == NULL)
== 0)
== 0)
if (g_zonename != NULL)
/*
* 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);
}
/* ARGSUSED */
static int
{
scfdie();
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
goto out;
}
}
out:
return (0);
}
int
{
}
/*
* get_notify_param_classes()
* return the fma classes that don't have a tag in fma_tags[], otherwise NULL
*/
static char **
{
int n = 0;
int err;
scf_strerror(scf_error()));
}
scf_strerror(scf_error()));
scf_strerror(scf_error()));
SCF_NOTIFY_PARAMS_PG_TYPE) != 0)
scf_strerror(scf_error()));
char *c;
scf_strerror(scf_error()));
*c = '\0';
if (has_fma_tag(pgname))
continue;
if (!is_fma_token(pgname))
/*
* We don't emmit a warning here so that we don't
* pollute the output
*/
continue;
if (n + 1 >= size) {
size *= 2;
}
++n;
}
/*
* NULL terminate buf
*/
if (err == -1)
scf_strerror(scf_error()));
/* sort the classes */
return (buf);
}
/*
* get_fma_notify_params()
* populates an nvlist_t with notifycation parameters for a given FMA class
* returns 0 if the nvlist is populated, 1 otherwise;
*/
int
{
/*
* if the preferences have just been deleted
* or does not exist, just skip.
*/
if (scf_error() != SCF_ERROR_NOT_FOUND &&
scf_error() != SCF_ERROR_DELETED)
"Failed get_fma_notify_params %s\n"),
scf_strerror(scf_error()));
return (1);
}
return (0);
}
/*
* print_notify_fma()
* outputs the notification paramets of FMA events.
* It first outputs classes in fma_tags[], then outputs the other classes
* sorted alphabetically
*/
static void
print_notify_fma(void)
{
char **classes, *p;
const char *class;
uint32_t i;
}
goto cleanup;
if (get_fma_notify_params(nvl, p) == 0)
free(p);
}
}
/*
* print_notify_fmri()
* prints notifycation parameters for an SMF instance.
*/
static void
{
SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_NOT_FOUND &&
scf_error() != SCF_ERROR_DELETED)
"Failed _scf_get_svc_notify_params: %s\n"),
scf_strerror(scf_error()));
} else {
gettext("System wide notification parameters:\n"));
}
}
/*
* print_notify_special()
* prints notification parameters for FMA events and system wide SMF state
* transitions parameters
*/
static void
{
safe_printf("Notification parameters for FMA Events\n");
}
/*
* print_notify()
* callback function to print notification parameters for SMF state transition
* instances. It skips global and notify-params instances as they should be
* printed by print_notify_special()
*/
/* ARGSUSED */
static int
{
return (0);
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
{
char *cp;
int i;
/*
* If the user has specified a restarter, check for a match first
*/
if (restarters != NULL) {
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;
/*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
{
char *cfmri;
const char *pg_name;
"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
{
return (UU_WALK_NEXT);
}
/* ARGSUSED */
static void
{}
int
{
int i, n;
char *cp;
const char *progname;
void (*errfunc)(const char *, ...);
int show_all = 0;
int show_zones = 0;
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':
case 'L':
if (opt_mode != 0)
break;
case 'n':
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 'L':
case 'n':
case 'x':
break;
case 'z':
if (getzoneid() != GLOBAL_ZONEID)
"the global zone\n"));
if (show_zones)
break;
case 'Z':
if (getzoneid() != GLOBAL_ZONEID)
"the global zone\n"));
show_zones = 1;
break;
case '?':
/* NOTREACHED */
default:
assert(0);
abort();
}
}
/*
* -a is only meaningful when given no arguments
*/
while (show_zones) {
"%d zone IDs"), nzents);
}
/*
* 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.
*/
break;
}
h = scf_handle_create(SCF_VERSION);
if (h == NULL)
scfdie();
goto nextzone;
}
} else {
}
scfdie();
scfdie();
}
if (scf_handle_bind(h) == -1) {
if (g_zonename != NULL) {
"server for zone %s: %s\n"), g_zonename,
scf_strerror(scf_error()));
if (!show_zones)
return (UU_EXIT_FATAL);
goto nextzone;
}
}
scfdie();
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...
*/
missing = 0;
} else {
errarg = &exit_status;
}
/*
* 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)
}
goto nextzone;
}
if (opt_mode == 'L') {
}
goto nextzone;
}
if (opt_mode == 'n') {
}
goto nextzone;
}
if (opt_mode == 'x') {
goto nextzone;
}
if (columns_str == NULL) {
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) {
"zone,state,stime,fmri" : "state,stime,fmri");
} else {
"zone,state,nstate,stime,ctid,fmri" :
"state,nstate,stime,ctid,fmri");
}
}
if (opt_columns == NULL) {
/* Decode columns_str into opt_columns. */
line_sz = 0;
opt_cnum = 1;
if (*cp == ',')
++opt_cnum;
if (*columns_str == '\0')
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:
/*
* 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 */
show_all = 1;
}
break;
case 'd':
if (argc == 0)
}
break;
case 'D':
if (argc == 0)
}
break;
case 'n':
break;
default:
assert(0);
abort();
}
goto again;
}
if (show_zones && exit_status == 0)
if (opt_columns == NULL)
return (exit_status);
if (show_header)
print_header();
return (exit_status);
}