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(
131 				    "Problem with property: %s\n"), 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"),
150 		    sc_get_usage(USAGE_CTL_GET));
151 		(void) printf(gettext("\tprotocol must be specified.\n"));
152 		return (SA_INVALID_PROTOCOL);
153 	}
154 
155 	proto = argv[optind];
156 	if (sa_valid_protocol(proto)) {
157 		sa_protocol_properties_t propset;
158 		propset = sa_proto_get_properties(proto);
159 		if (propset != NULL) {
160 			sa_property_t prop;
161 			char *value;
162 			char *name;
163 
164 			if (optlist == NULL) {
165 				/*
166 				 * Display all known properties for
167 				 * this protocol.
168 				 */
169 				for (prop = sa_get_protocol_property(propset,
170 				    NULL);
171 				    prop != NULL;
172 				    prop = sa_get_next_protocol_property(
173 				    prop)) {
174 
175 					/*
176 					 * Get and display the
177 					 * property and value.
178 					 */
179 					name = sa_get_property_attr(prop,
180 					    "type");
181 					if (name != NULL) {
182 						value = sa_get_property_attr(
183 						    prop, "value");
184 						(void) printf(gettext(
185 						    "%s=%s\n"), name,
186 						    value != NULL ? value : "");
187 					}
188 					if (value != NULL)
189 						sa_free_attr_string(value);
190 					if (name != NULL)
191 						sa_free_attr_string(name);
192 				}
193 			} else {
194 				struct options *opt;
195 				/* list the specified option(s) */
196 				for (opt = optlist;
197 				    opt != NULL;
198 				    opt = opt->next) {
199 					prop = sa_get_protocol_property(
200 					    propset, opt->optname);
201 					if (prop != NULL) {
202 						value = sa_get_property_attr(
203 						    prop, "value");
204 						(void) printf(gettext(
205 						    "%s=%s\n"),
206 						    opt->optname,
207 						    value != NULL ?
208 						    value : "");
209 						sa_free_attr_string(value);
210 					} else {
211 						(void) printf(gettext(
212 						    "%s: not defined\n"),
213 						    opt->optname);
214 						ret = SA_NO_SUCH_PROP;
215 					}
216 				}
217 			}
218 		}
219 	} else {
220 		(void) printf(gettext("Invalid protocol specified: %s\n"),
221 		    proto);
222 		ret = SA_INVALID_PROTOCOL;
223 	}
224 	return (ret);
225 }
226 
227 /*ARGSUSED*/
228 static int
229 sc_set(sa_handle_t handle, int flags, int argc, char *argv[])
230 {
231 	char *proto = NULL;
232 	struct options *optlist = NULL;
233 	int ret = SA_OK;
234 	int c;
235 	sa_protocol_properties_t propset;
236 
237 	while ((c = getopt(argc, argv, "?hp:")) != EOF) {
238 		switch (c) {
239 		case 'p':
240 			ret = add_opt(&optlist, optarg, 0);
241 			if (ret != SA_OK) {
242 				(void) printf(gettext(
243 				    "Problem with property: %s\n"), optarg);
244 				return (SA_NO_MEMORY);
245 			}
246 			break;
247 		default:
248 			(void) printf(gettext("usage: %s\n"),
249 			    sc_get_usage(USAGE_CTL_SET));
250 			return (SA_SYNTAX_ERR);
251 		case '?':
252 		case 'h':
253 			(void) printf(gettext("usage: %s\n"),
254 			    sc_get_usage(USAGE_CTL_SET));
255 			return (SA_OK);
256 			break;
257 		}
258 	}
259 
260 	if (optind >= argc) {
261 		(void) printf(gettext("usage: %s\n"),
262 		    sc_get_usage(USAGE_CTL_SET));
263 		(void) printf(gettext("\tprotocol must be specified.\n"));
264 		return (SA_INVALID_PROTOCOL);
265 	}
266 
267 	proto = argv[optind];
268 	if (!sa_valid_protocol(proto)) {
269 		(void) printf(gettext("Invalid protocol specified: %s\n"),
270 		    proto);
271 		return (SA_INVALID_PROTOCOL);
272 	}
273 	propset = sa_proto_get_properties(proto);
274 	if (propset != NULL) {
275 		sa_property_t prop;
276 		int err;
277 		if (optlist == NULL) {
278 			(void) printf(gettext("usage: %s\n"),
279 			    sc_get_usage(USAGE_CTL_SET));
280 			(void) printf(gettext(
281 			    "\tat least one property and value "
282 			    "must be specified\n"));
283 		} else {
284 			struct options *opt;
285 			/* list the specified option(s) */
286 			for (opt = optlist;
287 			    opt != NULL;
288 			    opt = opt->next) {
289 				prop = sa_get_protocol_property(
290 				    propset, opt->optname);
291 				if (prop != NULL) {
292 					/*
293 					 * "err" is used in order to
294 					 * prevent setting ret to
295 					 * SA_OK if there has been a
296 					 * real error. We want to be
297 					 * able to return an error
298 					 * status on exit in that
299 					 * case. Error messages are
300 					 * printed for each error, so
301 					 * we only care on exit that
302 					 * there was an error and not
303 					 * the specific error value.
304 					 */
305 					err = sa_set_protocol_property(
306 					    prop, opt->optvalue);
307 					if (err != SA_OK) {
308 						(void) printf(gettext(
309 						    "Could not set property"
310 						    " %s: %s\n"),
311 						    opt->optname,
312 						    sa_errorstr(err));
313 						ret = err;
314 					}
315 				} else {
316 					(void) printf(gettext(
317 					    "%s: not defined\n"),
318 					    opt->optname);
319 					ret = SA_NO_SUCH_PROP;
320 				}
321 			}
322 		}
323 	}
324 	return (ret);
325 }
326 
327 static void
328 show_status(char *proto)
329 {
330 	char *status;
331 
332 	status = sa_get_protocol_status(proto);
333 	(void) printf("%s\t%s\n", proto, status ? gettext(status) : "-");
334 	if (status != NULL)
335 		free(status);
336 }
337 
338 static int
339 valid_proto(char **protos, int num, char *proto)
340 {
341 	int i;
342 	for (i = 0; i < num; i++)
343 		if (strcmp(protos[i], proto) == 0)
344 			return (1);
345 	return (0);
346 }
347 
348 /*ARGSUSED*/
349 static int
350 sc_status(sa_handle_t handle, int flags, int argc, char *argv[])
351 {
352 	char **protos;
353 	int ret = SA_OK;
354 	int c;
355 	int i;
356 	int num_proto;
357 	int verbose = 0;
358 
359 	while ((c = getopt(argc, argv, "?hv")) != EOF) {
360 		switch (c) {
361 		case 'v':
362 			verbose++;
363 			break;
364 		case '?':
365 		case 'h':
366 			(void) printf(gettext("usage: %s\n"),
367 			    sc_get_usage(USAGE_CTL_STATUS));
368 			return (SA_OK);
369 		default:
370 			(void) printf(gettext("usage: %s\n"),
371 			    sc_get_usage(USAGE_CTL_STATUS));
372 			return (SA_SYNTAX_ERR);
373 		}
374 	}
375 
376 	num_proto = sa_get_protocols(&protos);
377 	if (optind == argc) {
378 		/* status for all protocols */
379 		for (i = 0; i < num_proto; i++) {
380 			show_status(protos[i]);
381 		}
382 	} else {
383 		for (i = optind; i < argc; i++) {
384 			if (valid_proto(protos, num_proto, argv[i])) {
385 				show_status(argv[i]);
386 			} else {
387 				(void) printf(gettext("Invalid protocol: %s\n"),
388 				    argv[i]);
389 				ret = SA_INVALID_PROTOCOL;
390 			}
391 		}
392 	}
393 	if (protos != NULL)
394 		free(protos);
395 	return (ret);
396 }
397 
398 static sa_command_t commands[] = {
399 	{"get", 0, sc_get, USAGE_CTL_GET},
400 	{"set", 0, sc_set, USAGE_CTL_SET},
401 	{"status", 0, sc_status, USAGE_CTL_STATUS},
402 	{NULL, 0, NULL, 0},
403 };
404 
405 /*ARGSUSED*/
406 void
407 sub_command_help(char *proto)
408 {
409 	int i;
410 
411 	(void) printf("\tsub-commands:\n");
412 	for (i = 0; commands[i].cmdname != NULL; i++) {
413 		if (!(commands[i].flags & (CMD_ALIAS|CMD_NODISPLAY)))
414 			(void) printf("\t%s\n",
415 			    sc_get_usage((sc_usage_t)commands[i].cmdidx));
416 	}
417 }
418 
419 sa_command_t *
420 sa_lookup(char *cmd)
421 {
422 	int i;
423 	size_t len;
424 
425 	len = strlen(cmd);
426 	for (i = 0; commands[i].cmdname != NULL; i++) {
427 		if (strncmp(cmd, commands[i].cmdname, len) == 0)
428 			return (&commands[i]);
429 	}
430 	return (NULL);
431 }
432 
433 static int
434 run_command(char *command, int argc, char *argv[], sa_handle_t handle)
435 {
436 	sa_command_t *cmdvec;
437 	int ret;
438 
439 	/*
440 	 * To get here, we know there should be a command due to the
441 	 * preprocessing done earlier.  Need to find the protocol
442 	 * that is being affected. If no protocol, then it is ALL
443 	 * protocols.
444 	 *
445 	 * ??? do we really need the protocol at this level? it may be
446 	 * sufficient to let the commands look it up if needed since
447 	 * not all commands do proto specific things
448 	 *
449 	 * Known sub-commands are handled at this level. An unknown
450 	 * command will be passed down to the shared object that
451 	 * actually implements it. We can do this since the semantics
452 	 * of the common sub-commands is well defined.
453 	 */
454 
455 	cmdvec = sa_lookup(command);
456 	if (cmdvec == NULL) {
457 		(void) printf(gettext("command %s not found\n"), command);
458 		exit(1);
459 	}
460 	/*
461 	 * need to check priviledges and restrict what can be done
462 	 * based on least priviledge and sub-command.
463 	 */
464 	ret = cmdvec->cmdfunc(handle, NULL, argc, argv);
465 	return (ret);
466 }
467