xref: /illumos-gate/usr/src/lib/libshare/autofs/libshare_autofs.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * AUTOMOUNT specific functions
28  */
29 #include <stdio.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <zone.h>
35 #include <errno.h>
36 #include <locale.h>
37 #include <fcntl.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <syslog.h>
41 #include "libshare.h"
42 #include "libshare_impl.h"
43 #include <pwd.h>
44 #include <limits.h>
45 #include <libscf.h>
46 #include <strings.h>
47 #include <libdlpi.h>
48 #include "smfcfg.h"
49 
50 
51 static int autofs_init();
52 static void autofs_fini();
53 static int autofs_validate_property(sa_handle_t, sa_property_t, sa_optionset_t);
54 static int autofs_set_proto_prop(sa_property_t);
55 static sa_protocol_properties_t autofs_get_proto_set();
56 static char *autofs_get_status();
57 static uint64_t autofs_features();
58 
59 static int initautofsprotofromsmf();
60 static int true_false_validator(int index, char *value);
61 static int strlen_validator(int index, char *value);
62 static int range_check_validator(int index, char *value);
63 
64 /*
65  * ops vector that provides the protocol specific info and operations
66  * for share management.
67  */
68 struct sa_plugin_ops sa_plugin_ops = {
69 	SA_PLUGIN_VERSION,
70 	"autofs",
71 	autofs_init,		/* Init autofs */
72 	autofs_fini,		/* Fini autofs */
73 	NULL,			/* Start Sharing */
74 	NULL,			/* stop sharing */
75 	autofs_validate_property,
76 	NULL,			/* valid_space */
77 	NULL,			/* security_prop */
78 	NULL,			/* parse optstring */
79 	NULL,			/* format optstring */
80 	autofs_set_proto_prop,	/* Set properties */
81 	autofs_get_proto_set,	/* get properties */
82 	autofs_get_status,	/* get status */
83 	NULL,			/* space_alias */
84 	NULL,			/* update_legacy */
85 	NULL,			/* delete_legacy */
86 	NULL,			/* change notify */
87 	NULL,			/* enable resource */
88 	NULL,			/* disable resource */
89 	autofs_features,	/* features */
90 	NULL,			/* transient shares */
91 	NULL,			/* notify resource */
92 	NULL,			/* rename resource */
93 	NULL,			/* run_command */
94 	NULL,			/* command_help */
95 	NULL			/* delete_proto_section */
96 };
97 
98 
99 static sa_protocol_properties_t protoset;
100 
101 #define	AUTOMOUNT_VERBOSE_DEFAULT	0
102 #define	AUTOMOUNTD_VERBOSE_DEFAULT	0
103 #define	AUTOMOUNT_NOBROWSE_DEFAULT	0
104 #define	AUTOMOUNT_TIMEOUT_DEFAULT	600
105 #define	AUTOMOUNT_TRACE_DEFAULT		0
106 /*
107  * Protocol Management functions
108  */
109 struct proto_option_defs {
110 	char *tag;
111 	char *name;	/* display name -- remove protocol identifier */
112 	int index;
113 	scf_type_t type;
114 	union {
115 	    int intval;
116 	    char *string;
117 	} defvalue;
118 	int32_t minval;
119 	int32_t maxval;
120 	int (*check)(int, char *);
121 } proto_options[] = {
122 #define	PROTO_OPT_AUTOMOUNT_TIMEOUT	0
123 	{ "timeout",
124 	    "timeout",	PROTO_OPT_AUTOMOUNT_TIMEOUT,
125 	    SCF_TYPE_INTEGER, AUTOMOUNT_TIMEOUT_DEFAULT,
126 	    1, INT32_MAX, range_check_validator},
127 #define	PROTO_OPT_AUTOMOUNT_VERBOSE	1
128 	{ "automount_verbose",
129 	    "automount_verbose", PROTO_OPT_AUTOMOUNT_VERBOSE,
130 	    SCF_TYPE_BOOLEAN, AUTOMOUNT_VERBOSE_DEFAULT, 0, 1,
131 	    true_false_validator},
132 #define	PROTO_OPT_AUTOMOUNTD_VERBOSE	2
133 	{ "automountd_verbose",
134 	    "automountd_verbose", PROTO_OPT_AUTOMOUNTD_VERBOSE,
135 	    SCF_TYPE_BOOLEAN, AUTOMOUNTD_VERBOSE_DEFAULT, 0, 1,
136 	    true_false_validator},
137 #define	PROTO_OPT_AUTOMOUNTD_NOBROWSE	3
138 	{ "nobrowse",
139 	    "nobrowse", PROTO_OPT_AUTOMOUNTD_NOBROWSE, SCF_TYPE_BOOLEAN,
140 	    AUTOMOUNT_NOBROWSE_DEFAULT, 0, 1, true_false_validator},
141 #define	PROTO_OPT_AUTOMOUNTD_TRACE	4
142 	{ "trace",
143 	    "trace", PROTO_OPT_AUTOMOUNTD_TRACE,
144 	    SCF_TYPE_INTEGER, AUTOMOUNT_TRACE_DEFAULT,
145 	    0, 20, range_check_validator},
146 #define	PROTO_OPT_AUTOMOUNTD_ENV	5
147 	{ "environment",
148 	    "environment", PROTO_OPT_AUTOMOUNTD_ENV, SCF_TYPE_ASTRING,
149 	    0, 0, 1024, strlen_validator},
150 	{NULL, NULL, 0, 0, 0, 0, 0, NULL}
151 };
152 
153 #define	AUTOFS_PROP_MAX	(sizeof (proto_options) / sizeof (proto_options[0]))
154 
155 static void
156 add_defaults()
157 {
158 	int i;
159 	char number[MAXDIGITS];
160 
161 	for (i = 0; proto_options[i].tag != NULL; i++) {
162 		sa_property_t prop;
163 		prop = sa_get_protocol_property(protoset,
164 		    proto_options[i].name);
165 		if (prop == NULL) {
166 			/* add the default value */
167 			switch (proto_options[i].type) {
168 			case SCF_TYPE_INTEGER:
169 				(void) snprintf(number, sizeof (number), "%d",
170 				    proto_options[i].defvalue.intval);
171 				prop = sa_create_property(proto_options[i].name,
172 				    number);
173 				break;
174 
175 			case SCF_TYPE_BOOLEAN:
176 				prop = sa_create_property(proto_options[i].name,
177 				    proto_options[i].defvalue.intval ?
178 				    "true" : "false");
179 				break;
180 
181 			default:
182 				/* treat as strings of zero length */
183 				prop = sa_create_property(proto_options[i].name,
184 				    "");
185 				break;
186 			}
187 			if (prop != NULL)
188 				(void) sa_add_protocol_property(protoset, prop);
189 		}
190 	}
191 }
192 
193 static int
194 autofs_init()
195 {
196 	int ret = SA_OK;
197 
198 	if (sa_plugin_ops.sa_init != autofs_init) {
199 		(void) printf(dgettext(TEXT_DOMAIN,
200 		    "AUTOFS plugin not installed properly\n"));
201 		return (SA_CONFIG_ERR);
202 	}
203 
204 	ret = initautofsprotofromsmf();
205 	if (ret != SA_OK) {
206 		(void) printf(dgettext(TEXT_DOMAIN,
207 		    "AUTOFS plugin problem with SMF properties: %s\n"),
208 		    sa_errorstr(ret));
209 		ret = SA_OK;
210 	}
211 	add_defaults();
212 	return (ret);
213 }
214 
215 static void
216 free_protoprops()
217 {
218 	if (protoset != NULL) {
219 		xmlFreeNode(protoset);
220 		protoset = NULL;
221 	}
222 }
223 
224 static void
225 autofs_fini()
226 {
227 	free_protoprops();
228 }
229 
230 static int
231 findprotoopt(char *propname)
232 {
233 	int i;
234 
235 	for (i = 0; proto_options[i].tag != NULL; i++)
236 		if (strcmp(proto_options[i].name, propname) == 0)
237 			return (i);
238 	return (-1);
239 }
240 
241 static int
242 autofs_validate_property(sa_handle_t handle, sa_property_t property,
243     sa_optionset_t parent)
244 {
245 	int ret = SA_OK;
246 	char *propname;
247 	int optionindex;
248 	char *value;
249 
250 #ifdef lint
251 	handle = handle;
252 	parent = parent;
253 #endif
254 	propname = sa_get_property(property, "type");
255 	if (propname == NULL)
256 		return (SA_NO_SUCH_PROP);
257 
258 	if ((optionindex = findprotoopt(propname)) < 0)
259 		ret = SA_NO_SUCH_PROP;
260 
261 	if (ret != SA_OK) {
262 		if (propname != NULL)
263 			sa_free_attr_string(propname);
264 		return (ret);
265 	}
266 
267 	value = sa_get_property_attr(property, "value");
268 	if (value != NULL) {
269 		/*
270 		 * If any property is added to AUTOFS, which is a different
271 		 * type than the below list, a case needs to be added for that
272 		 * to check the values. For now AUTOFS type are just integers,
273 		 * string and boolean properties. Just taking care of them.
274 		 */
275 		switch (proto_options[optionindex].type) {
276 		case SCF_TYPE_INTEGER:
277 		case SCF_TYPE_BOOLEAN:
278 		case SCF_TYPE_ASTRING:
279 			ret = proto_options[optionindex].check(optionindex,
280 			    value);
281 			break;
282 		default:
283 			break;
284 		}
285 	}
286 
287 	/* Free the value */
288 	if (value != NULL)
289 		sa_free_attr_string(value);
290 	if (propname != NULL)
291 		sa_free_attr_string(propname);
292 	return (ret);
293 }
294 
295 /*
296  * service_in_state(service, chkstate)
297  *
298  * Want to know if the specified service is in the desired state
299  * (chkstate) or not. Return true (1) if it is and false (0) if it
300  * isn't.
301  */
302 static int
303 service_in_state(char *service, const char *chkstate)
304 {
305 	char *state;
306 	int ret = B_FALSE;
307 
308 	state = smf_get_state(service);
309 	if (state != NULL) {
310 		/* got the state so get the equality for the return value */
311 		ret = strcmp(state, chkstate) == 0 ? B_TRUE : B_FALSE;
312 		free(state);
313 	}
314 	return (ret);
315 }
316 
317 static void
318 restart_service(char *service)
319 {
320 	int ret = -1;
321 
322 	/*
323 	 * Only attempt to restart the service if it is
324 	 * currently running. In the future, it may be
325 	 * desirable to use smf_refresh_instance if the AUTOFS
326 	 * services ever implement the refresh method.
327 	 */
328 	if (service_in_state(service, SCF_STATE_STRING_ONLINE)) {
329 		ret = smf_restart_instance(service);
330 		/*
331 		 * There are only a few SMF errors at this point, but
332 		 * it is also possible that a bad value may have put
333 		 * the service into maintenance if there wasn't an
334 		 * SMF level error.
335 		 */
336 		if (ret != 0) {
337 			(void) fprintf(stderr,
338 			    dgettext(TEXT_DOMAIN,
339 			    "%s failed to restart: %s\n"),
340 			    scf_strerror(scf_error()));
341 		} else {
342 			/*
343 			 * Check whether it has gone to "maintenance"
344 			 * mode or not. Maintenance implies something
345 			 * went wrong.
346 			 */
347 			if (service_in_state(service,
348 			    SCF_STATE_STRING_MAINT)) {
349 				(void) fprintf(stderr,
350 				    dgettext(TEXT_DOMAIN,
351 				    "%s failed to restart\n"),
352 				    service);
353 			}
354 		}
355 	}
356 }
357 
358 static int
359 is_a_number(char *number)
360 {
361 	int ret = 1;
362 	int hex = 0;
363 
364 	if (strncmp(number, "0x", 2) == 0) {
365 		number += 2;
366 		hex = 1;
367 	} else if (*number == '-') {
368 		number++; /* skip the minus */
369 	}
370 	while (ret == 1 && *number != '\0') {
371 		if (hex) {
372 			ret = isxdigit(*number++);
373 		} else {
374 			ret = isdigit(*number++);
375 		}
376 	}
377 	return (ret);
378 }
379 
380 /*
381  * fixcaselower(str)
382  *
383  * convert a string to lower case (inplace).
384  */
385 
386 static void
387 fixcaselower(char *str)
388 {
389 	while (*str) {
390 		*str = tolower(*str);
391 		str++;
392 	}
393 }
394 
395 /*
396  * skipwhitespace(str)
397  *
398  * Skip leading white space. It is assumed that it is called with a
399  * valid pointer.
400  */
401 static char *
402 skipwhitespace(char *str)
403 {
404 	while (*str && isspace(*str))
405 		str++;
406 
407 	return (str);
408 }
409 
410 /*
411  * extractprop()
412  *
413  * Extract the property and value out of the line and create the
414  * property in the optionset.
415  */
416 static int
417 extractprop(char *name, char *value)
418 {
419 	sa_property_t prop;
420 	int index;
421 	int ret = SA_OK;
422 	/*
423 	 * Remove any leading
424 	 * white space.
425 	 */
426 	name = skipwhitespace(name);
427 
428 	index = findprotoopt(name);
429 	if (index >= 0) {
430 		fixcaselower(name);
431 		prop = sa_create_property(proto_options[index].name, value);
432 		if (prop != NULL)
433 			ret = sa_add_protocol_property(protoset, prop);
434 		else
435 			ret = SA_NO_MEMORY;
436 	}
437 	return (ret);
438 }
439 
440 static int
441 initautofsprotofromsmf(void)
442 {
443 	char name[PATH_MAX];
444 	char value[PATH_MAX];
445 	int ret = SA_OK, bufsz = 0, i;
446 	char *instance = NULL;
447 	scf_type_t sctype;
448 
449 	protoset = sa_create_protocol_properties("autofs");
450 	if (protoset != NULL) {
451 		for (i = 0; proto_options[i].tag != NULL; i++) {
452 			bzero(value, PATH_MAX);
453 			(void) strncpy(name, proto_options[i].name, PATH_MAX);
454 			sctype = proto_options[i].type;
455 			bufsz = PATH_MAX;
456 			ret = autofs_smf_get_prop(name, value,
457 			    instance, sctype, AUTOFS_FMRI, &bufsz);
458 			if (ret == SA_OK) {
459 				ret = extractprop(name, value);
460 			}
461 		}
462 	} else {
463 		ret = SA_NO_MEMORY;
464 	}
465 	return (ret);
466 }
467 
468 static int
469 range_check_validator(int index, char *value)
470 {
471 	int ret = SA_OK;
472 	if (!is_a_number(value)) {
473 		ret = SA_BAD_VALUE;
474 	} else {
475 		int val;
476 		errno = 0;
477 		val = strtoul(value, NULL, 0);
478 		if (errno != 0)
479 			return (SA_BAD_VALUE);
480 
481 		if (val < proto_options[index].minval ||
482 		    val > proto_options[index].maxval)
483 			ret = SA_BAD_VALUE;
484 	}
485 	return (ret);
486 }
487 
488 static int
489 true_false_validator(int index, char *value)
490 {
491 
492 #ifdef lint
493 	index = index;
494 #endif
495 	if ((strcasecmp(value, "true") == 0) ||
496 	    (strcasecmp(value, "on") == 0) ||
497 	    (strcasecmp(value, "yes") == 0) ||
498 	    (strcmp(value, "1") == 0) ||
499 	    (strcasecmp(value, "false") == 0) ||
500 	    (strcasecmp(value, "off") == 0) ||
501 	    (strcasecmp(value, "no") == 0) ||
502 	    (strcmp(value, "0") == 0)) {
503 		return (SA_OK);
504 	}
505 	return (SA_BAD_VALUE);
506 }
507 
508 static int
509 strlen_validator(int index, char *value)
510 {
511 	int ret = SA_OK;
512 	if (value == NULL) {
513 		if (proto_options[index].minval == 0) {
514 			return (ret);
515 		} else {
516 			return (SA_BAD_VALUE);
517 		}
518 	}
519 	if (strlen(value) > proto_options[index].maxval ||
520 	    strlen(value) < proto_options[index].minval)
521 		ret = SA_BAD_VALUE;
522 	return (ret);
523 }
524 
525 static int
526 autofs_validate_proto_prop(int index, char *name, char *value)
527 {
528 #ifdef lint
529 	name = name;
530 #endif
531 	return (proto_options[index].check(index, value));
532 }
533 
534 static int
535 autofs_set_proto_prop(sa_property_t prop)
536 {
537 	int ret = SA_OK;
538 	char *name;
539 	char *value, *instance = NULL;
540 	scf_type_t sctype;
541 
542 	name = sa_get_property_attr(prop, "type");
543 	value = sa_get_property_attr(prop, "value");
544 	if (name != NULL && value != NULL) {
545 		int index = findprotoopt(name);
546 		if (index >= 0) {
547 			ret = autofs_validate_proto_prop(index, name, value);
548 			if (ret == SA_OK) {
549 				sctype = proto_options[index].type;
550 				if (sctype == SCF_TYPE_BOOLEAN) {
551 					if (value != NULL)
552 						sa_free_attr_string(value);
553 					if (string_to_boolean(value) == 0)
554 						value = strdup("0");
555 					else
556 						value = strdup("1");
557 				}
558 				ret = autofs_smf_set_prop(name, value,
559 				    instance, sctype, AUTOFS_FMRI);
560 				/*
561 				 * Make an instance based FMRI.
562 				 * For now its DEFAULT_AUTOFS_FMRI.
563 				 */
564 				if (ret == SA_OK)
565 					restart_service(AUTOFS_DEFAULT_FMRI);
566 			}
567 		} else {
568 			ret = SA_NO_SUCH_PROP;
569 		}
570 	} else {
571 		ret = SA_CONFIG_ERR;
572 	}
573 
574 	if (name != NULL)
575 		sa_free_attr_string(name);
576 	if (value != NULL)
577 		sa_free_attr_string(value);
578 	return (ret);
579 }
580 
581 
582 static sa_protocol_properties_t
583 autofs_get_proto_set(void)
584 {
585 	return (protoset);
586 }
587 
588 static uint64_t
589 autofs_features(void)
590 {
591 	return (0);
592 }
593 
594 static char *
595 autofs_get_status(void)
596 {
597 	return (smf_get_state(AUTOFS_DEFAULT_FMRI));
598 }
599