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