xref: /illumos-gate/usr/src/cmd/dfs.cmds/sharectl/sharectl.c (revision 2b24ab6b3865caeede9eeb9db6b83e1d89dcd1ea)
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 2008 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 	case USAGE_CTL_DELSECT:
113 		ret = gettext("delsect [-h] section proto");
114 		break;
115 	}
116 	return (ret);
117 }
118 
119 /*ARGSUSED*/
120 static int
121 sc_get(sa_handle_t handle, int flags, int argc, char *argv[])
122 {
123 	char *proto = NULL;
124 	struct options *optlist = NULL;
125 	int ret = SA_OK;
126 	int c;
127 	sa_protocol_properties_t propset, propsect;
128 	sa_property_t prop;
129 	char *section, *value, *name;
130 	int first = 1;
131 
132 	while ((c = getopt(argc, argv, "?hp:")) != EOF) {
133 		switch (c) {
134 		case 'p':
135 			ret = add_opt(&optlist, optarg, 1);
136 			if (ret != SA_OK) {
137 				(void) printf(gettext(
138 				    "Problem with property: %s\n"), optarg);
139 				return (SA_NO_MEMORY);
140 			}
141 			break;
142 		default:
143 			(void) printf(gettext("usage: %s\n"),
144 			    sc_get_usage(USAGE_CTL_GET));
145 			return (SA_SYNTAX_ERR);
146 		case '?':
147 		case 'h':
148 			(void) printf(gettext("usage: %s\n"),
149 			    sc_get_usage(USAGE_CTL_GET));
150 			return (SA_OK);
151 			break;
152 		}
153 	}
154 
155 	if (optind >= argc) {
156 		(void) printf(gettext("usage: %s\n"),
157 		    sc_get_usage(USAGE_CTL_GET));
158 		(void) printf(gettext("\tprotocol must be specified.\n"));
159 		return (SA_INVALID_PROTOCOL);
160 	}
161 
162 	proto = argv[optind];
163 	if (!sa_valid_protocol(proto)) {
164 		(void) printf(gettext("Invalid protocol specified: %s\n"),
165 		    proto);
166 		return (SA_INVALID_PROTOCOL);
167 	}
168 	propset = sa_proto_get_properties(proto);
169 	if (propset == NULL)
170 		return (ret);
171 
172 	if (optlist == NULL) {
173 		/* Display all known properties for this protocol */
174 		for (propsect = sa_get_protocol_section(propset, NULL);
175 		    propsect != NULL;
176 		    propsect = sa_get_next_protocol_section(propsect, NULL)) {
177 			section = sa_get_property_attr(propsect,
178 			    "name");
179 			/*
180 			 * If properties are organized into sections, as
181 			 * in the SMB client, print the section name.
182 			 */
183 			if (sa_proto_get_featureset(proto) &
184 			    SA_FEATURE_HAS_SECTIONS) {
185 				if (!first)
186 					(void) printf("\n");
187 				first = 0;
188 				(void) printf("[%s]\n", section);
189 			}
190 			/* Display properties for this section */
191 			for (prop = sa_get_protocol_property(propsect, NULL);
192 			    prop != NULL;
193 			    prop = sa_get_next_protocol_property(prop, NULL)) {
194 
195 				/* get and display the property and value */
196 				name = sa_get_property_attr(prop, "type");
197 				if (name != NULL) {
198 					value = sa_get_property_attr(prop,
199 					    "value");
200 					(void) printf(gettext("%s=%s\n"), name,
201 					    value != NULL ? value : "");
202 				}
203 				if (value != NULL)
204 					sa_free_attr_string(value);
205 				if (name != NULL)
206 					sa_free_attr_string(name);
207 			}
208 		}
209 	} else {
210 		struct options *opt;
211 
212 		/* list the specified option(s) */
213 		for (opt = optlist; opt != NULL; opt = opt->next) {
214 			int printed = 0;
215 
216 			for (propsect = sa_get_protocol_section(propset, NULL);
217 			    propsect != NULL;
218 			    propsect = sa_get_next_protocol_section(propsect,
219 			    NULL)) {
220 
221 				section = sa_get_property_attr(propsect,
222 				    "name");
223 				for (prop = sa_get_protocol_property(propsect,
224 				    opt->optname);
225 				    prop != NULL;
226 				    prop = sa_get_next_protocol_property(
227 				    propsect, opt->optname)) {
228 					value = sa_get_property_attr(prop,
229 					    "value");
230 					if (sa_proto_get_featureset(proto) &
231 					    SA_FEATURE_HAS_SECTIONS) {
232 						(void) printf(
233 						    gettext("[%s] %s=%s\n"),
234 						    section, opt->optname,
235 						    value != NULL ? value : "");
236 						sa_free_attr_string(section);
237 					} else {
238 						(void) printf(
239 						    gettext("%s=%s\n"),
240 						    opt->optname,
241 						    value != NULL ? value : "");
242 					}
243 					sa_free_attr_string(value);
244 					printed = 1;
245 				}
246 			}
247 			if (!printed) {
248 				(void) printf(gettext("%s: not defined\n"),
249 				    opt->optname);
250 				ret = SA_NO_SUCH_PROP;
251 			}
252 		}
253 	}
254 	return (ret);
255 }
256 
257 /*ARGSUSED*/
258 static int
259 sc_set(sa_handle_t handle, int flags, int argc, char *argv[])
260 {
261 	char *proto = NULL;
262 	struct options *optlist = NULL;
263 	sa_protocol_properties_t propsect;
264 	int ret = SA_OK;
265 	int c;
266 	int err;
267 	sa_protocol_properties_t propset;
268 	sa_property_t prop;
269 
270 	while ((c = getopt(argc, argv, "?hp:")) != EOF) {
271 		switch (c) {
272 		case 'p':
273 			ret = add_opt(&optlist, optarg, 0);
274 			if (ret != SA_OK) {
275 				(void) printf(gettext(
276 				    "Problem with property: %s\n"), optarg);
277 				return (SA_NO_MEMORY);
278 			}
279 			break;
280 		default:
281 			(void) printf(gettext("usage: %s\n"),
282 			    sc_get_usage(USAGE_CTL_SET));
283 			return (SA_SYNTAX_ERR);
284 		case '?':
285 		case 'h':
286 			(void) printf(gettext("usage: %s\n"),
287 			    sc_get_usage(USAGE_CTL_SET));
288 			return (SA_OK);
289 			break;
290 		}
291 	}
292 
293 	if (optind >= argc) {
294 		(void) printf(gettext("usage: %s\n"),
295 		    sc_get_usage(USAGE_CTL_SET));
296 		(void) printf(gettext("\tprotocol must be specified.\n"));
297 		return (SA_INVALID_PROTOCOL);
298 	}
299 
300 	proto = argv[optind];
301 	if (!sa_valid_protocol(proto)) {
302 		(void) printf(gettext("Invalid protocol specified: %s\n"),
303 		    proto);
304 		return (SA_INVALID_PROTOCOL);
305 	}
306 	propset = sa_proto_get_properties(proto);
307 	if (propset == NULL)
308 		return (ret);
309 
310 	if (optlist == NULL) {
311 		(void) printf(gettext("usage: %s\n"),
312 		    sc_get_usage(USAGE_CTL_SET));
313 		(void) printf(gettext(
314 		    "\tat least one property and value "
315 		    "must be specified\n"));
316 	} else {
317 		struct options *opt;
318 		char *section = NULL;
319 		/* fetch and change the specified option(s) */
320 		for (opt = optlist; opt != NULL; opt = opt->next) {
321 			if (strncmp("section", opt->optname, 7) == 0) {
322 				if (section != NULL)
323 					free(section);
324 				section = strdup(opt->optvalue);
325 				continue;
326 			}
327 			if (sa_proto_get_featureset(proto) &
328 			    SA_FEATURE_HAS_SECTIONS) {
329 				propsect = sa_get_protocol_section(propset,
330 				    section);
331 				prop = sa_get_protocol_property(propsect,
332 				    opt->optname);
333 			} else {
334 				prop = sa_get_protocol_property(propset,
335 				    opt->optname);
336 			}
337 			if (prop == NULL && sa_proto_get_featureset(proto) &
338 			    SA_FEATURE_ADD_PROPERTIES) {
339 				sa_property_t sect;
340 				sect = sa_create_section(section, NULL);
341 				sa_set_section_attr(sect, "type", proto);
342 				(void) sa_add_protocol_property(propset, sect);
343 				prop = sa_create_property(
344 				    opt->optname, opt->optvalue);
345 				(void) sa_add_protocol_property(sect, prop);
346 			}
347 			if (prop != NULL) {
348 				/*
349 				 * "err" is used in order to prevent
350 				 * setting ret to SA_OK if there has
351 				 * been a real error. We want to be
352 				 * able to return an error status on
353 				 * exit in that case. Error messages
354 				 * are printed for each error, so we
355 				 * only care on exit that there was an
356 				 * error and not the specific error
357 				 * value.
358 				 */
359 				err = sa_set_protocol_property(prop, section,
360 				    opt->optvalue);
361 				if (err != SA_OK) {
362 					(void) printf(gettext(
363 					    "Could not set property"
364 					    " %s: %s\n"),
365 					    opt->optname, sa_errorstr(err));
366 					ret = err;
367 				}
368 			} else {
369 				(void) printf(gettext("%s: not defined\n"),
370 				    opt->optname);
371 				ret = SA_NO_SUCH_PROP;
372 			}
373 		}
374 	}
375 	return (ret);
376 }
377 
378 static void
379 show_status(char *proto)
380 {
381 	char *status;
382 	uint64_t features;
383 
384 	status = sa_get_protocol_status(proto);
385 	features = sa_proto_get_featureset(proto);
386 	(void) printf("%s\t%s", proto, status ? gettext(status) : "-");
387 	if (status != NULL)
388 		free(status);
389 	/*
390 	 * Need to flag a client only protocol so test suites can
391 	 * remove it from consideration.
392 	 */
393 	if (!(features & SA_FEATURE_SERVER))
394 		(void) printf(" client");
395 	(void) printf("\n");
396 }
397 
398 static int
399 valid_proto(char **protos, int num, char *proto)
400 {
401 	int i;
402 	for (i = 0; i < num; i++)
403 		if (strcmp(protos[i], proto) == 0)
404 			return (1);
405 	return (0);
406 }
407 
408 /*ARGSUSED*/
409 static int
410 sc_status(sa_handle_t handle, int flags, int argc, char *argv[])
411 {
412 	char **protos;
413 	int ret = SA_OK;
414 	int c;
415 	int i;
416 	int num_proto;
417 	int verbose = 0;
418 
419 	while ((c = getopt(argc, argv, "?hv")) != EOF) {
420 		switch (c) {
421 		case 'v':
422 			verbose++;
423 			break;
424 		case '?':
425 		case 'h':
426 			(void) printf(gettext("usage: %s\n"),
427 			    sc_get_usage(USAGE_CTL_STATUS));
428 			return (SA_OK);
429 		default:
430 			(void) printf(gettext("usage: %s\n"),
431 			    sc_get_usage(USAGE_CTL_STATUS));
432 			return (SA_SYNTAX_ERR);
433 		}
434 	}
435 
436 	num_proto = sa_get_protocols(&protos);
437 	if (optind == argc) {
438 		/* status for all protocols */
439 		for (i = 0; i < num_proto; i++) {
440 			show_status(protos[i]);
441 		}
442 	} else {
443 		for (i = optind; i < argc; i++) {
444 			if (valid_proto(protos, num_proto, argv[i])) {
445 				show_status(argv[i]);
446 			} else {
447 				(void) printf(gettext("Invalid protocol: %s\n"),
448 				    argv[i]);
449 				ret = SA_INVALID_PROTOCOL;
450 			}
451 		}
452 	}
453 	if (protos != NULL)
454 		free(protos);
455 	return (ret);
456 }
457 
458 /*ARGSUSED*/
459 static int
460 sc_delsect(sa_handle_t handle, int flags, int argc, char *argv[])
461 {
462 	char *proto = NULL;
463 	char *section = NULL;
464 	sa_protocol_properties_t propset;
465 	sa_protocol_properties_t propsect;
466 	int ret = SA_OK;
467 	int c;
468 
469 	while ((c = getopt(argc, argv, "?h")) != EOF) {
470 		switch (c) {
471 		default:
472 			ret = SA_SYNTAX_ERR;
473 			/*FALLTHROUGH*/
474 		case '?':
475 		case 'h':
476 			(void) printf(gettext("usage: %s\n"),
477 			    sc_get_usage(USAGE_CTL_DELSECT));
478 			return (ret);
479 		}
480 		/*NOTREACHED*/
481 	}
482 
483 	section = argv[optind++];
484 
485 	if (optind >= argc) {
486 		(void) printf(gettext("usage: %s\n"),
487 		    sc_get_usage(USAGE_CTL_DELSECT));
488 		(void) printf(gettext(
489 		    "\tsection and protocol must be specified.\n"));
490 		return (SA_INVALID_PROTOCOL);
491 	}
492 
493 	proto = argv[optind];
494 	if (!sa_valid_protocol(proto)) {
495 		(void) printf(gettext("Invalid protocol specified: %s\n"),
496 		    proto);
497 		return (SA_INVALID_PROTOCOL);
498 	}
499 
500 	if ((sa_proto_get_featureset(proto) & SA_FEATURE_HAS_SECTIONS) == 0) {
501 		(void) printf(gettext("Protocol %s does not have sections\n"),
502 		    section, proto);
503 		return (SA_NOT_SUPPORTED);
504 	}
505 
506 	propset = sa_proto_get_properties(proto);
507 	if (propset == NULL) {
508 		(void) printf(gettext("Cannot get properties for %s\n"),
509 		    proto);
510 		return (SA_NO_PROPERTIES);
511 	}
512 
513 	propsect = sa_get_protocol_section(propset, section);
514 	if (propsect == NULL) {
515 		(void) printf(gettext("Cannot find section %s for proto %s\n"),
516 		    section, proto);
517 		return (SA_NO_SUCH_SECTION);
518 	}
519 
520 	ret = sa_proto_delete_section(proto, section);
521 
522 	return (ret);
523 }
524 
525 static sa_command_t commands[] = {
526 	{"get", 0, sc_get, USAGE_CTL_GET},
527 	{"set", 0, sc_set, USAGE_CTL_SET},
528 	{"status", 0, sc_status, USAGE_CTL_STATUS},
529 	{"delsect", 0, sc_delsect, USAGE_CTL_DELSECT},
530 	{NULL, 0, NULL, 0},
531 };
532 
533 /*ARGSUSED*/
534 void
535 sub_command_help(char *proto)
536 {
537 	int i;
538 
539 	(void) printf("\tsub-commands:\n");
540 	for (i = 0; commands[i].cmdname != NULL; i++) {
541 		if (!(commands[i].flags & (CMD_ALIAS|CMD_NODISPLAY)))
542 			(void) printf("\t%s\n",
543 			    sc_get_usage((sc_usage_t)commands[i].cmdidx));
544 	}
545 }
546 
547 sa_command_t *
548 sa_lookup(char *cmd)
549 {
550 	int i;
551 	size_t len;
552 
553 	len = strlen(cmd);
554 	for (i = 0; commands[i].cmdname != NULL; i++) {
555 		if (strncmp(cmd, commands[i].cmdname, len) == 0)
556 			return (&commands[i]);
557 	}
558 	return (NULL);
559 }
560 
561 static int
562 run_command(char *command, int argc, char *argv[], sa_handle_t handle)
563 {
564 	sa_command_t *cmdvec;
565 	int ret;
566 
567 	/*
568 	 * To get here, we know there should be a command due to the
569 	 * preprocessing done earlier.  Need to find the protocol
570 	 * that is being affected. If no protocol, then it is ALL
571 	 * protocols.
572 	 *
573 	 * ??? do we really need the protocol at this level? it may be
574 	 * sufficient to let the commands look it up if needed since
575 	 * not all commands do proto specific things
576 	 *
577 	 * Known sub-commands are handled at this level. An unknown
578 	 * command will be passed down to the shared object that
579 	 * actually implements it. We can do this since the semantics
580 	 * of the common sub-commands is well defined.
581 	 */
582 
583 	cmdvec = sa_lookup(command);
584 	if (cmdvec == NULL) {
585 		(void) printf(gettext("command %s not found\n"), command);
586 		exit(1);
587 	}
588 	/*
589 	 * need to check priviledges and restrict what can be done
590 	 * based on least priviledge and sub-command.
591 	 */
592 	ret = cmdvec->cmdfunc(handle, NULL, argc, argv);
593 	return (ret);
594 }
595