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 2014 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 *);
78 static ndmp_scfhandle_t *ndmp_smf_scf_init(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 *);
82 static int ndmp_smf_set_property(ndmp_scfhandle_t *, char *, char *);
83 static int ndmp_smf_get_property(ndmp_scfhandle_t *, char *, char *, size_t);
84 static int ndmp_smf_create_service_pgroup(ndmp_scfhandle_t *, char *);
85 static int ndmp_smf_delete_property(ndmp_scfhandle_t *, char *);
86 static int ndmp_smf_get_pg_name(ndmp_scfhandle_t *, char *, char **);
87 
88 /*
89  * This routine send a refresh signal to ndmpd service which cause ndmpd
90  * property table to be refeshed with current ndmpd properties value from SMF.
91  */
92 int
93 ndmp_service_refresh(void)
94 {
95 	if ((smf_get_state(NDMP_INST)) != NULL)
96 		return (smf_refresh_instance(NDMP_INST));
97 
98 	ndmp_errno = ENDMP_SMF_INTERNAL;
99 	return (-1);
100 }
101 
102 /*
103  * Returns value of the specified variable/property. The return value is a
104  * string pointer to the locally allocated memory if the config param is
105  * defined otherwise it would be NULL.
106  */
107 int
108 ndmp_get_prop(char *prop, char **value)
109 {
110 	ndmp_scfhandle_t *handle = NULL;
111 	char *lval = (char *)malloc(NDMP_PROP_LEN);
112 	char *pgname;
113 
114 	if (!lval) {
115 		ndmp_errno = ENDMP_MEM_ALLOC;
116 		return (-1);
117 	}
118 	if ((handle = ndmp_smf_scf_init(NDMP_GROUP_FMRI_PREFIX)) == NULL) {
119 		free(lval);
120 		return (-1);
121 	}
122 	if (ndmp_smf_get_pg_name(handle, prop, &pgname)) {
123 		free(lval);
124 		ndmp_errno = ENDMP_SMF_PROP_GRP;
125 		return (-1);
126 	}
127 	if (ndmp_smf_create_service_pgroup(handle, pgname)) {
128 		ndmp_smf_scf_fini(handle);
129 		free(lval);
130 		return (-1);
131 	}
132 	if (ndmp_smf_get_property(handle, prop, lval, NDMP_PROP_LEN) != 0) {
133 		ndmp_smf_scf_fini(handle);
134 		free(lval);
135 		ndmp_errno = ENDMP_SMF_PROP;
136 		return (-1);
137 	}
138 	*value = lval;
139 	ndmp_smf_scf_fini(handle);
140 	return (0);
141 }
142 
143 int
144 ndmp_set_prop(char *env, char *env_val)
145 {
146 	ndmp_scfhandle_t *handle = NULL;
147 	char *pgname;
148 
149 	if ((handle = ndmp_smf_scf_init(NDMP_GROUP_FMRI_PREFIX)) == NULL)
150 		return (-1);
151 
152 	if (ndmp_smf_get_pg_name(handle, env, &pgname)) {
153 		ndmp_errno = ENDMP_SMF_PROP_GRP;
154 		return (-1);
155 	}
156 
157 	if (ndmp_smf_create_service_pgroup(handle, pgname))
158 		return (-1);
159 
160 	if (ndmp_smf_start_transaction(handle))
161 		return (-1);
162 
163 	if (env_val) {
164 		if (ndmp_smf_set_property(handle, env, env_val)) {
165 			return (-1);
166 		}
167 	} else {
168 		if (ndmp_smf_delete_property(handle, env))
169 			return (-1);
170 	}
171 
172 	if (ndmp_config_saveenv(handle) != 0)
173 		return (-1);
174 
175 	return (0);
176 }
177 
178 static int
179 ndmp_smf_get_pg_name(ndmp_scfhandle_t *h, char *pname, char **pgname)
180 {
181 	scf_value_t *value;
182 	scf_property_t *prop;
183 	int i;
184 
185 	for (i = 0; i < NPG; i++) {
186 		if (scf_service_get_pg(h->scf_service, ndmp_pg[i],
187 		    h->scf_pg) != 0)
188 			return (-1);
189 
190 		if ((value = scf_value_create(h->scf_handle)) == NULL)
191 			return (-1);
192 
193 		if ((prop = scf_property_create(h->scf_handle)) == NULL) {
194 			scf_value_destroy(value);
195 			return (-1);
196 		}
197 		/*
198 		 * This will fail if property does not exist in the property
199 		 * group. Check the next property group in case of failure.
200 		 */
201 		if ((scf_pg_get_property(h->scf_pg, pname, prop)) != 0) {
202 			scf_value_destroy(value);
203 			scf_property_destroy(prop);
204 			continue;
205 		}
206 
207 		*pgname = ndmp_pg[i];
208 		scf_value_destroy(value);
209 		scf_property_destroy(prop);
210 		return (0);
211 	}
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 			default:
334 				ndmp_errno = ENDMP_SMF_INTERNAL;
335 				return (-1);
336 			}
337 		}
338 	}
339 	return (0);
340 }
341 
342 /*
343  * Start transaction on current pg in handle. The pg could be service or
344  * instance level. Must be called after pg handle is obtained from create or
345  * get.
346  */
347 static int
348 ndmp_smf_start_transaction(ndmp_scfhandle_t *handle)
349 {
350 	/*
351 	 * Lookup the property group and create it if it doesn't already
352 	 * exist.
353 	 */
354 	if (handle->scf_state == NDMP_SCH_STATE_INIT) {
355 		if ((handle->scf_trans =
356 		    scf_transaction_create(handle->scf_handle)) != NULL) {
357 			if (scf_transaction_start(handle->scf_trans,
358 			    handle->scf_pg) != 0) {
359 				scf_transaction_destroy(handle->scf_trans);
360 				handle->scf_trans = NULL;
361 				ndmp_errno = ENDMP_SMF_INTERNAL;
362 				return (-1);
363 			}
364 		} else {
365 			ndmp_errno = ENDMP_SMF_INTERNAL;
366 			return (-1);
367 		}
368 	}
369 	if (scf_error() == SCF_ERROR_PERMISSION_DENIED) {
370 		ndmp_errno = ENDMP_SMF_PERM;
371 		return (-1);
372 	}
373 
374 	return (0);
375 }
376 
377 /*
378  * Commit the changes that were added to the transaction in the handle. Do all
379  * necessary cleanup.
380  */
381 static int
382 ndmp_smf_end_transaction(ndmp_scfhandle_t *handle)
383 {
384 	if (scf_transaction_commit(handle->scf_trans) < 0) {
385 		ndmp_errno = ENDMP_SMF_INTERNAL;
386 		return (-1);
387 	}
388 
389 	scf_transaction_destroy_children(handle->scf_trans);
390 	scf_transaction_destroy(handle->scf_trans);
391 	handle->scf_trans = NULL;
392 
393 	return (0);
394 }
395 
396 /*
397  * Deletes property in current pg
398  */
399 static int
400 ndmp_smf_delete_property(ndmp_scfhandle_t *handle, char *propname)
401 {
402 	scf_transaction_entry_t *entry = NULL;
403 
404 	/*
405 	 * Properties must be set in transactions and don't take effect until
406 	 * the transaction has been ended/committed.
407 	 */
408 	if ((entry = scf_entry_create(handle->scf_handle)) != NULL) {
409 		if (scf_transaction_property_delete(handle->scf_trans, entry,
410 		    propname) != 0) {
411 			scf_entry_destroy(entry);
412 			ndmp_errno = ENDMP_SMF_INTERNAL;
413 			return (-1);
414 		}
415 	} else {
416 		ndmp_errno = ENDMP_SMF_INTERNAL;
417 		return (-1);
418 	}
419 	if ((scf_error()) == SCF_ERROR_PERMISSION_DENIED) {
420 		ndmp_errno = ENDMP_SMF_PERM;
421 		scf_entry_destroy(entry);
422 		return (-1);
423 	}
424 
425 	return (0);
426 }
427 
428 /*
429  * Sets property in current pg
430  */
431 static int
432 ndmp_smf_set_property(ndmp_scfhandle_t *handle,
433     char *propname, char *valstr)
434 {
435 	int ret = 0;
436 	scf_value_t *value = NULL;
437 	scf_transaction_entry_t *entry = NULL;
438 	scf_property_t *prop;
439 	scf_type_t type;
440 	int64_t valint;
441 	uint8_t valbool;
442 
443 	/*
444 	 * Properties must be set in transactions and don't take effect until
445 	 * the transaction has been ended/committed.
446 	 */
447 	if (((value = scf_value_create(handle->scf_handle)) != NULL) &&
448 	    (entry = scf_entry_create(handle->scf_handle)) != NULL) {
449 		if (((prop =
450 		    scf_property_create(handle->scf_handle)) != NULL) &&
451 		    ((scf_pg_get_property(handle->scf_pg, propname,
452 		    prop)) == 0)) {
453 			if (scf_property_get_value(prop, value) == 0) {
454 				type = scf_value_type(value);
455 				if ((scf_transaction_property_change(
456 				    handle->scf_trans, entry, propname,
457 				    type) == 0) ||
458 				    (scf_transaction_property_new(
459 				    handle->scf_trans, entry, propname,
460 				    type) == 0)) {
461 					switch (type) {
462 					case SCF_TYPE_ASTRING:
463 						if ((scf_value_set_astring(
464 						    value,
465 						    valstr)) != SCF_SUCCESS)
466 							ret = -1;
467 						break;
468 					case SCF_TYPE_INTEGER:
469 						valint = strtoll(valstr, 0, 0);
470 						scf_value_set_integer(value,
471 						    valint);
472 						break;
473 					case SCF_TYPE_BOOLEAN:
474 						if (strncmp(valstr, "yes", 3))
475 							valbool = 0;
476 						else
477 							valbool = 1;
478 						scf_value_set_boolean(value,
479 						    valbool);
480 						break;
481 					default:
482 						ret = -1;
483 					}
484 					if (scf_entry_add_value(entry,
485 					    value) != 0) {
486 						ret = -1;
487 						scf_value_destroy(value);
488 					}
489 					/* The value is in the transaction */
490 					value = NULL;
491 				}
492 				/* The entry is in the transaction */
493 				entry = NULL;
494 			} else {
495 				ret = -1;
496 			}
497 		} else {
498 			ret = -1;
499 		}
500 	} else {
501 		ret = -1;
502 	}
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_value_destroy(value);
510 	scf_entry_destroy(entry);
511 	return (ret);
512 }
513 
514 /*
515  * Gets a property value.upto sz size. Caller is responsible to have enough
516  * memory allocated.
517  */
518 static int
519 ndmp_smf_get_property(ndmp_scfhandle_t *handle, char *propname,
520     char *valstr, size_t sz)
521 {
522 	int ret = 0;
523 	scf_value_t *value;
524 	scf_property_t *prop;
525 	scf_type_t type;
526 	int64_t valint;
527 	uint8_t valbool;
528 	char valstrbuf[NDMP_PROP_LEN];
529 
530 	if (((value = scf_value_create(handle->scf_handle)) != NULL) &&
531 	    ((prop = scf_property_create(handle->scf_handle)) != NULL) &&
532 	    (scf_pg_get_property(handle->scf_pg, propname, prop) == 0)) {
533 		if (scf_property_get_value(prop, value) == 0) {
534 			type = scf_value_type(value);
535 			switch (type) {
536 			case SCF_TYPE_ASTRING:
537 				if (scf_value_get_astring(value, valstr,
538 				    sz) < 0) {
539 					ret = -1;
540 				}
541 				break;
542 			case SCF_TYPE_INTEGER:
543 				if (scf_value_get_integer(value,
544 				    &valint) != 0) {
545 					ret = -1;
546 					break;
547 				}
548 				valstrbuf[NDMP_PROP_LEN - 1] = '\0';
549 				(void) strncpy(valstr, lltostr(valint,
550 				    &valstrbuf[NDMP_PROP_LEN - 1]),
551 				    NDMP_PROP_LEN);
552 				break;
553 			case SCF_TYPE_BOOLEAN:
554 				if (scf_value_get_boolean(value,
555 				    &valbool) != 0) {
556 					ret = -1;
557 					break;
558 				}
559 				if (valbool == 1)
560 					(void) strncpy(valstr, "yes", 4);
561 				else
562 					(void) strncpy(valstr, "no", 3);
563 				break;
564 			default:
565 				ret = -1;
566 			}
567 		} else {
568 			ret = -1;
569 		}
570 	} else {
571 		ret = -1;
572 	}
573 	scf_value_destroy(value);
574 	scf_property_destroy(prop);
575 	return (ret);
576 }
577