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
ndmp_service_refresh(void)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
ndmp_get_prop(const char * prop,char ** value)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
ndmp_set_prop(const char * env,const char * env_val)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
ndmp_smf_get_pg_name(ndmp_scfhandle_t * h,const char * pname,char ** pgname)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
ndmp_config_saveenv(ndmp_scfhandle_t * handle,boolean_t commit)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
ndmp_smf_scf_fini(ndmp_scfhandle_t * handle)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 *
ndmp_smf_scf_init(const char * svc_name)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
ndmp_smf_create_service_pgroup(ndmp_scfhandle_t * handle,const char * pgroup)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 /*
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 */
353 static int
ndmp_smf_start_transaction(ndmp_scfhandle_t * handle)354 ndmp_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 */
387 static int
ndmp_smf_end_transaction(ndmp_scfhandle_t * handle,boolean_t commit)388 ndmp_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 */
409 static int
ndmp_smf_delete_property(ndmp_scfhandle_t * handle,const char * propname)410 ndmp_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 */
441 static int
ndmp_smf_set_property(ndmp_scfhandle_t * handle,const char * propname,const char * valstr)442 ndmp_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
503 out:
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 */
520 static int
ndmp_smf_get_property(ndmp_scfhandle_t * handle,const char * propname,char * valstr,size_t sz)521 ndmp_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