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