1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <unistd.h>
34 #include <getopt.h>
35 #include <libgen.h>
36 
37 #include "libshare.h"
38 #include <sharemgr.h>
39 
40 #include <libintl.h>
41 #include <locale.h>
42 
43 static int run_command(char *, int, char **, sa_handle_t);
44 static void sub_command_help(char *proto);
45 
46 static void
47 global_help()
48 {
49 	(void) printf(gettext("usage: sharectl <command> [options]\n"));
50 	sub_command_help(NULL);
51 }
52 
53 int
54 main(int argc, char *argv[])
55 {
56 	int c;
57 	int help = 0;
58 	int rval;
59 	char *command;
60 	sa_handle_t handle;
61 
62 	/*
63 	 * make sure locale and gettext domain is setup
64 	 */
65 	(void) setlocale(LC_ALL, "");
66 	(void) textdomain(TEXT_DOMAIN);
67 
68 	handle = sa_init(SA_INIT_CONTROL_API);
69 
70 	while ((c = getopt(argc, argv, "h?")) != EOF) {
71 	    switch (c) {
72 	    case '?':
73 	    case 'h':
74 		help = 1;
75 		break;
76 	    default:
77 		(void) printf(gettext("Invalid option: %c\n"), c);
78 	    }
79 	}
80 	if (optind == argc || help) {
81 	    /* no subcommand */
82 	    global_help();
83 	    exit(0);
84 	}
85 	optind = 1;
86 
87 	/*
88 	 * now have enough to parse rest of command line
89 	 */
90 	command = argv[optind];
91 	rval = run_command(command, argc - optind, argv + optind, handle);
92 
93 	sa_fini(handle);
94 	return (rval);
95 }
96 
97 char *
98 sc_get_usage(sc_usage_t index)
99 {
100 	char *ret = NULL;
101 
102 	switch (index) {
103 	case USAGE_CTL_GET:
104 	    ret = gettext("get [-h | -p property ...] proto");
105 	    break;
106 	case USAGE_CTL_SET:
107 	    ret = gettext("set [-h] -p property=value ... proto");
108 	    break;
109 	case USAGE_CTL_STATUS:
110 	    ret = gettext("status [-h | proto ...]");
111 	    break;
112 	}
113 	return (ret);
114 }
115 
116 /*ARGSUSED*/
117 static int
118 sc_get(sa_handle_t handle, int flags, int argc, char *argv[])
119 {
120 	char *proto = NULL;
121 	struct options *optlist = NULL;
122 	int ret = SA_OK;
123 	int c;
124 
125 	while ((c = getopt(argc, argv, "?hp:")) != EOF) {
126 	    switch (c) {
127 	    case 'p':
128 		ret = add_opt(&optlist, optarg, 1);
129 		if (ret != SA_OK) {
130 		    (void) printf(gettext("Problem with property: %s\n"),
131 						optarg);
132 		    return (SA_NO_MEMORY);
133 		}
134 		break;
135 	    default:
136 		(void) printf(gettext("usage: %s\n"),
137 				sc_get_usage(USAGE_CTL_GET));
138 		return (SA_SYNTAX_ERR);
139 	    case '?':
140 	    case 'h':
141 		(void) printf(gettext("usage: %s\n"),
142 				sc_get_usage(USAGE_CTL_GET));
143 		return (SA_OK);
144 		break;
145 	    }
146 	}
147 
148 	if (optind >= argc) {
149 	    (void) printf(gettext("usage: %s\n"), sc_get_usage(USAGE_CTL_GET));
150 	    (void) printf(gettext("\tprotocol must be specified.\n"));
151 	    return (SA_INVALID_PROTOCOL);
152 	}
153 
154 	proto = argv[optind];
155 	if (sa_valid_protocol(proto)) {
156 	    sa_protocol_properties_t propset;
157 	    propset = sa_proto_get_properties(proto);
158 	    if (propset != NULL) {
159 		sa_property_t prop;
160 		char *value;
161 		char *name;
162 		if (optlist == NULL) {
163 		    /* display all known properties for this protocol */
164 		    for (prop = sa_get_protocol_property(propset, NULL);
165 			prop != NULL;
166 			prop = sa_get_next_protocol_property(prop)) {
167 
168 			/* get and display the property and value */
169 			name = sa_get_property_attr(prop, "type");
170 			if (name != NULL) {
171 			    value = sa_get_property_attr(prop, "value");
172 			    (void) printf(gettext("%s=%s\n"), name,
173 						value != NULL ? value : "");
174 			}
175 			if (value != NULL)
176 			    sa_free_attr_string(value);
177 			if (name != NULL)
178 			    sa_free_attr_string(name);
179 		    }
180 		} else {
181 		    struct options *opt;
182 		    /* list the specified option(s) */
183 		    for (opt = optlist; opt != NULL; opt = opt->next) {
184 			prop = sa_get_protocol_property(propset, opt->optname);
185 			if (prop != NULL) {
186 			    value = sa_get_property_attr(prop, "value");
187 			    (void) printf(gettext("%s=%s\n"), opt->optname,
188 					value != NULL ? value : "");
189 			    sa_free_attr_string(value);
190 			} else {
191 			    (void) printf(gettext("%s: not defined\n"),
192 						opt->optname);
193 			    ret = SA_NO_SUCH_PROP;
194 			}
195 		    }
196 		}
197 	    }
198 	} else {
199 	    (void) printf(gettext("Invalid protocol specified: %s\n"), proto);
200 	    ret = SA_INVALID_PROTOCOL;
201 	}
202 	return (ret);
203 }
204 
205 /*ARGSUSED*/
206 static int
207 sc_set(sa_handle_t handle, int flags, int argc, char *argv[])
208 {
209 	char *proto = NULL;
210 	struct options *optlist = NULL;
211 	int ret = SA_OK;
212 	int c;
213 
214 	while ((c = getopt(argc, argv, "?hp:")) != EOF) {
215 	    switch (c) {
216 	    case 'p':
217 		ret = add_opt(&optlist, optarg, 0);
218 		if (ret != SA_OK) {
219 		    (void) printf(gettext("Problem with property: %s\n"),
220 					optarg);
221 		    return (SA_NO_MEMORY);
222 		}
223 		break;
224 	    default:
225 		(void) printf(gettext("usage: %s\n"),
226 				sc_get_usage(USAGE_CTL_SET));
227 		return (SA_SYNTAX_ERR);
228 	    case '?':
229 	    case 'h':
230 		(void) printf(gettext("usage: %s\n"),
231 				sc_get_usage(USAGE_CTL_SET));
232 		return (SA_OK);
233 		break;
234 	    }
235 	}
236 
237 	if (optind >= argc) {
238 	    (void) printf(gettext("usage: %s\n"),
239 				sc_get_usage(USAGE_CTL_SET));
240 	    (void) printf(gettext("\tprotocol must be specified.\n"));
241 	    return (SA_INVALID_PROTOCOL);
242 	}
243 
244 	proto = argv[optind];
245 	if (sa_valid_protocol(proto)) {
246 	    sa_protocol_properties_t propset;
247 	    propset = sa_proto_get_properties(proto);
248 	    if (propset != NULL) {
249 		sa_property_t prop;
250 		int err;
251 
252 		if (optlist == NULL) {
253 		    (void) printf(gettext("usage: %s\n"),
254 				sc_get_usage(USAGE_CTL_SET));
255 		    (void) printf(gettext("\tat least one property and value "
256 				    "must be specified\n"));
257 		} else {
258 		    struct options *opt;
259 		    /* list the specified option(s) */
260 		    for (opt = optlist; opt != NULL; opt = opt->next) {
261 			prop = sa_get_protocol_property(propset, opt->optname);
262 			if (prop != NULL) {
263 				/*
264 				 * "err" is used in order to prevent
265 				 * setting ret to SA_OK if there has
266 				 * been a real error. We want to be
267 				 * able to return an error status on
268 				 * exit in that case. Error messages
269 				 * are printed for each error, so we
270 				 * only care on exit that there was an
271 				 * error and not the specific error
272 				 * value.
273 				 */
274 			    err = sa_set_protocol_property(prop, opt->optvalue);
275 			    if (err != SA_OK) {
276 				(void) printf(gettext("Could not set property"
277 							" %s: %s\n"),
278 					opt->optname, sa_errorstr(err));
279 				ret = err;
280 			    }
281 			} else {
282 			    (void) printf(gettext("%s: not defined\n"),
283 						opt->optname);
284 			    ret = SA_NO_SUCH_PROP;
285 			}
286 		    }
287 		}
288 	    }
289 	} else {
290 	    (void) printf(gettext("Invalid protocol specified: %s\n"), proto);
291 	    ret = SA_INVALID_PROTOCOL;
292 	}
293 	return (ret);
294 }
295 
296 static void
297 show_status(char *proto)
298 {
299 	char *status;
300 	status = sa_get_protocol_status(proto);
301 	(void) printf("%s\t%s\n", proto, status ? gettext(status) : "-");
302 	if (status != NULL)
303 	    free(status);
304 }
305 
306 static int
307 valid_proto(char **protos, int num, char *proto)
308 {
309 	int i;
310 	for (i = 0; i < num; i++)
311 	    if (strcmp(protos[i], proto) == 0)
312 		return (1);
313 	return (0);
314 }
315 
316 /*ARGSUSED*/
317 static int
318 sc_status(sa_handle_t handle, int flags, int argc, char *argv[])
319 {
320 	char **protos;
321 	int ret = SA_OK;
322 	int c;
323 	int i;
324 	int num_proto;
325 	int verbose = 0;
326 
327 	while ((c = getopt(argc, argv, "?hv")) != EOF) {
328 	    switch (c) {
329 	    case 'v':
330 		verbose++;
331 		break;
332 	    case '?':
333 	    case 'h':
334 		(void) printf(gettext("usage: %s\n"),
335 				sc_get_usage(USAGE_CTL_STATUS));
336 		return (SA_OK);
337 	    default:
338 		(void) printf(gettext("usage: %s\n"),
339 				sc_get_usage(USAGE_CTL_STATUS));
340 		return (SA_SYNTAX_ERR);
341 	    }
342 	}
343 
344 	num_proto = sa_get_protocols(&protos);
345 	if (optind == argc) {
346 	    /* status for all protocols */
347 	    for (i = 0; i < num_proto; i++) {
348 		show_status(protos[i]);
349 	    }
350 	} else {
351 	    for (i = optind; i < argc; i++) {
352 		if (valid_proto(protos, num_proto, argv[i])) {
353 		    show_status(argv[i]);
354 		} else {
355 		    (void) printf(gettext("Invalid protocol: %s\n"), argv[i]);
356 		    ret = SA_INVALID_PROTOCOL;
357 		}
358 	    }
359 	}
360 	if (protos != NULL)
361 	    free(protos);
362 	return (ret);
363 }
364 
365 static sa_command_t commands[] = {
366 	{"get", 0, sc_get, USAGE_CTL_GET},
367 	{"set", 0, sc_set, USAGE_CTL_SET},
368 	{"status", 0, sc_status, USAGE_CTL_STATUS},
369 	{NULL, 0, NULL, 0},
370 };
371 
372 /*ARGSUSED*/
373 void
374 sub_command_help(char *proto)
375 {
376 	int i;
377 
378 	(void) printf("\tsub-commands:\n");
379 	for (i = 0; commands[i].cmdname != NULL; i++) {
380 		if (!(commands[i].flags & (CMD_ALIAS|CMD_NODISPLAY)))
381 			(void) printf("\t%s\n",
382 				sc_get_usage((sc_usage_t)commands[i].cmdidx));
383 	}
384 }
385 
386 sa_command_t *
387 sa_lookup(char *cmd)
388 {
389 	int i;
390 	size_t len;
391 
392 	len = strlen(cmd);
393 	for (i = 0; commands[i].cmdname != NULL; i++) {
394 		if (strncmp(cmd, commands[i].cmdname, len) == 0)
395 			return (&commands[i]);
396 	}
397 	return (NULL);
398 }
399 
400 static int
401 run_command(char *command, int argc, char *argv[], sa_handle_t handle)
402 {
403 	sa_command_t *cmdvec;
404 	int ret;
405 
406 	/*
407 	 * To get here, we know there should be a command due to the
408 	 * preprocessing done earlier.  Need to find the protocol
409 	 * that is being affected. If no protocol, then it is ALL
410 	 * protocols.
411 	 *
412 	 * ??? do we really need the protocol at this level? it may be
413 	 * sufficient to let the commands look it up if needed since
414 	 * not all commands do proto specific things
415 	 *
416 	 * Known sub-commands are handled at this level. An unknown
417 	 * command will be passed down to the shared object that
418 	 * actually implements it. We can do this since the semantics
419 	 * of the common sub-commands is well defined.
420 	 */
421 
422 	cmdvec = sa_lookup(command);
423 	if (cmdvec == NULL) {
424 		(void) printf(gettext("command %s not found\n"), command);
425 		exit(1);
426 	}
427 	/*
428 	 * need to check priviledges and restrict what can be done
429 	 * based on least priviledge and sub-command.
430 	 */
431 	ret = cmdvec->cmdfunc(handle, NULL, argc, argv);
432 	return (ret);
433 }
434