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
51static int autofs_init();
52static void autofs_fini();
53static int autofs_validate_property(sa_handle_t, sa_property_t, sa_optionset_t);
54static int autofs_set_proto_prop(sa_property_t);
55static sa_protocol_properties_t autofs_get_proto_set();
56static char *autofs_get_status();
57static uint64_t autofs_features();
58
59static int initautofsprotofromsmf();
60static int true_false_validator(int index, char *value);
61static int strlen_validator(int index, char *value);
62static 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 */
68struct 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
99static 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 */
109struct 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	    NULL, 0, 1024, strlen_validator},
150	{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
151};
152
153#define	AUTOFS_PROP_MAX	(sizeof (proto_options) / sizeof (proto_options[0]))
154
155static void
156add_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
193static int
194autofs_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
215static void
216free_protoprops()
217{
218	if (protoset != NULL) {
219		xmlFreeNode(protoset);
220		protoset = NULL;
221	}
222}
223
224static void
225autofs_fini()
226{
227	free_protoprops();
228}
229
230static int
231findprotoopt(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
241static int
242autofs_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 */
302static int
303service_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
317static void
318restart_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
358static int
359is_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
386static void
387fixcaselower(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 */
401static char *
402skipwhitespace(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 */
416static int
417extractprop(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
440static int
441initautofsprotofromsmf(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
468static int
469range_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
488static int
489true_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
508static int
509strlen_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
525static int
526autofs_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
534static int
535autofs_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
582static sa_protocol_properties_t
583autofs_get_proto_set(void)
584{
585	return (protoset);
586}
587
588static uint64_t
589autofs_features(void)
590{
591	return (0);
592}
593
594static char *
595autofs_get_status(void)
596{
597	char *state = NULL;
598	state = smf_get_state(AUTOFS_DEFAULT_FMRI);
599	return (state != NULL ? state : "-");
600}
601