/*
* 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
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <libintl.h>
#include <alloca.h>
#include <getopt.h>
#include <libhotplug.h>
#include <sys/types.h>
#include <sys/sunddi.h>
#include <sys/ddi_hp.h>
#if !defined(TEXT_DOMAIN) /* should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
#endif
/*
* Function prototypes.
*/
static int cmd_list(int, char **, const char *);
static int cmd_online(int, char **, const char *);
static int cmd_offline(int, char **, const char *);
static int cmd_enable(int, char **, const char *);
static int cmd_disable(int, char **, const char *);
static int cmd_poweron(int, char **, const char *);
static int cmd_poweroff(int, char **, const char *);
static int cmd_getpriv(int, char **, const char *);
static int cmd_setpriv(int, char **, const char *);
static int cmd_changestate(int, char **, const char *);
static void parse_common(int, char **, const char *);
static void parse_flags(int, char **, int *, const char *);
static void parse_target(int, char **, char **, char **, const char *);
static void parse_options(int, char **, char **, const char *);
static void bad_option(int, int, const char *);
static void usage(const char *);
static int list_cb(hp_node_t, void *);
static int list_long_cb(hp_node_t, void *);
static int error_cb(hp_node_t, void *);
static void print_options(const char *);
static void print_error(int);
static int state_atoi(char *);
static char *state_itoa(int);
static short valid_target(int);
/*
* Define a conversion table for hotplug states.
*/
typedef struct {
int state;
char *state_str;
short valid_target;
} hpstate_t;
static hpstate_t hpstates[] = {
{ DDI_HP_CN_STATE_EMPTY, "EMPTY", 0 },
{ DDI_HP_CN_STATE_PRESENT, "PRESENT", 1 },
{ DDI_HP_CN_STATE_POWERED, "POWERED", 1 },
{ DDI_HP_CN_STATE_ENABLED, "ENABLED", 1 },
{ DDI_HP_CN_STATE_PORT_EMPTY, "PORT-EMPTY", 0 },
{ DDI_HP_CN_STATE_PORT_PRESENT, "PORT-PRESENT", 1 },
{ DDI_HP_CN_STATE_OFFLINE, "OFFLINE", 1 },
{ DDI_HP_CN_STATE_ATTACHED, "ATTACHED", 0 },
{ DDI_HP_CN_STATE_MAINTENANCE, "MAINTENANCE", 0 },
{ DDI_HP_CN_STATE_ONLINE, "ONLINE", 1 },
{ 0, 0, 0 }
};
/*
* Define tables of supported subcommands.
*/
typedef struct {
char *usage_str;
char *cmd_str;
int (*func)(int argc, char *argv[], const char *usage_str);
} subcmd_t;
static subcmd_t subcmds[] = {
{ "list [-l] [-v] [<path> [<connection>]]", "list", cmd_list },
{ "online <path> <port>", "online", cmd_online },
{ "offline [-f] [-q] <path> <port>", "offline", cmd_offline },
{ "enable <path> <connector>", "enable", cmd_enable },
{ "disable [-f] [-q] <path> <connector>", "disable", cmd_disable },
{ "poweron <path> <connector>", "poweron", cmd_poweron },
{ "poweroff [-f] [-q] <path> <connector>", "poweroff", cmd_poweroff },
{ "get -o <options> <path> <connector>", "get", cmd_getpriv },
{ "set -o <options> <path> <connector>", "set", cmd_setpriv }
};
static subcmd_t hidden_subcmds[] = {
{ "changestate [-f] [-q] -s <state> <path> <connection>",
"changestate", cmd_changestate }
};
/*
* Define tables of command line options.
*/
static const struct option common_opts[] = {
{ "help", no_argument, 0, '?' },
{ "version", no_argument, 0, 'V' },
{ 0, 0, 0, 0 }
};
static const struct option list_opts[] = {
{ "list-path", no_argument, 0, 'l' },
{ "verbose", no_argument, 0, 'v' },
{ 0, 0, 0, 0 }
};
static const struct option flag_opts[] = {
{ "force", no_argument, 0, 'f' },
{ "query", no_argument, 0, 'q' },
{ 0, 0, 0, 0 }
};
static const struct option private_opts[] = {
{ "options", required_argument, 0, 'o' },
{ 0, 0, 0, 0 }
};
static const struct option changestate_opts[] = {
{ "force", no_argument, 0, 'f' },
{ "query", no_argument, 0, 'q' },
{ "state", required_argument, 0, 's' },
{ 0, 0, 0, 0 }
};
/*
* Define exit codes.
*/
#define EXIT_OK 0
#define EXIT_EINVAL 1 /* invalid arguments */
#define EXIT_ENOENT 2 /* path or connection doesn't exist */
#define EXIT_FAILED 3 /* operation failed */
#define EXIT_UNAVAIL 4 /* service not available */
/*
* Global variables.
*/
static char *prog;
static char version[] = "1.0";
extern int errno;
/*
* main()
*
* The main routine determines which subcommand is used,
* and dispatches control to the corresponding function.
*/
int
main(int argc, char *argv[])
{
int i, rv;
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
if ((prog = strrchr(argv[0], '/')) == NULL)
prog = argv[0];
else
prog++;
if (argc < 2) {
usage(NULL);
return (EXIT_EINVAL);
}
parse_common(argc, argv, NULL);
/* Check the list of defined subcommands. */
for (i = 0; i < (sizeof (subcmds) / sizeof (subcmd_t)); i++) {
if (strcmp(argv[1], subcmds[i].cmd_str) == 0) {
rv = subcmds[i].func(argc - 1, &argv[1],
subcmds[i].usage_str);
goto finished;
}
}
/* Check the list of hidden subcommands. */
for (i = 0; i < (sizeof (hidden_subcmds) / sizeof (subcmd_t)); i++) {
if (strcmp(argv[1], hidden_subcmds[i].cmd_str) == 0) {
rv = hidden_subcmds[i].func(argc - 1, &argv[1],
hidden_subcmds[i].usage_str);
goto finished;
}
}
/* No matching subcommand found. */
(void) fprintf(stderr, gettext("ERROR: %s: unknown subcommand '%s'\n"),
prog, argv[1]);
usage(NULL);
exit(EXIT_EINVAL);
finished:
/* Determine exit code */
switch (rv) {
case 0:
break;
case EINVAL:
return (EXIT_EINVAL);
case ENXIO:
case ENOENT:
return (EXIT_ENOENT);
case EBADF:
return (EXIT_UNAVAIL);
default:
return (EXIT_FAILED);
}
return (EXIT_OK);
}
/*
* cmd_list()
*
* Subcommand to list hotplug information.
*/
static int
cmd_list(int argc, char *argv[], const char *usage_str)
{
hp_node_t root;
char *path = NULL;
char *connection = NULL;
boolean_t long_flag = B_FALSE;
int flags = 0;
int opt;
/* Parse command line options */
parse_common(argc, argv, usage_str);
while ((opt = getopt_clip(argc, argv, "lv", list_opts, NULL)) != -1) {
switch (opt) {
case 'l':
long_flag = B_TRUE;
break;
case 'v':
flags |= HPINFOUSAGE;
break;
default:
bad_option(opt, optopt, usage_str);
break;
}
}
parse_target(argc, argv, &path, &connection, usage_str);
/* Default path is "/" */
if (path == NULL)
path = "/";
/* Get hotplug information snapshot */
if ((root = hp_init(path, connection, flags)) == NULL) {
print_error(errno);
return (errno);
}
/* Display hotplug information */
(void) hp_traverse(root, NULL, long_flag ? list_long_cb : list_cb);
/* Discard hotplug information snapshot */
hp_fini(root);
return (0);
}
/*
* cmd_online()
*
* Subcommand to online a hotplug port.
*/
static int
cmd_online(int argc, char *argv[], const char *usage_str)
{
hp_node_t root;
hp_node_t results = NULL;
char *path = NULL;
char *connection = NULL;
int rv;
/* Parse command line options */
parse_common(argc, argv, usage_str);
parse_target(argc, argv, &path, &connection, usage_str);
/* Path and connection are required */
if ((path == NULL) || (connection == NULL)) {
(void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
usage(usage_str);
return (EINVAL);
}
/* Get hotplug information snapshot */
if ((root = hp_init(path, connection, 0)) == NULL) {
print_error(errno);
return (errno);
}
/* Verify target is a port */
if (hp_type(root) != HP_NODE_PORT) {
(void) fprintf(stderr,
gettext("ERROR: invalid target (must be a port).\n"));
hp_fini(root);
return (EINVAL);
}
/* Do state change */
rv = hp_set_state(root, 0, DDI_HP_CN_STATE_ONLINE, &results);
/* Display results */
if (rv == EIO) {
(void) fprintf(stderr, gettext("ERROR: failed to attach device "
"drivers or other internal errors.\n"));
} else if (rv != 0) {
print_error(rv);
}
if (results != NULL) {
(void) hp_traverse(results, NULL, error_cb);
hp_fini(results);
}
/* Discard hotplug information snapshot */
hp_fini(root);
return (rv);
}
/*
* cmd_offline()
*
* Subcommand to offline a hotplug port.
*/
static int
cmd_offline(int argc, char *argv[], const char *usage_str)
{
hp_node_t root;
hp_node_t results = NULL;
char *path = NULL;
char *connection = NULL;
int flags = 0;
int rv;
/* Parse command line options */
parse_common(argc, argv, usage_str);
parse_flags(argc, argv, &flags, usage_str);
parse_target(argc, argv, &path, &connection, usage_str);
/* Path and connection are required */
if ((path == NULL) || (connection == NULL)) {
(void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
usage(usage_str);
return (EINVAL);
}
/* Get hotplug information snapshot */
if ((root = hp_init(path, connection, 0)) == NULL) {
print_error(errno);
return (errno);
}
/* Verify target is a port */
if (hp_type(root) != HP_NODE_PORT) {
(void) fprintf(stderr,
gettext("ERROR: invalid target (must be a port).\n"));
hp_fini(root);
return (EINVAL);
}
/* Do state change */
rv = hp_set_state(root, flags, DDI_HP_CN_STATE_OFFLINE, &results);
/* Display results */
print_error(rv);
if (results != NULL) {
(void) hp_traverse(results, NULL, error_cb);
hp_fini(results);
}
/* Discard hotplug information snapshot */
hp_fini(root);
return (rv);
}
/*
* cmd_enable()
*
* Subcommand to enable a hotplug connector.
*/
static int
cmd_enable(int argc, char *argv[], const char *usage_str)
{
hp_node_t root;
hp_node_t results = NULL;
char *path = NULL;
char *connection = NULL;
int rv;
/* Parse command line options */
parse_common(argc, argv, usage_str);
parse_target(argc, argv, &path, &connection, usage_str);
/* Path and connection are required */
if ((path == NULL) || (connection == NULL)) {
(void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
usage(usage_str);
return (EINVAL);
}
/* Get hotplug information snapshot */
if ((root = hp_init(path, connection, 0)) == NULL) {
print_error(errno);
return (errno);
}
/* Verify target is a connector */
if (hp_type(root) != HP_NODE_CONNECTOR) {
(void) fprintf(stderr,
gettext("ERROR: invalid target (must be a connector).\n"));
hp_fini(root);
return (EINVAL);
}
/* Do state change */
rv = hp_set_state(root, 0, DDI_HP_CN_STATE_ENABLED, &results);
/* Display results */
print_error(rv);
if (results != NULL) {
(void) hp_traverse(results, NULL, error_cb);
hp_fini(results);
}
/* Discard hotplug information snapshot */
hp_fini(root);
return (rv);
}
/*
* cmd_disable()
*
* Subcommand to disable a hotplug connector.
*/
static int
cmd_disable(int argc, char *argv[], const char *usage_str)
{
hp_node_t root;
hp_node_t results = NULL;
char *path = NULL;
char *connection = NULL;
int flags = 0;
int rv;
/* Parse command line options */
parse_common(argc, argv, usage_str);
parse_flags(argc, argv, &flags, usage_str);
parse_target(argc, argv, &path, &connection, usage_str);
/* Path and connection are required */
if ((path == NULL) || (connection == NULL)) {
(void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
usage(usage_str);
return (EINVAL);
}
/* Get hotplug information snapshot */
if ((root = hp_init(path, connection, 0)) == NULL) {
print_error(errno);
return (errno);
}
/* Verify target is a connector */
if (hp_type(root) != HP_NODE_CONNECTOR) {
(void) fprintf(stderr,
gettext("ERROR: invalid target (must be a connector).\n"));
hp_fini(root);
return (EINVAL);
}
/*
* Do nothing unless the connector is in the ENABLED state.
* Otherwise this subcommand becomes an alias for 'poweron.'
*/
if (hp_state(root) != DDI_HP_CN_STATE_ENABLED) {
hp_fini(root);
return (0);
}
/* Do state change */
rv = hp_set_state(root, flags, DDI_HP_CN_STATE_POWERED, &results);
/* Display results */
print_error(rv);
if (results != NULL) {
(void) hp_traverse(results, NULL, error_cb);
hp_fini(results);
}
/* Discard hotplug information snapshot */
hp_fini(root);
return (rv);
}
/*
* cmd_poweron()
*
* Subcommand to power on a hotplug connector.
*/
static int
cmd_poweron(int argc, char *argv[], const char *usage_str)
{
hp_node_t root;
hp_node_t results = NULL;
char *path = NULL;
char *connection = NULL;
int rv;
/* Parse command line options */
parse_common(argc, argv, usage_str);
parse_target(argc, argv, &path, &connection, usage_str);
/* Path and connection are required */
if ((path == NULL) || (connection == NULL)) {
(void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
usage(usage_str);
return (EINVAL);
}
/* Get hotplug information snapshot */
if ((root = hp_init(path, connection, 0)) == NULL) {
print_error(errno);
return (errno);
}
/* Verify target is a connector */
if (hp_type(root) != HP_NODE_CONNECTOR) {
(void) fprintf(stderr,
gettext("ERROR: invalid target (must be a connector).\n"));
hp_fini(root);
return (EINVAL);
}
/*
* Do nothing if the connector is already powered.
* Otherwise this subcommand becomes an alias for 'disable.'
*/
if (hp_state(root) >= DDI_HP_CN_STATE_POWERED) {
hp_fini(root);
return (0);
}
/* Do state change */
rv = hp_set_state(root, 0, DDI_HP_CN_STATE_POWERED, &results);
/* Display results */
print_error(rv);
if (results != NULL) {
(void) hp_traverse(results, NULL, error_cb);
hp_fini(results);
}
/* Discard hotplug information snapshot */
hp_fini(root);
return (rv);
}
/*
* cmd_poweroff()
*
* Subcommand to power off a hotplug connector.
*/
static int
cmd_poweroff(int argc, char *argv[], const char *usage_str)
{
hp_node_t root;
hp_node_t results = NULL;
char *path = NULL;
char *connection = NULL;
int flags = 0;
int rv;
/* Parse command line options */
parse_common(argc, argv, usage_str);
parse_flags(argc, argv, &flags, usage_str);
parse_target(argc, argv, &path, &connection, usage_str);
/* Path and connection are required */
if ((path == NULL) || (connection == NULL)) {
(void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
usage(usage_str);
return (EINVAL);
}
/* Get hotplug information snapshot */
if ((root = hp_init(path, connection, 0)) == NULL) {
print_error(errno);
return (errno);
}
/* Verify target is a connector */
if (hp_type(root) != HP_NODE_CONNECTOR) {
(void) fprintf(stderr,
gettext("ERROR: invalid target (must be a connector).\n"));
hp_fini(root);
return (EINVAL);
}
/* Do state change */
rv = hp_set_state(root, flags, DDI_HP_CN_STATE_PRESENT, &results);
/* Display results */
print_error(rv);
if (results != NULL) {
(void) hp_traverse(results, NULL, error_cb);
hp_fini(results);
}
/* Discard hotplug information snapshot */
hp_fini(root);
return (rv);
}
/*
* cmd_getpriv()
*
* Subcommand to get and display bus private options.
*/
static int
cmd_getpriv(int argc, char *argv[], const char *usage_str)
{
hp_node_t root;
char *path = NULL;
char *connection = NULL;
char *options = NULL;
char *results = NULL;
int rv;
/* Parse command line options */
parse_common(argc, argv, usage_str);
parse_options(argc, argv, &options, usage_str);
parse_target(argc, argv, &path, &connection, usage_str);
/* Options, path, and connection are all required */
if ((options == NULL) || (path == NULL) || (connection == NULL)) {
(void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
usage(usage_str);
return (EINVAL);
}
/* Get hotplug information snapshot */
if ((root = hp_init(path, connection, 0)) == NULL) {
print_error(errno);
return (errno);
}
/* Verify target is a connector */
if (hp_type(root) != HP_NODE_CONNECTOR) {
(void) fprintf(stderr,
gettext("ERROR: invalid target (must be a connector).\n"));
hp_fini(root);
return (EINVAL);
}
/* Do the operation */
rv = hp_get_private(root, options, &results);
/* Display results */
if (rv == ENOTSUP) {
(void) fprintf(stderr,
gettext("ERROR: unsupported property name or value.\n"));
(void) fprintf(stderr,
gettext("(Properties may depend upon connector state.)\n"));
} else if (rv != 0) {
print_error(rv);
}
if (results != NULL) {
print_options(results);
free(results);
}
/* Discard hotplug information snapshot */
hp_fini(root);
return (rv);
}
/*
* cmd_setpriv()
*
* Subcommand to set bus private options.
*/
static int
cmd_setpriv(int argc, char *argv[], const char *usage_str)
{
hp_node_t root;
char *path = NULL;
char *connection = NULL;
char *options = NULL;
char *results = NULL;
int rv;
/* Parse command line options */
parse_common(argc, argv, usage_str);
parse_options(argc, argv, &options, usage_str);
parse_target(argc, argv, &path, &connection, usage_str);
/* Options, path, and connection are all required */
if ((options == NULL) || (path == NULL) || (connection == NULL)) {
(void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
usage(usage_str);
return (EINVAL);
}
/* Get hotplug information snapshot */
if ((root = hp_init(path, connection, 0)) == NULL) {
print_error(errno);
return (errno);
}
/* Verify target is a connector */
if (hp_type(root) != HP_NODE_CONNECTOR) {
(void) fprintf(stderr,
gettext("ERROR: invalid target (must be a connector).\n"));
hp_fini(root);
return (EINVAL);
}
/* Do the operation */
rv = hp_set_private(root, options, &results);
/* Display results */
if (rv == ENOTSUP) {
(void) fprintf(stderr,
gettext("ERROR: unsupported property name or value.\n"));
(void) fprintf(stderr,
gettext("(Properties may depend upon connector state.)\n"));
} else if (rv != 0) {
print_error(rv);
}
if (results != NULL) {
print_options(results);
free(results);
}
/* Discard hotplug information snapshot */
hp_fini(root);
return (rv);
}
/*
* cmd_changestate()
*
* Subcommand to initiate a state change operation. This is
* a hidden subcommand to directly set a connector or port to
* a specific target state.
*/
static int
cmd_changestate(int argc, char *argv[], const char *usage_str)
{
hp_node_t root;
hp_node_t results = NULL;
char *path = NULL;
char *connection = NULL;
int state = -1;
int flags = 0;
int opt, rv;
/* Parse command line options */
parse_common(argc, argv, usage_str);
while ((opt = getopt_clip(argc, argv, "fqs:", changestate_opts,
NULL)) != -1) {
switch (opt) {
case 'f':
flags |= HPFORCE;
break;
case 'q':
flags |= HPQUERY;
break;
case 's':
if ((state = state_atoi(optarg)) == -1) {
(void) printf("ERROR: invalid target state\n");
return (EINVAL);
}
break;
default:
bad_option(opt, optopt, usage_str);
break;
}
}
parse_target(argc, argv, &path, &connection, usage_str);
/* State, path, and connection are all required */
if ((state == -1) || (path == NULL) || (connection == NULL)) {
(void) fprintf(stderr, gettext("ERROR: too few arguments.\n"));
usage(usage_str);
return (EINVAL);
}
/* Check that target state is valid */
if (valid_target(state) == 0) {
(void) fprintf(stderr,
gettext("ERROR: invalid target state\n"));
return (EINVAL);
}
/* Get hotplug information snapshot */
if ((root = hp_init(path, connection, 0)) == NULL) {
print_error(errno);
return (errno);
}
/* Initiate state change operation on root of snapshot */
rv = hp_set_state(root, flags, state, &results);
/* Display results */
print_error(rv);
if (results) {
(void) hp_traverse(results, NULL, error_cb);
hp_fini(results);
}
/* Discard hotplug information snapshot */
hp_fini(root);
return (rv);
}
/*
* parse_common()
*
* Parse command line options that are common to the
* entire program, and to each of its subcommands.
*/
static void
parse_common(int argc, char *argv[], const char *usage_str)
{
int opt;
extern int opterr;
extern int optind;
/* Turn off error reporting */
opterr = 0;
while ((opt = getopt_clip(argc, argv, "?V", common_opts, NULL)) != -1) {
switch (opt) {
case '?':
if (optopt == '?') {
usage(usage_str);
exit(0);
}
break;
case 'V':
(void) printf(gettext("%s: Version %s\n"),
prog, version);
exit(0);
default:
break;
}
}
/* Reset option index */
optind = 1;
}
/*
* parse_flags()
*
* Parse command line flags common to all downward state
* change operations (offline, disable, poweoff).
*/
static void
parse_flags(int argc, char *argv[], int *flagsp, const char *usage_str)
{
int opt;
int flags = 0;
while ((opt = getopt_clip(argc, argv, "fq", flag_opts, NULL)) != -1) {
switch (opt) {
case 'f':
flags |= HPFORCE;
break;
case 'q':
flags |= HPQUERY;
break;
default:
bad_option(opt, optopt, usage_str);
break;
}
}
*flagsp = flags;
}
/*
* parse_options()
*
* Parse command line options common to the bus private set and
* get subcommands.
*/
static void
parse_options(int argc, char *argv[], char **optionsp, const char *usage_str)
{
int opt;
while ((opt = getopt_clip(argc, argv, "o:", private_opts,
NULL)) != -1) {
switch (opt) {
case 'o':
*optionsp = optarg;
break;
default:
bad_option(opt, optopt, usage_str);
break;
}
}
}
/*
* parse_target()
*
* Parse the target path and connection name from the command line.
*/
static void
parse_target(int argc, char *argv[], char **pathp, char **connectionp,
const char *usage_str)
{
extern int optind;
if (optind < argc)
*pathp = argv[optind++];
if (optind < argc)
*connectionp = argv[optind++];
if (optind < argc) {
(void) fprintf(stderr, gettext("ERROR: too many arguments.\n"));
usage(usage_str);
exit(EINVAL);
}
}
/*
* bad_option()
*
* Routine to handle bad command line options.
*/
static void
bad_option(int opt, int optopt, const char *usage_str)
{
switch (opt) {
case ':':
(void) fprintf(stderr,
gettext("ERROR: option '%c' requires an argument.\n"),
optopt);
break;
default:
if (optopt == '?') {
usage(usage_str);
exit(EXIT_OK);
}
(void) fprintf(stderr,
gettext("ERROR: unrecognized option '%c'.\n"), optopt);
break;
}
usage(usage_str);
exit(EXIT_EINVAL);
}
/*
* usage()
*
* Display general usage of the command. Including
* the usage synopsis of each defined subcommand.
*/
static void
usage(const char *usage_str)
{
int i;
if (usage_str != NULL) {
(void) fprintf(stderr, gettext("Usage: %s %s\n\n"),
prog, usage_str);
return;
}
(void) fprintf(stderr, gettext("Usage: %s <subcommand> [<args>]\n\n"),
prog);
(void) fprintf(stderr, gettext("Subcommands:\n\n"));
for (i = 0; i < (sizeof (subcmds) / sizeof (subcmd_t)); i++)
(void) fprintf(stderr, " %s\n\n", subcmds[i].usage_str);
}
/*
* list_cb()
*
* Callback function for hp_traverse(), to display nodes
* of a hotplug information snapshot. (Short version.)
*/
/*ARGSUSED*/
static int
list_cb(hp_node_t node, void *arg)
{
hp_node_t parent;
/* Indent */
for (parent = hp_parent(node); parent; parent = hp_parent(parent))
if (hp_type(parent) == HP_NODE_DEVICE)
(void) printf(" ");
switch (hp_type(node)) {
case HP_NODE_DEVICE:
(void) printf("%s\n", hp_name(node));
break;
case HP_NODE_CONNECTOR:
(void) printf("[%s]", hp_name(node));
(void) printf(" (%s)", state_itoa(hp_state(node)));
(void) printf("\n");
break;
case HP_NODE_PORT:
(void) printf("<%s>", hp_name(node));
(void) printf(" (%s)", state_itoa(hp_state(node)));
(void) printf("\n");
break;
case HP_NODE_USAGE:
(void) printf("{ %s }\n", hp_usage(node));
break;
}
return (HP_WALK_CONTINUE);
}
/*
* list_long_cb()
*
* Callback function for hp_traverse(), to display nodes
* of a hotplug information snapshot. (Long version.)
*/
/*ARGSUSED*/
static int
list_long_cb(hp_node_t node, void *arg)
{
char path[MAXPATHLEN];
char connection[MAXPATHLEN];
if (hp_type(node) != HP_NODE_USAGE) {
if (hp_path(node, path, connection) != 0)
return (HP_WALK_CONTINUE);
(void) printf("%s", path);
}
switch (hp_type(node)) {
case HP_NODE_CONNECTOR:
(void) printf(" [%s]", connection);
(void) printf(" (%s)", state_itoa(hp_state(node)));
break;
case HP_NODE_PORT:
(void) printf(" <%s>", connection);
(void) printf(" (%s)", state_itoa(hp_state(node)));
break;
case HP_NODE_USAGE:
(void) printf(" { %s }", hp_usage(node));
break;
}
(void) printf("\n");
return (HP_WALK_CONTINUE);
}
/*
* error_cb()
*
* Callback function for hp_traverse(), to display
* error results from a state change operation.
*/
/*ARGSUSED*/
static int
error_cb(hp_node_t node, void *arg)
{
hp_node_t child;
char *usage_str;
static char path[MAXPATHLEN];
static char connection[MAXPATHLEN];
if (((child = hp_child(node)) != NULL) &&
(hp_type(child) == HP_NODE_USAGE)) {
if (hp_path(node, path, connection) == 0)
(void) printf("%s:\n", path);
return (HP_WALK_CONTINUE);
}
if ((hp_type(node) == HP_NODE_USAGE) &&
((usage_str = hp_usage(node)) != NULL))
(void) printf(" { %s }\n", usage_str);
return (HP_WALK_CONTINUE);
}
/*
* print_options()
*
* Parse and display bus private options. The options are
* formatted as a string which conforms to the getsubopt(3C)
* format. This routine only splits the string elements as
* separated by commas, and displays each portion on its own
* separate line of output.
*/
static void
print_options(const char *options)
{
char *buf, *curr, *next;
size_t len;
/* Do nothing if options string is empty */
if ((len = strlen(options)) == 0)
return;
/* To avoid modifying the input string, make a copy on the stack */
if ((buf = (char *)alloca(len + 1)) == NULL) {
(void) printf("%s\n", options);
return;
}
(void) strlcpy(buf, options, len + 1);
/* Iterate through each comma-separated name/value pair */
curr = buf;
do {
if ((next = strchr(curr, ',')) != NULL) {
*next = '\0';
next++;
}
(void) printf("%s\n", curr);
} while ((curr = next) != NULL);
}
/*
* print_error()
*
* Common routine to print error numbers in an appropriate way.
* Prints nothing if error code is 0.
*/
static void
print_error(int error)
{
switch (error) {
case 0:
/* No error */
return;
case EACCES:
(void) fprintf(stderr,
gettext("ERROR: operation not authorized.\n"));
break;
case EBADF:
(void) fprintf(stderr,
gettext("ERROR: hotplug service is not available.\n"));
break;
case EBUSY:
(void) fprintf(stderr,
gettext("ERROR: devices or resources are busy.\n"));
break;
case EEXIST:
(void) fprintf(stderr,
gettext("ERROR: resource already exists.\n"));
break;
case EFAULT:
(void) fprintf(stderr,
gettext("ERROR: internal failure in hotplug service.\n"));
break;
case EINVAL:
(void) fprintf(stderr,
gettext("ERROR: invalid arguments.\n"));
break;
case ENOENT:
(void) fprintf(stderr,
gettext("ERROR: there are no connections to display.\n"));
(void) fprintf(stderr,
gettext("(See hotplug(1m) for more information.)\n"));
break;
case ENXIO:
(void) fprintf(stderr,
gettext("ERROR: no such path or connection.\n"));
break;
case ENOMEM:
(void) fprintf(stderr,
gettext("ERROR: not enough memory.\n"));
break;
case ENOTSUP:
(void) fprintf(stderr,
gettext("ERROR: operation not supported.\n"));
break;
case EIO:
(void) fprintf(stderr,
gettext("ERROR: hardware or driver specific failure.\n"));
break;
default:
(void) fprintf(stderr, gettext("ERROR: operation failed: %s\n"),
strerror(error));
break;
}
}
/*
* state_atoi()
*
* Convert a hotplug state from a string to an integer.
*/
static int
state_atoi(char *state)
{
int i;
for (i = 0; hpstates[i].state_str != NULL; i++)
if (strcasecmp(state, hpstates[i].state_str) == 0)
return (hpstates[i].state);
return (-1);
}
/*
* state_itoa()
*
* Convert a hotplug state from an integer to a string.
*/
static char *
state_itoa(int state)
{
static char unknown[] = "UNKNOWN";
int i;
for (i = 0; hpstates[i].state_str != NULL; i++)
if (state == hpstates[i].state)
return (hpstates[i].state_str);
return (unknown);
}
/*
* valid_target()
*
* Check if a state is a valid target for a changestate command.
*/
static short
valid_target(int state)
{
int i;
for (i = 0; hpstates[i].state_str != NULL; i++)
if (state == hpstates[i].state)
return (hpstates[i].valid_target);
return (0);
}