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
ndmp_service_refresh(void)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
ndmp_get_prop(char * prop,char ** value)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
ndmp_set_prop(char * env,char * env_val)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
ndmp_smf_get_pg_name(ndmp_scfhandle_t * h,char * pname,char ** pgname)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
ndmp_config_saveenv(ndmp_scfhandle_t * handle)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
ndmp_smf_scf_fini(ndmp_scfhandle_t * handle)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 *
ndmp_smf_scf_init(char * svc_name)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
ndmp_smf_create_service_pgroup(ndmp_scfhandle_t * handle,char * pgroup)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
ndmp_smf_start_transaction(ndmp_scfhandle_t * handle)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
ndmp_smf_end_transaction(ndmp_scfhandle_t * handle)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
ndmp_smf_delete_property(ndmp_scfhandle_t * handle,char * propname)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
ndmp_smf_set_property(ndmp_scfhandle_t * handle,char * propname,char * valstr)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
ndmp_smf_get_property(ndmp_scfhandle_t * handle,char * propname,char * valstr,size_t sz)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