1/*
2 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 * Copyright 2012 Milan Jurik. All rights reserved.
5 * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
6 */
7
8/*
9 * BSD 3 Clause License
10 *
11 * Copyright (c) 2007, The Storage Networking Industry Association.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 	- Redistributions of source code must retain the above copyright
17 *	  notice, this list of conditions and the following disclaimer.
18 *
19 * 	- Redistributions in binary form must reproduce the above copyright
20 *	  notice, this list of conditions and the following disclaimer in
21 *	  the documentation and/or other materials provided with the
22 *	  distribution.
23 *
24 *	- Neither the name of The Storage Networking Industry Association (SNIA)
25 *	  nor the names of its contributors may be used to endorse or promote
26 *	  products derived from this software without specific prior written
27 *	  permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
30 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
33 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
34 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
35 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
36 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
37 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39 * POSSIBILITY OF SUCH DAMAGE.
40 */
41
42/*
43 * NDMP configuration management
44 */
45#include <stdio.h>
46#include <stdlib.h>
47#include <synch.h>
48#include <libintl.h>
49#include <strings.h>
50#include <libndmp.h>
51
52/* NDMP properties configuration */
53#define	NDMP_GROUP_FMRI_PREFIX	"system/ndmpd"
54#define	NDMP_INST		"svc:/system/ndmpd:default"
55#define	NDMP_PROP_LEN		600
56static char *ndmp_pg[] = {
57	"ndmpd",
58	"read"
59};
60#define	NPG	(sizeof (ndmp_pg) / sizeof (ndmp_pg[0]))
61
62/* Handle Init states */
63#define	NDMP_SCH_STATE_UNINIT		0
64#define	NDMP_SCH_STATE_INITIALIZING	1
65#define	NDMP_SCH_STATE_INIT		2
66
67/* NDMP scf handle structure */
68typedef struct ndmp_scfhandle {
69	scf_handle_t *scf_handle;
70	int scf_state;
71	scf_service_t *scf_service;
72	scf_scope_t *scf_scope;
73	scf_transaction_t *scf_trans;
74	scf_propertygroup_t *scf_pg;
75} ndmp_scfhandle_t;
76
77static int ndmp_config_saveenv(ndmp_scfhandle_t *, boolean_t);
78static ndmp_scfhandle_t *ndmp_smf_scf_init(const char *);
79static void ndmp_smf_scf_fini(ndmp_scfhandle_t *);
80static int ndmp_smf_start_transaction(ndmp_scfhandle_t *);
81static int ndmp_smf_end_transaction(ndmp_scfhandle_t *, boolean_t);
82static int ndmp_smf_set_property(ndmp_scfhandle_t *, const char *,
83		const char *);
84static int ndmp_smf_get_property(ndmp_scfhandle_t *, const char *, char *,
85		size_t);
86static int ndmp_smf_create_service_pgroup(ndmp_scfhandle_t *, const char *);
87static int ndmp_smf_delete_property(ndmp_scfhandle_t *, const char *);
88static int ndmp_smf_get_pg_name(ndmp_scfhandle_t *, const char *, char **);
89
90/*
91 * This routine send a refresh signal to ndmpd service which cause ndmpd
92 * property table to be refeshed with current ndmpd properties value from SMF.
93 */
94int
95ndmp_service_refresh(void)
96{
97	int rc = smf_refresh_instance(NDMP_INST);
98
99	if (rc != 0)
100		ndmp_errno = ENDMP_SMF_INTERNAL;
101	return (rc);
102}
103
104/*
105 * Returns value of the specified variable/property. The return value is a
106 * string pointer to the locally allocated memory if the config param is
107 * defined otherwise it would be NULL.
108 */
109int
110ndmp_get_prop(const char *prop, char **value)
111{
112	ndmp_scfhandle_t *handle;
113	char *lval;
114	char *pgname;
115
116	*value = NULL;
117	if ((handle = ndmp_smf_scf_init(NDMP_GROUP_FMRI_PREFIX)) == NULL) {
118		return (-1);
119	}
120	if (ndmp_smf_get_pg_name(handle, prop, &pgname)) {
121		ndmp_smf_scf_fini(handle);
122		ndmp_errno = ENDMP_SMF_PROP_GRP;
123		return (-1);
124	}
125	if (ndmp_smf_create_service_pgroup(handle, pgname)) {
126		ndmp_smf_scf_fini(handle);
127		return (-1);
128	}
129	if ((lval = malloc(NDMP_PROP_LEN)) == NULL) {
130		ndmp_smf_scf_fini(handle);
131		ndmp_errno = ENDMP_MEM_ALLOC;
132		return (-1);
133	}
134	if (ndmp_smf_get_property(handle, prop, lval, NDMP_PROP_LEN) != 0) {
135		ndmp_smf_scf_fini(handle);
136		free(lval);
137		ndmp_errno = ENDMP_SMF_PROP;
138		return (-1);
139	}
140	*value = lval;
141	ndmp_smf_scf_fini(handle);
142	return (0);
143}
144
145int
146ndmp_set_prop(const char *env, const char *env_val)
147{
148	ndmp_scfhandle_t *handle;
149	char *pgname;
150	int rc;
151
152	if ((handle = ndmp_smf_scf_init(NDMP_GROUP_FMRI_PREFIX)) == NULL)
153		return (-1);
154
155	if (ndmp_smf_get_pg_name(handle, env, &pgname)) {
156		ndmp_smf_scf_fini(handle);
157		ndmp_errno = ENDMP_SMF_PROP_GRP;
158		return (-1);
159	}
160
161	if (ndmp_smf_create_service_pgroup(handle, pgname)) {
162		ndmp_smf_scf_fini(handle);
163		return (-1);
164	}
165
166	if (ndmp_smf_start_transaction(handle)) {
167		ndmp_smf_scf_fini(handle);
168		return (-1);
169	}
170
171	if (env_val)
172		rc = ndmp_smf_set_property(handle, env, env_val);
173	else
174		rc = ndmp_smf_delete_property(handle, env);
175
176	if (ndmp_config_saveenv(handle, (rc == 0)) == 0)
177		return (rc);
178	else
179		return (-1);
180}
181
182static int
183ndmp_smf_get_pg_name(ndmp_scfhandle_t *h, const char *pname, char **pgname)
184{
185	scf_value_t *value;
186	scf_property_t *prop;
187	int i;
188
189	for (i = 0; i < NPG; i++) {
190		if (scf_service_get_pg(h->scf_service, ndmp_pg[i],
191		    h->scf_pg) != 0)
192			return (-1);
193
194		if ((value = scf_value_create(h->scf_handle)) == NULL)
195			return (-1);
196
197		if ((prop = scf_property_create(h->scf_handle)) == NULL) {
198			scf_value_destroy(value);
199			return (-1);
200		}
201		/*
202		 * This will fail if property does not exist in the property
203		 * group. Check the next property group in case of failure.
204		 */
205		if ((scf_pg_get_property(h->scf_pg, pname, prop)) != 0) {
206			scf_value_destroy(value);
207			scf_property_destroy(prop);
208			continue;
209		}
210
211		*pgname = ndmp_pg[i];
212		scf_value_destroy(value);
213		scf_property_destroy(prop);
214		return (0);
215	}
216	return (-1);
217}
218
219/*
220 * Basically commit the transaction.
221 */
222static int
223ndmp_config_saveenv(ndmp_scfhandle_t *handle, boolean_t commit)
224{
225	int ret = 0;
226
227	ret = ndmp_smf_end_transaction(handle, commit);
228
229	ndmp_smf_scf_fini(handle);
230	return (ret);
231}
232
233/*
234 * Must be called when done. Called with the handle allocated in
235 * ndmp_smf_scf_init(), it cleans up the state and frees any SCF resources
236 * still in use.
237 */
238static void
239ndmp_smf_scf_fini(ndmp_scfhandle_t *handle)
240{
241	if (handle == NULL)
242		return;
243
244	scf_scope_destroy(handle->scf_scope);
245	scf_service_destroy(handle->scf_service);
246	scf_pg_destroy(handle->scf_pg);
247	handle->scf_state = NDMP_SCH_STATE_UNINIT;
248	(void) scf_handle_unbind(handle->scf_handle);
249	scf_handle_destroy(handle->scf_handle);
250	free(handle);
251}
252
253/*
254 * Must be called before using any of the SCF functions. Returns
255 * ndmp_scfhandle_t pointer if success.
256 */
257static ndmp_scfhandle_t *
258ndmp_smf_scf_init(const char *svc_name)
259{
260	ndmp_scfhandle_t *handle;
261
262	handle = (ndmp_scfhandle_t *)calloc(1, sizeof (ndmp_scfhandle_t));
263	if (handle != NULL) {
264		handle->scf_state = NDMP_SCH_STATE_INITIALIZING;
265		if (((handle->scf_handle =
266		    scf_handle_create(SCF_VERSION)) != NULL) &&
267		    (scf_handle_bind(handle->scf_handle) == 0)) {
268			if ((handle->scf_scope =
269			    scf_scope_create(handle->scf_handle)) == NULL)
270				goto err;
271
272			if (scf_handle_get_local_scope(handle->scf_handle,
273			    handle->scf_scope) != 0)
274				goto err;
275
276			if ((handle->scf_service =
277			    scf_service_create(handle->scf_handle)) == NULL)
278				goto err;
279
280			if (scf_scope_get_service(handle->scf_scope, svc_name,
281			    handle->scf_service) != SCF_SUCCESS)
282				goto err;
283
284			if ((handle->scf_pg =
285			    scf_pg_create(handle->scf_handle)) == NULL)
286				goto err;
287
288			handle->scf_state = NDMP_SCH_STATE_INIT;
289		} else {
290			goto err;
291		}
292	} else {
293		ndmp_errno = ENDMP_MEM_ALLOC;
294		handle = NULL;
295	}
296	return (handle);
297
298	/* Error handling/unwinding */
299err:
300	(void) ndmp_smf_scf_fini(handle);
301	ndmp_errno = ENDMP_SMF_INTERNAL;
302	return (NULL);
303}
304
305/*
306 * Create a new property group at service level.
307 */
308static int
309ndmp_smf_create_service_pgroup(ndmp_scfhandle_t *handle, const char *pgroup)
310{
311	int err;
312
313	/*
314	 * Only create a handle if it doesn't exist. It is ok to exist since
315	 * the pg handle will be set as a side effect.
316	 */
317	if (handle->scf_pg == NULL) {
318		if ((handle->scf_pg =
319		    scf_pg_create(handle->scf_handle)) == NULL) {
320			ndmp_errno = ENDMP_SMF_INTERNAL;
321			return (-1);
322		}
323	}
324
325	/*
326	 * If the pgroup exists, we are done. If it doesn't, then we need to
327	 * actually add one to the service instance.
328	 */
329	if (scf_service_get_pg(handle->scf_service,
330	    pgroup, handle->scf_pg) != 0) {
331		/* Doesn't exist so create one */
332		if (scf_service_add_pg(handle->scf_service, pgroup,
333		    SCF_GROUP_FRAMEWORK, 0, handle->scf_pg) != 0) {
334			err = scf_error();
335			switch (err) {
336			case SCF_ERROR_PERMISSION_DENIED:
337				ndmp_errno = ENDMP_SMF_PERM;
338				return (-1);
339			default:
340				ndmp_errno = ENDMP_SMF_INTERNAL;
341				return (-1);
342			}
343		}
344	}
345	return (0);
346}
347
348/*
349 * Start transaction on current pg in handle. The pg could be service or
350 * instance level. Must be called after pg handle is obtained from create or
351 * get.
352 */
353static int
354ndmp_smf_start_transaction(ndmp_scfhandle_t *handle)
355{
356	/*
357	 * Lookup the property group and create it if it doesn't already
358	 * exist.
359	 */
360	if (handle->scf_state == NDMP_SCH_STATE_INIT) {
361		if ((handle->scf_trans =
362		    scf_transaction_create(handle->scf_handle)) != NULL) {
363			if (scf_transaction_start(handle->scf_trans,
364			    handle->scf_pg) != 0) {
365				scf_transaction_destroy(handle->scf_trans);
366				handle->scf_trans = NULL;
367				ndmp_errno = ENDMP_SMF_INTERNAL;
368				return (-1);
369			}
370		} else {
371			ndmp_errno = ENDMP_SMF_INTERNAL;
372			return (-1);
373		}
374	}
375	if (scf_error() == SCF_ERROR_PERMISSION_DENIED) {
376		ndmp_errno = ENDMP_SMF_PERM;
377		return (-1);
378	}
379
380	return (0);
381}
382
383/*
384 * Commit the changes that were added to the transaction in the handle. Do all
385 * necessary cleanup.
386 */
387static int
388ndmp_smf_end_transaction(ndmp_scfhandle_t *handle, boolean_t commit)
389{
390	int rc = 0;
391
392	if (commit) {
393		if (scf_transaction_commit(handle->scf_trans) < 0) {
394			ndmp_errno = ENDMP_SMF_INTERNAL;
395			rc = -1;
396		}
397	}
398
399	scf_transaction_destroy_children(handle->scf_trans);
400	scf_transaction_destroy(handle->scf_trans);
401	handle->scf_trans = NULL;
402
403	return (rc);
404}
405
406/*
407 * Deletes property in current pg
408 */
409static int
410ndmp_smf_delete_property(ndmp_scfhandle_t *handle, const char *propname)
411{
412	scf_transaction_entry_t *entry = NULL;
413
414	/*
415	 * Properties must be set in transactions and don't take effect until
416	 * the transaction has been ended/committed.
417	 */
418	if ((entry = scf_entry_create(handle->scf_handle)) != NULL) {
419		if (scf_transaction_property_delete(handle->scf_trans, entry,
420		    propname) != 0) {
421			scf_entry_destroy(entry);
422			ndmp_errno = ENDMP_SMF_INTERNAL;
423			return (-1);
424		}
425	} else {
426		ndmp_errno = ENDMP_SMF_INTERNAL;
427		return (-1);
428	}
429	if ((scf_error()) == SCF_ERROR_PERMISSION_DENIED) {
430		ndmp_errno = ENDMP_SMF_PERM;
431		scf_entry_destroy(entry);
432		return (-1);
433	}
434
435	return (0);
436}
437
438/*
439 * Sets property in current pg
440 */
441static int
442ndmp_smf_set_property(ndmp_scfhandle_t *handle, const char *propname,
443    const char *valstr)
444{
445	int ret = 0;
446	scf_value_t *value = NULL;
447	scf_transaction_entry_t *entry = NULL;
448	scf_property_t *prop = NULL;
449	scf_type_t type;
450	int64_t valint;
451	uint8_t valbool;
452
453	/*
454	 * Properties must be set in transactions and don't take effect until
455	 * the transaction has been ended/committed.
456	 */
457	if (((value = scf_value_create(handle->scf_handle)) == NULL) ||
458	    ((entry = scf_entry_create(handle->scf_handle)) == NULL) ||
459	    ((prop = scf_property_create(handle->scf_handle)) == NULL) ||
460	    (scf_pg_get_property(handle->scf_pg, propname, prop) != 0) ||
461	    (scf_property_get_value(prop, value) != 0)) {
462		ret = -1;
463		goto out;
464	}
465
466	type = scf_value_type(value);
467	if ((scf_transaction_property_change(handle->scf_trans, entry, propname,
468	    type) != 0) &&
469	    (scf_transaction_property_new(handle->scf_trans, entry, propname,
470	    type) != 0)) {
471		ret = -1;
472		goto out;
473	}
474
475	switch (type) {
476	case SCF_TYPE_ASTRING:
477		if ((scf_value_set_astring(value, valstr)) != SCF_SUCCESS)
478			ret = -1;
479		break;
480	case SCF_TYPE_INTEGER:
481		valint = strtoll(valstr, 0, 0);
482		scf_value_set_integer(value, valint);
483		break;
484	case SCF_TYPE_BOOLEAN:
485		if (strncmp(valstr, "yes", 3))
486			valbool = 0;
487		else
488			valbool = 1;
489		scf_value_set_boolean(value, valbool);
490		break;
491	default:
492		ret = -1;
493	}
494	if (scf_entry_add_value(entry, value) == 0) {
495		/* The value is in the transaction */
496		value = NULL;
497	} else {
498		ret = -1;
499	}
500	/* The entry is in the transaction */
501	entry = NULL;
502
503out:
504	if (ret == -1) {
505		if ((scf_error() == SCF_ERROR_PERMISSION_DENIED))
506			ndmp_errno = ENDMP_SMF_PERM;
507		else
508			ndmp_errno = ENDMP_SMF_INTERNAL;
509	}
510	scf_property_destroy(prop);
511	scf_value_destroy(value);
512	scf_entry_destroy(entry);
513	return (ret);
514}
515
516/*
517 * Gets a property value.upto sz size. Caller is responsible to have enough
518 * memory allocated.
519 */
520static int
521ndmp_smf_get_property(ndmp_scfhandle_t *handle, const char *propname,
522    char *valstr, size_t sz)
523{
524	int ret = 0;
525	scf_value_t *value = NULL;
526	scf_property_t *prop = NULL;
527	scf_type_t type;
528	int64_t valint;
529	uint8_t valbool;
530	char valstrbuf[NDMP_PROP_LEN];
531
532	if (((value = scf_value_create(handle->scf_handle)) != NULL) &&
533	    ((prop = scf_property_create(handle->scf_handle)) != NULL) &&
534	    (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) {
535		if (scf_property_get_value(prop, value) == 0) {
536			type = scf_value_type(value);
537			switch (type) {
538			case SCF_TYPE_ASTRING:
539				if (scf_value_get_astring(value, valstr,
540				    sz) < 0) {
541					ret = -1;
542				}
543				break;
544			case SCF_TYPE_INTEGER:
545				if (scf_value_get_integer(value,
546				    &valint) != 0) {
547					ret = -1;
548					break;
549				}
550				valstrbuf[NDMP_PROP_LEN - 1] = '\0';
551				(void) strncpy(valstr, lltostr(valint,
552				    &valstrbuf[NDMP_PROP_LEN - 1]),
553				    NDMP_PROP_LEN);
554				break;
555			case SCF_TYPE_BOOLEAN:
556				if (scf_value_get_boolean(value,
557				    &valbool) != 0) {
558					ret = -1;
559					break;
560				}
561				if (valbool == 1)
562					(void) strncpy(valstr, "yes", 4);
563				else
564					(void) strncpy(valstr, "no", 3);
565				break;
566			default:
567				ret = -1;
568			}
569		} else {
570			ret = -1;
571		}
572	} else {
573		ret = -1;
574	}
575	scf_value_destroy(value);
576	scf_property_destroy(prop);
577	return (ret);
578}
579