xref: /illumos-gate/usr/src/lib/libndmp/common/libndmp_prop.c (revision 843c398e8904ed9d833d2af3103894f909fb4b52)
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
56 static 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 */
68 typedef 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 
77 static int ndmp_config_saveenv(ndmp_scfhandle_t *, boolean_t);
78 static ndmp_scfhandle_t *ndmp_smf_scf_init(const char *);
79 static void ndmp_smf_scf_fini(ndmp_scfhandle_t *);
80 static int ndmp_smf_start_transaction(ndmp_scfhandle_t *);
81 static int ndmp_smf_end_transaction(ndmp_scfhandle_t *, boolean_t);
82 static int ndmp_smf_set_property(ndmp_scfhandle_t *, const char *,
83 		const char *);
84 static int ndmp_smf_get_property(ndmp_scfhandle_t *, const char *, char *,
85 		size_t);
86 static int ndmp_smf_create_service_pgroup(ndmp_scfhandle_t *, const char *);
87 static int ndmp_smf_delete_property(ndmp_scfhandle_t *, const char *);
88 static 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  */
94 int
95 ndmp_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  */
109 int
110 ndmp_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 
145 int
146 ndmp_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 
182 static int
183 ndmp_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  */
222 static int
223 ndmp_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  */
238 static void
239 ndmp_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  */
257 static ndmp_scfhandle_t *
258 ndmp_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 */
299 err:
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  */
308 static int
309 ndmp_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 	 * If the pgroup exists, we are done. If it doesn't, then we need to
326 	 * actually add one to the service instance.
327 	 */
328 	if (scf_service_get_pg(handle->scf_service,
329 	    pgroup, handle->scf_pg) != 0) {
330 		/* Doesn't exist so create one */
331 		if (scf_service_add_pg(handle->scf_service, pgroup,
332 		    SCF_GROUP_FRAMEWORK, 0, handle->scf_pg) != 0) {
333 			err = scf_error();
334 			switch (err) {
335 			case SCF_ERROR_PERMISSION_DENIED:
336 				ndmp_errno = ENDMP_SMF_PERM;
337 				return (-1);
338 			default:
339 				ndmp_errno = ENDMP_SMF_INTERNAL;
340 				return (-1);
341 			}
342 		}
343 	}
344 	return (0);
345 }
346 
347 /*
348  * Start transaction on current pg in handle. The pg could be service or
349  * instance level. Must be called after pg handle is obtained from create or
350  * get.
351  */
352 static int
353 ndmp_smf_start_transaction(ndmp_scfhandle_t *handle)
354 {
355 	/*
356 	 * Lookup the property group and create it if it doesn't already
357 	 * exist.
358 	 */
359 	if (handle->scf_state == NDMP_SCH_STATE_INIT) {
360 		if ((handle->scf_trans =
361 		    scf_transaction_create(handle->scf_handle)) != NULL) {
362 			if (scf_transaction_start(handle->scf_trans,
363 			    handle->scf_pg) != 0) {
364 				scf_transaction_destroy(handle->scf_trans);
365 				handle->scf_trans = NULL;
366 				ndmp_errno = ENDMP_SMF_INTERNAL;
367 				return (-1);
368 			}
369 		} else {
370 			ndmp_errno = ENDMP_SMF_INTERNAL;
371 			return (-1);
372 		}
373 	}
374 	if (scf_error() == SCF_ERROR_PERMISSION_DENIED) {
375 		ndmp_errno = ENDMP_SMF_PERM;
376 		return (-1);
377 	}
378 
379 	return (0);
380 }
381 
382 /*
383  * Commit the changes that were added to the transaction in the handle. Do all
384  * necessary cleanup.
385  */
386 static int
387 ndmp_smf_end_transaction(ndmp_scfhandle_t *handle, boolean_t commit)
388 {
389 	int rc = 0;
390 
391 	if (commit) {
392 		if (scf_transaction_commit(handle->scf_trans) < 0) {
393 			ndmp_errno = ENDMP_SMF_INTERNAL;
394 			rc = -1;
395 		}
396 	}
397 
398 	scf_transaction_destroy_children(handle->scf_trans);
399 	scf_transaction_destroy(handle->scf_trans);
400 	handle->scf_trans = NULL;
401 
402 	return (rc);
403 }
404 
405 /*
406  * Deletes property in current pg
407  */
408 static int
409 ndmp_smf_delete_property(ndmp_scfhandle_t *handle, const char *propname)
410 {
411 	scf_transaction_entry_t *entry = NULL;
412 
413 	/*
414 	 * Properties must be set in transactions and don't take effect until
415 	 * the transaction has been ended/committed.
416 	 */
417 	if ((entry = scf_entry_create(handle->scf_handle)) != NULL) {
418 		if (scf_transaction_property_delete(handle->scf_trans, entry,
419 		    propname) != 0) {
420 			scf_entry_destroy(entry);
421 			ndmp_errno = ENDMP_SMF_INTERNAL;
422 			return (-1);
423 		}
424 	} else {
425 		ndmp_errno = ENDMP_SMF_INTERNAL;
426 		return (-1);
427 	}
428 	if ((scf_error()) == SCF_ERROR_PERMISSION_DENIED) {
429 		ndmp_errno = ENDMP_SMF_PERM;
430 		scf_entry_destroy(entry);
431 		return (-1);
432 	}
433 
434 	return (0);
435 }
436 
437 /*
438  * Sets property in current pg
439  */
440 static int
441 ndmp_smf_set_property(ndmp_scfhandle_t *handle, const char *propname,
442     const char *valstr)
443 {
444 	int ret = 0;
445 	scf_value_t *value = NULL;
446 	scf_transaction_entry_t *entry = NULL;
447 	scf_property_t *prop = NULL;
448 	scf_type_t type;
449 	int64_t valint;
450 	uint8_t valbool;
451 
452 	/*
453 	 * Properties must be set in transactions and don't take effect until
454 	 * the transaction has been ended/committed.
455 	 */
456 	if (((value = scf_value_create(handle->scf_handle)) == NULL) ||
457 	    ((entry = scf_entry_create(handle->scf_handle)) == NULL) ||
458 	    ((prop = scf_property_create(handle->scf_handle)) == NULL) ||
459 	    (scf_pg_get_property(handle->scf_pg, propname, prop) != 0) ||
460 	    (scf_property_get_value(prop, value) != 0)) {
461 		ret = -1;
462 		goto out;
463 	}
464 
465 	type = scf_value_type(value);
466 	if ((scf_transaction_property_change(handle->scf_trans, entry, propname,
467 	    type) != 0) &&
468 	    (scf_transaction_property_new(handle->scf_trans, entry, propname,
469 	    type) != 0)) {
470 		ret = -1;
471 		goto out;
472 	}
473 
474 	switch (type) {
475 	case SCF_TYPE_ASTRING:
476 		if ((scf_value_set_astring(value, valstr)) != SCF_SUCCESS)
477 			ret = -1;
478 		break;
479 	case SCF_TYPE_INTEGER:
480 		valint = strtoll(valstr, 0, 0);
481 		scf_value_set_integer(value, valint);
482 		break;
483 	case SCF_TYPE_BOOLEAN:
484 		if (strncmp(valstr, "yes", 3))
485 			valbool = 0;
486 		else
487 			valbool = 1;
488 		scf_value_set_boolean(value, valbool);
489 		break;
490 	default:
491 		ret = -1;
492 	}
493 	if (scf_entry_add_value(entry, value) == 0) {
494 		/* The value is in the transaction */
495 		value = NULL;
496 	} else {
497 		ret = -1;
498 	}
499 	/* The entry is in the transaction */
500 	entry = NULL;
501 
502 out:
503 	if (ret == -1) {
504 		if ((scf_error() == SCF_ERROR_PERMISSION_DENIED))
505 			ndmp_errno = ENDMP_SMF_PERM;
506 		else
507 			ndmp_errno = ENDMP_SMF_INTERNAL;
508 	}
509 	scf_property_destroy(prop);
510 	scf_value_destroy(value);
511 	scf_entry_destroy(entry);
512 	return (ret);
513 }
514 
515 /*
516  * Gets a property value.upto sz size. Caller is responsible to have enough
517  * memory allocated.
518  */
519 static int
520 ndmp_smf_get_property(ndmp_scfhandle_t *handle, const char *propname,
521     char *valstr, size_t sz)
522 {
523 	int ret = 0;
524 	scf_value_t *value = NULL;
525 	scf_property_t *prop = NULL;
526 	scf_type_t type;
527 	int64_t valint;
528 	uint8_t valbool;
529 	char valstrbuf[NDMP_PROP_LEN];
530 
531 	if (((value = scf_value_create(handle->scf_handle)) != NULL) &&
532 	    ((prop = scf_property_create(handle->scf_handle)) != NULL) &&
533 	    (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) {
534 		if (scf_property_get_value(prop, value) == 0) {
535 			type = scf_value_type(value);
536 			switch (type) {
537 			case SCF_TYPE_ASTRING:
538 				if (scf_value_get_astring(value, valstr,
539 				    sz) < 0) {
540 					ret = -1;
541 				}
542 				break;
543 			case SCF_TYPE_INTEGER:
544 				if (scf_value_get_integer(value,
545 				    &valint) != 0) {
546 					ret = -1;
547 					break;
548 				}
549 				valstrbuf[NDMP_PROP_LEN - 1] = '\0';
550 				(void) strncpy(valstr, lltostr(valint,
551 				    &valstrbuf[NDMP_PROP_LEN - 1]),
552 				    NDMP_PROP_LEN);
553 				break;
554 			case SCF_TYPE_BOOLEAN:
555 				if (scf_value_get_boolean(value,
556 				    &valbool) != 0) {
557 					ret = -1;
558 					break;
559 				}
560 				if (valbool == 1)
561 					(void) strncpy(valstr, "yes", 4);
562 				else
563 					(void) strncpy(valstr, "no", 3);
564 				break;
565 			default:
566 				ret = -1;
567 			}
568 		} else {
569 			ret = -1;
570 		}
571 	} else {
572 		ret = -1;
573 	}
574 	scf_value_destroy(value);
575 	scf_property_destroy(prop);
576 	return (ret);
577 }
578