sharectl.c revision 25a68471b9ababbc21cfdbbb2866014f34f419ec
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <getopt.h>
#include <libgen.h>
#include "libshare.h"
#include <sharemgr.h>
#include <libintl.h>
#include <locale.h>
static int run_command(char *, int, char **, sa_handle_t);
static void sub_command_help(char *proto);
static void
global_help()
{
(void) printf(gettext("usage: sharectl <command> [options]\n"));
sub_command_help(NULL);
}
int
main(int argc, char *argv[])
{
int c;
int help = 0;
int rval;
char *command;
sa_handle_t handle;
/*
* make sure locale and gettext domain is setup
*/
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
handle = sa_init(SA_INIT_CONTROL_API);
while ((c = getopt(argc, argv, "h?")) != EOF) {
switch (c) {
case '?':
case 'h':
help = 1;
break;
default:
(void) printf(gettext("Invalid option: %c\n"), c);
}
}
if (optind == argc || help) {
/* no subcommand */
global_help();
exit(0);
}
optind = 1;
/*
* now have enough to parse rest of command line
*/
command = argv[optind];
rval = run_command(command, argc - optind, argv + optind, handle);
sa_fini(handle);
return (rval);
}
char *
sc_get_usage(sc_usage_t index)
{
char *ret = NULL;
switch (index) {
case USAGE_CTL_GET:
ret = gettext("get [-h | -p property ...] proto");
break;
case USAGE_CTL_SET:
ret = gettext("set [-h] -p property=value ... proto");
break;
case USAGE_CTL_STATUS:
ret = gettext("status [-h | proto ...]");
break;
}
return (ret);
}
/*ARGSUSED*/
static int
sc_get(sa_handle_t handle, int flags, int argc, char *argv[])
{
char *proto = NULL;
struct options *optlist = NULL;
int ret = SA_OK;
int c;
while ((c = getopt(argc, argv, "?hp:")) != EOF) {
switch (c) {
case 'p':
ret = add_opt(&optlist, optarg, 1);
if (ret != SA_OK) {
(void) printf(gettext(
"Problem with property: %s\n"), optarg);
return (SA_NO_MEMORY);
}
break;
default:
(void) printf(gettext("usage: %s\n"),
sc_get_usage(USAGE_CTL_GET));
return (SA_SYNTAX_ERR);
case '?':
case 'h':
(void) printf(gettext("usage: %s\n"),
sc_get_usage(USAGE_CTL_GET));
return (SA_OK);
break;
}
}
if (optind >= argc) {
(void) printf(gettext("usage: %s\n"),
sc_get_usage(USAGE_CTL_GET));
(void) printf(gettext("\tprotocol must be specified.\n"));
return (SA_INVALID_PROTOCOL);
}
proto = argv[optind];
if (sa_valid_protocol(proto)) {
sa_protocol_properties_t propset;
propset = sa_proto_get_properties(proto);
if (propset != NULL) {
sa_property_t prop;
char *value;
char *name;
if (optlist == NULL) {
/*
* Display all known properties for
* this protocol.
*/
for (prop = sa_get_protocol_property(propset,
NULL);
prop != NULL;
prop = sa_get_next_protocol_property(
prop)) {
/*
* Get and display the
* property and value.
*/
name = sa_get_property_attr(prop,
"type");
if (name != NULL) {
value = sa_get_property_attr(
prop, "value");
(void) printf(gettext(
"%s=%s\n"), name,
value != NULL ? value : "");
}
if (value != NULL)
sa_free_attr_string(value);
if (name != NULL)
sa_free_attr_string(name);
}
} else {
struct options *opt;
/* list the specified option(s) */
for (opt = optlist;
opt != NULL;
opt = opt->next) {
prop = sa_get_protocol_property(
propset, opt->optname);
if (prop != NULL) {
value = sa_get_property_attr(
prop, "value");
(void) printf(gettext(
"%s=%s\n"),
opt->optname,
value != NULL ?
value : "");
sa_free_attr_string(value);
} else {
(void) printf(gettext(
"%s: not defined\n"),
opt->optname);
ret = SA_NO_SUCH_PROP;
}
}
}
}
} else {
(void) printf(gettext("Invalid protocol specified: %s\n"),
proto);
ret = SA_INVALID_PROTOCOL;
}
return (ret);
}
/*ARGSUSED*/
static int
sc_set(sa_handle_t handle, int flags, int argc, char *argv[])
{
char *proto = NULL;
struct options *optlist = NULL;
int ret = SA_OK;
int c;
sa_protocol_properties_t propset;
while ((c = getopt(argc, argv, "?hp:")) != EOF) {
switch (c) {
case 'p':
ret = add_opt(&optlist, optarg, 0);
if (ret != SA_OK) {
(void) printf(gettext(
"Problem with property: %s\n"), optarg);
return (SA_NO_MEMORY);
}
break;
default:
(void) printf(gettext("usage: %s\n"),
sc_get_usage(USAGE_CTL_SET));
return (SA_SYNTAX_ERR);
case '?':
case 'h':
(void) printf(gettext("usage: %s\n"),
sc_get_usage(USAGE_CTL_SET));
return (SA_OK);
break;
}
}
if (optind >= argc) {
(void) printf(gettext("usage: %s\n"),
sc_get_usage(USAGE_CTL_SET));
(void) printf(gettext("\tprotocol must be specified.\n"));
return (SA_INVALID_PROTOCOL);
}
proto = argv[optind];
if (!sa_valid_protocol(proto)) {
(void) printf(gettext("Invalid protocol specified: %s\n"),
proto);
return (SA_INVALID_PROTOCOL);
}
propset = sa_proto_get_properties(proto);
if (propset != NULL) {
sa_property_t prop;
int err;
if (optlist == NULL) {
(void) printf(gettext("usage: %s\n"),
sc_get_usage(USAGE_CTL_SET));
(void) printf(gettext(
"\tat least one property and value "
"must be specified\n"));
} else {
struct options *opt;
/* list the specified option(s) */
for (opt = optlist;
opt != NULL;
opt = opt->next) {
prop = sa_get_protocol_property(
propset, opt->optname);
if (prop != NULL) {
/*
* "err" is used in order to
* prevent setting ret to
* SA_OK if there has been a
* real error. We want to be
* able to return an error
* status on exit in that
* case. Error messages are
* printed for each error, so
* we only care on exit that
* there was an error and not
* the specific error value.
*/
err = sa_set_protocol_property(
prop, opt->optvalue);
if (err != SA_OK) {
(void) printf(gettext(
"Could not set property"
" %s: %s\n"),
opt->optname,
sa_errorstr(err));
ret = err;
}
} else {
(void) printf(gettext(
"%s: not defined\n"),
opt->optname);
ret = SA_NO_SUCH_PROP;
}
}
}
}
return (ret);
}
static void
show_status(char *proto)
{
char *status;
status = sa_get_protocol_status(proto);
(void) printf("%s\t%s\n", proto, status ? gettext(status) : "-");
if (status != NULL)
free(status);
}
static int
valid_proto(char **protos, int num, char *proto)
{
int i;
for (i = 0; i < num; i++)
if (strcmp(protos[i], proto) == 0)
return (1);
return (0);
}
/*ARGSUSED*/
static int
sc_status(sa_handle_t handle, int flags, int argc, char *argv[])
{
char **protos;
int ret = SA_OK;
int c;
int i;
int num_proto;
int verbose = 0;
while ((c = getopt(argc, argv, "?hv")) != EOF) {
switch (c) {
case 'v':
verbose++;
break;
case '?':
case 'h':
(void) printf(gettext("usage: %s\n"),
sc_get_usage(USAGE_CTL_STATUS));
return (SA_OK);
default:
(void) printf(gettext("usage: %s\n"),
sc_get_usage(USAGE_CTL_STATUS));
return (SA_SYNTAX_ERR);
}
}
num_proto = sa_get_protocols(&protos);
if (optind == argc) {
/* status for all protocols */
for (i = 0; i < num_proto; i++) {
show_status(protos[i]);
}
} else {
for (i = optind; i < argc; i++) {
if (valid_proto(protos, num_proto, argv[i])) {
show_status(argv[i]);
} else {
(void) printf(gettext("Invalid protocol: %s\n"),
argv[i]);
ret = SA_INVALID_PROTOCOL;
}
}
}
if (protos != NULL)
free(protos);
return (ret);
}
static sa_command_t commands[] = {
{"get", 0, sc_get, USAGE_CTL_GET},
{"set", 0, sc_set, USAGE_CTL_SET},
{"status", 0, sc_status, USAGE_CTL_STATUS},
{NULL, 0, NULL, 0},
};
/*ARGSUSED*/
void
sub_command_help(char *proto)
{
int i;
(void) printf("\tsub-commands:\n");
for (i = 0; commands[i].cmdname != NULL; i++) {
if (!(commands[i].flags & (CMD_ALIAS|CMD_NODISPLAY)))
(void) printf("\t%s\n",
sc_get_usage((sc_usage_t)commands[i].cmdidx));
}
}
sa_command_t *
sa_lookup(char *cmd)
{
int i;
size_t len;
len = strlen(cmd);
for (i = 0; commands[i].cmdname != NULL; i++) {
if (strncmp(cmd, commands[i].cmdname, len) == 0)
return (&commands[i]);
}
return (NULL);
}
static int
run_command(char *command, int argc, char *argv[], sa_handle_t handle)
{
sa_command_t *cmdvec;
int ret;
/*
* To get here, we know there should be a command due to the
* preprocessing done earlier. Need to find the protocol
* that is being affected. If no protocol, then it is ALL
* protocols.
*
* ??? do we really need the protocol at this level? it may be
* sufficient to let the commands look it up if needed since
* not all commands do proto specific things
*
* Known sub-commands are handled at this level. An unknown
* command will be passed down to the shared object that
* actually implements it. We can do this since the semantics
* of the common sub-commands is well defined.
*/
cmdvec = sa_lookup(command);
if (cmdvec == NULL) {
(void) printf(gettext("command %s not found\n"), command);
exit(1);
}
/*
* need to check priviledges and restrict what can be done
* based on least priviledge and sub-command.
*/
ret = cmdvec->cmdfunc(handle, NULL, argc, argv);
return (ret);
}