6659 nvlist_free(NULL) is a no-op
[illumos-gate.git] / usr / src / lib / libdladm / common / propfuncs.c
blobd46632360447359c3cf7a4fb99e92907d7b0cdc1
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <stdlib.h>
27 #include <strings.h>
28 #include <errno.h>
29 #include <ctype.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/dld.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <libdladm_impl.h>
36 #include <libdlflow_impl.h>
39 * XXX duplicate defines
41 #define DLADM_PROP_VAL_MAX 32
42 #define DLADM_MAX_PROPS 32
44 static void
45 free_props(prop_db_info_t *lip)
47 prop_db_info_t *lip_next;
48 prop_val_t *lvp, *lvp_next;
50 for (; lip != NULL; lip = lip_next) {
51 lip_next = lip->li_nextprop;
52 for (lvp = lip->li_val; lvp != NULL; lvp = lvp_next) {
53 lvp_next = lvp->lv_nextval;
54 free(lvp);
56 free(lip);
61 * Generate an entry in the property database.
62 * Each entry has this format:
63 * <name> <prop0>=<val0>,...,<valn>;...;<propn>=<val0>,...,<valn>;
65 static void
66 generate_prop_line(const char *name, char *buf,
67 prop_db_info_t *listp, dladm_status_t *statusp)
69 char tmpbuf[MAXLINELEN];
70 char *ptr, *lim = tmpbuf + MAXLINELEN;
71 prop_db_info_t *lip = listp;
72 prop_val_t *lvp = NULL;
75 * Delete line if there are no properties left.
77 if (lip == NULL ||
78 (lip->li_val == NULL && lip->li_nextprop == NULL)) {
79 buf[0] = '\0';
80 return;
82 ptr = tmpbuf;
83 ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", name);
84 for (; lip != NULL; lip = lip->li_nextprop) {
86 * Skip properties without values.
88 if (lip->li_val == NULL)
89 continue;
91 ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s=", lip->li_name);
92 for (lvp = lip->li_val; lvp != NULL; lvp = lvp->lv_nextval) {
93 ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s%c",
94 lvp->lv_name,
95 ((lvp->lv_nextval == NULL) ? ';' : ','));
98 if (ptr > lim) {
99 *statusp = DLADM_STATUS_TOOSMALL;
100 return;
102 (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf);
106 * This function is used to update or create an entry in the persistent db.
107 * process_prop_db() will first scan the db for an entry matching the
108 * specified name. If a match is found, this function is invoked with the
109 * entry's contents (buf) and its linked-list representation (listp). lsp
110 * holds the name and values of the property to be added or updated; this
111 * information will be merged with listp. Subsequently, an updated entry
112 * will be written to buf, which will in turn be written to disk by
113 * process_prop_db(). If no entry matches the specified name, listp
114 * will be NULL; a new entry will be generated in this case and it will
115 * contain only the property information in lsp.
117 /* ARGSUSED */
118 boolean_t
119 process_prop_set(dladm_handle_t handle, prop_db_state_t *lsp, char *buf,
120 prop_db_info_t *listp, dladm_status_t *statusp)
122 dladm_status_t status;
123 prop_db_info_t *lastp = NULL, *lip = listp, *nlip = NULL;
124 prop_val_t **lvpp;
125 int i;
127 if (lsp->ls_propname == NULL) {
128 buf[0] = '\0';
129 return (B_FALSE);
133 * Find the prop we want to change.
135 for (; lip != NULL; lip = lip->li_nextprop) {
136 if (strcmp(lip->li_name, lsp->ls_propname) == 0)
137 break;
139 lastp = lip;
142 if (lip == NULL) {
144 * If the prop is not found, append it to the list.
146 if ((nlip = malloc(sizeof (prop_db_info_t))) == NULL) {
147 status = DLADM_STATUS_NOMEM;
148 goto fail;
151 * nlip will need to be freed later if there is no list to
152 * append to.
154 if (lastp != NULL)
155 lastp->li_nextprop = nlip;
156 nlip->li_name = lsp->ls_propname;
157 nlip->li_nextprop = NULL;
158 nlip->li_val = NULL;
159 lvpp = &nlip->li_val;
160 } else {
161 prop_val_t *lvp, *lvp_next;
164 * If the prop is found, delete the existing values from it.
166 for (lvp = lip->li_val; lvp != NULL; lvp = lvp_next) {
167 lvp_next = lvp->lv_nextval;
168 free(lvp);
170 lip->li_val = NULL;
171 lvpp = &lip->li_val;
175 * Fill our prop with the specified values.
177 for (i = 0; i < *lsp->ls_valcntp; i++) {
178 if ((*lvpp = malloc(sizeof (prop_val_t))) == NULL) {
179 status = DLADM_STATUS_NOMEM;
180 goto fail;
182 (*lvpp)->lv_name = lsp->ls_propval[i];
183 (*lvpp)->lv_nextval = NULL;
184 lvpp = &(*lvpp)->lv_nextval;
187 if (listp != NULL) {
188 generate_prop_line(lsp->ls_name, buf, listp, statusp);
189 } else {
190 generate_prop_line(lsp->ls_name, buf, nlip, statusp);
191 free_props(nlip);
193 return (B_FALSE);
195 fail:
196 *statusp = status;
197 if (listp == NULL)
198 free_props(nlip);
200 return (B_FALSE);
204 * This function is used for retrieving the values for a specific property.
205 * It gets called if an entry matching the specified name exists in the db.
206 * The entry is converted into a linked-list listp. This list is then scanned
207 * for the specified property name; if a matching property exists, its
208 * associated values are copied to the array lsp->ls_propval.
210 /* ARGSUSED */
211 boolean_t
212 process_prop_get(dladm_handle_t handle, prop_db_state_t *lsp, char *buf,
213 prop_db_info_t *listp, dladm_status_t *statusp)
215 prop_db_info_t *lip = listp;
216 prop_val_t *lvp;
217 uint_t valcnt = 0;
220 * Find the prop we want to get.
222 for (; lip != NULL; lip = lip->li_nextprop) {
223 if (strcmp(lip->li_name, lsp->ls_propname) == 0)
224 break;
226 if (lip == NULL) {
227 *statusp = DLADM_STATUS_NOTFOUND;
228 return (B_FALSE);
231 for (lvp = lip->li_val; lvp != NULL; lvp = lvp->lv_nextval) {
232 (void) strncpy(lsp->ls_propval[valcnt], lvp->lv_name,
233 DLADM_PROP_VAL_MAX);
235 if (++valcnt >= *lsp->ls_valcntp && lvp->lv_nextval != NULL) {
236 *statusp = DLADM_STATUS_TOOSMALL;
237 return (B_FALSE);
241 * This function is meant to be called at most once for each call
242 * to process_prop_db(). For this reason, it's ok to overwrite
243 * the caller's valcnt array size with the actual number of values
244 * returned.
246 *lsp->ls_valcntp = valcnt;
247 return (B_FALSE);
251 * This is used for initializing properties.
252 * Unlike the other routines, this gets called for every entry in the
253 * database. lsp->ls_name is not user-specified but instead is set to
254 * the current name being processed.
256 /* ARGSUSED */
257 boolean_t
258 process_prop_init(dladm_handle_t handle, prop_db_state_t *lsp, char *buf,
259 prop_db_info_t *listp, dladm_status_t *statusp)
261 dladm_status_t status = DLADM_STATUS_OK;
262 prop_db_info_t *lip = listp;
263 prop_val_t *lvp;
264 uint_t valcnt, i;
265 char **propval;
267 for (; lip != NULL; lip = lip->li_nextprop) {
269 * Construct the propval array and fill it with
270 * values from listp.
272 for (lvp = lip->li_val, valcnt = 0;
273 lvp != NULL; lvp = lvp->lv_nextval, valcnt++) {
276 propval = malloc(sizeof (char *) * valcnt);
277 if (propval == NULL) {
278 *statusp = DLADM_STATUS_NOMEM;
279 break;
281 lvp = lip->li_val;
282 for (i = 0; i < valcnt; i++, lvp = lvp->lv_nextval)
283 propval[i] = (char *)lvp->lv_name;
285 status = (*lsp->ls_initop)(handle, lsp->ls_name, lip->li_name,
286 propval, valcnt, DLADM_OPT_ACTIVE, NULL);
289 * We continue with initializing other properties even
290 * after encountering an error. This error will be
291 * propagated to the caller via 'statusp'.
293 if (status != DLADM_STATUS_OK)
294 *statusp = status;
296 free(propval);
298 return (B_TRUE);
301 static int
302 parse_props(char *buf, prop_db_info_t **lipp)
304 int i, len;
305 char *curr;
306 prop_db_info_t *lip = NULL;
307 prop_db_info_t **tailp = lipp;
308 prop_val_t *lvp = NULL;
309 prop_val_t **vtailp = NULL;
311 curr = buf;
312 len = strlen(buf);
313 for (i = 0; i < len; i++) {
314 char c = buf[i];
315 boolean_t match = (c == '=' || c == ',' || c == ';');
318 * Move to the next character if there is no match and
319 * if we have not reached the last character.
321 if (!match && i != len - 1)
322 continue;
324 if (match) {
326 * Nul-terminate the string pointed to by 'curr'.
328 buf[i] = '\0';
329 if (*curr == '\0')
330 goto fail;
333 if (lip != NULL) {
335 * We get here after we have processed the "<prop>="
336 * pattern. The pattern we are now interested in is
337 * "<val0>,<val1>,...,<valn>;". For each value we
338 * find, a prop_val_t will be allocated and
339 * added to the current 'lip'.
341 if (c == '=')
342 goto fail;
344 lvp = malloc(sizeof (*lvp));
345 if (lvp == NULL)
346 goto fail;
348 lvp->lv_name = curr;
349 lvp->lv_nextval = NULL;
350 *vtailp = lvp;
351 vtailp = &lvp->lv_nextval;
353 if (c == ';') {
354 tailp = &lip->li_nextprop;
355 vtailp = NULL;
356 lip = NULL;
358 } else {
360 * lip == NULL indicates that 'curr' must be refering
361 * to a property name. We allocate a new prop_db_info_t
362 * append it to the list given by the caller.
364 if (c != '=')
365 goto fail;
367 lip = malloc(sizeof (*lip));
368 if (lip == NULL)
369 goto fail;
371 lip->li_name = curr;
372 lip->li_val = NULL;
373 lip->li_nextprop = NULL;
374 *tailp = lip;
375 vtailp = &lip->li_val;
377 curr = buf + i + 1;
380 * The list must be non-empty and the last character must be ';'.
382 if (*lipp == NULL || lip != NULL)
383 goto fail;
385 return (0);
387 fail:
388 free_props(*lipp);
389 *lipp = NULL;
390 return (-1);
393 static boolean_t
394 process_prop_line(dladm_handle_t handle, prop_db_state_t *lsp, char *buf,
395 dladm_status_t *statusp)
397 prop_db_info_t *lip = NULL;
398 int i, len, llen;
399 char *str, *lasts;
400 boolean_t cont, noname = B_FALSE;
403 * Skip leading spaces, blank lines, and comments.
405 len = strlen(buf);
406 for (i = 0; i < len; i++) {
407 if (!isspace(buf[i]))
408 break;
410 if (i == len || buf[i] == '#')
411 return (B_TRUE);
413 str = buf + i;
414 if (lsp->ls_name != NULL) {
416 * Skip names we're not interested in.
417 * Note that strncmp() and isspace() are used here
418 * instead of strtok() and strcmp() because we don't
419 * want to modify buf in case it does not contain the
420 * specified name.
422 llen = strlen(lsp->ls_name);
423 if (strncmp(str, lsp->ls_name, llen) != 0 ||
424 !isspace(str[llen]))
425 return (B_TRUE);
426 } else {
428 * If a name is not specified, find the name
429 * and assign it to lsp->ls_name.
431 if (strtok_r(str, " \n\t", &lasts) == NULL)
432 goto fail;
434 llen = strlen(str);
435 lsp->ls_name = str;
436 noname = B_TRUE;
438 str += llen + 1;
439 if (str >= buf + len)
440 goto fail;
443 * Now find the list of properties.
445 if ((str = strtok_r(str, " \n\t", &lasts)) == NULL)
446 goto fail;
448 if (parse_props(str, &lip) < 0)
449 goto fail;
451 cont = (*lsp->ls_op)(handle, lsp, buf, lip, statusp);
452 free_props(lip);
453 if (noname)
454 lsp->ls_name = NULL;
455 return (cont);
457 fail:
458 free_props(lip);
459 if (noname)
460 lsp->ls_name = NULL;
463 * Delete corrupted line.
465 buf[0] = '\0';
466 return (B_TRUE);
469 dladm_status_t
470 process_prop_db(dladm_handle_t handle, void *arg, FILE *fp, FILE *nfp)
472 prop_db_state_t *lsp = arg;
473 dladm_status_t status = DLADM_STATUS_OK;
474 char buf[MAXLINELEN];
475 boolean_t cont = B_TRUE;
478 * This loop processes each line of the configuration file.
479 * buf can potentially be modified by process_prop_line().
480 * If this is a write operation and buf is not truncated, buf will
481 * be written to disk. process_prop_line() will no longer be
482 * called after it returns B_FALSE; at which point the remainder
483 * of the file will continue to be read and, if necessary, written
484 * to disk as well.
486 while (fgets(buf, MAXLINELEN, fp) != NULL) {
487 if (cont)
488 cont = process_prop_line(handle, lsp, buf, &status);
490 if (nfp != NULL && buf[0] != '\0' && fputs(buf, nfp) == EOF) {
491 status = dladm_errno2status(errno);
492 break;
496 if (status != DLADM_STATUS_OK || !cont)
497 return (status);
499 if (lsp->ls_op == process_prop_set) {
501 * If the specified name is not found above, we add the
502 * name and its properties to the configuration file.
504 (void) (*lsp->ls_op)(handle, lsp, buf, NULL, &status);
505 if (status == DLADM_STATUS_OK && fputs(buf, nfp) == EOF)
506 status = dladm_errno2status(errno);
509 if (lsp->ls_op == process_prop_get)
510 status = DLADM_STATUS_NOTFOUND;
512 return (status);
515 dladm_status_t
516 i_dladm_get_prop_temp(dladm_handle_t handle, const char *name, prop_type_t type,
517 const char *prop_name, char **prop_val, uint_t *val_cntp,
518 prop_table_t *prop_tbl)
520 int i;
521 dladm_status_t status;
522 uint_t cnt;
523 fprop_desc_t *pdp;
525 if (name == NULL || prop_name == NULL || prop_val == NULL ||
526 val_cntp == NULL || *val_cntp == 0)
527 return (DLADM_STATUS_BADARG);
529 for (i = 0; i < prop_tbl->pt_size; i++)
530 if (strcasecmp(prop_name, prop_tbl->pt_table[i].pd_name) == 0)
531 break;
533 if (i == prop_tbl->pt_size)
534 return (DLADM_STATUS_NOTFOUND);
536 pdp = &prop_tbl->pt_table[i];
537 status = DLADM_STATUS_OK;
539 switch (type) {
540 case DLADM_PROP_VAL_CURRENT:
541 status = pdp->pd_get(handle, name, prop_val, val_cntp);
542 break;
543 case DLADM_PROP_VAL_DEFAULT:
544 if (pdp->pd_defval.vd_name == NULL) {
545 status = DLADM_STATUS_NOTSUP;
546 break;
548 (void) strcpy(*prop_val, pdp->pd_defval.vd_name);
549 *val_cntp = 1;
550 break;
552 case DLADM_PROP_VAL_MODIFIABLE:
553 if (pdp->pd_getmod != NULL) {
554 status = pdp->pd_getmod(handle, name, prop_val,
555 val_cntp);
556 break;
558 cnt = pdp->pd_nmodval;
559 if (cnt == 0) {
560 status = DLADM_STATUS_NOTSUP;
561 } else if (cnt > *val_cntp) {
562 status = DLADM_STATUS_TOOSMALL;
563 } else {
564 for (i = 0; i < cnt; i++) {
565 (void) strcpy(prop_val[i],
566 pdp->pd_modval[i].vd_name);
568 *val_cntp = cnt;
570 break;
571 default:
572 status = DLADM_STATUS_BADARG;
573 break;
576 return (status);
579 static dladm_status_t
580 i_dladm_set_one_prop_temp(dladm_handle_t handle, const char *name,
581 fprop_desc_t *pdp, char **prop_val, uint_t val_cnt, uint_t flags)
583 dladm_status_t status;
584 val_desc_t *vdp = NULL;
585 uint_t cnt;
587 if (pdp->pd_temponly && (flags & DLADM_OPT_PERSIST) != 0)
588 return (DLADM_STATUS_TEMPONLY);
590 if (pdp->pd_set == NULL)
591 return (DLADM_STATUS_PROPRDONLY);
593 if (prop_val != NULL) {
594 if (pdp->pd_check != NULL)
595 status = pdp->pd_check(pdp, prop_val, val_cnt, &vdp);
596 else
597 status = DLADM_STATUS_BADARG;
599 if (status != DLADM_STATUS_OK)
600 return (status);
602 cnt = val_cnt;
603 } else {
604 if (pdp->pd_defval.vd_name == NULL)
605 return (DLADM_STATUS_NOTSUP);
607 if ((vdp = malloc(sizeof (val_desc_t))) == NULL)
608 return (DLADM_STATUS_NOMEM);
610 (void) memcpy(vdp, &pdp->pd_defval, sizeof (val_desc_t));
611 cnt = 1;
614 status = pdp->pd_set(handle, name, vdp, cnt);
616 free(vdp);
617 return (status);
620 dladm_status_t
621 i_dladm_set_prop_temp(dladm_handle_t handle, const char *name,
622 const char *prop_name, char **prop_val, uint_t val_cnt, uint_t flags,
623 char **errprop, prop_table_t *prop_tbl)
625 int i;
626 dladm_status_t status = DLADM_STATUS_OK;
627 boolean_t found = B_FALSE;
629 for (i = 0; i < prop_tbl->pt_size; i++) {
630 fprop_desc_t *pdp = &prop_tbl->pt_table[i];
631 dladm_status_t s;
633 if (prop_name != NULL &&
634 (strcasecmp(prop_name, pdp->pd_name) != 0))
635 continue;
637 found = B_TRUE;
638 s = i_dladm_set_one_prop_temp(handle, name, pdp, prop_val,
639 val_cnt, flags);
641 if (prop_name != NULL) {
642 status = s;
643 break;
644 } else {
645 if (s != DLADM_STATUS_OK &&
646 s != DLADM_STATUS_NOTSUP) {
647 if (errprop != NULL)
648 *errprop = pdp->pd_name;
649 status = s;
650 break;
655 if (!found)
656 status = DLADM_STATUS_NOTFOUND;
658 return (status);
661 boolean_t
662 i_dladm_is_prop_temponly(const char *prop_name, char **errprop,
663 prop_table_t *prop_tbl)
665 int i;
667 if (prop_name == NULL)
668 return (B_FALSE);
670 for (i = 0; i < prop_tbl->pt_size; i++) {
671 fprop_desc_t *pdp = &prop_tbl->pt_table[i];
673 if (strcasecmp(prop_name, pdp->pd_name) != 0)
674 continue;
676 if (errprop != NULL)
677 *errprop = pdp->pd_name;
679 if (pdp->pd_temponly)
680 return (B_TRUE);
683 return (B_FALSE);
685 void
686 dladm_free_props(dladm_arg_list_t *list)
688 dladm_free_args(list);
691 dladm_status_t
692 dladm_parse_props(char *str, dladm_arg_list_t **listp, boolean_t novalues)
694 if (dladm_parse_args(str, listp, novalues) != DLADM_STATUS_OK)
695 goto fail;
697 return (DLADM_STATUS_OK);
699 fail:
700 dladm_free_args(*listp);
701 return (DLADM_STATUS_PROP_PARSE_ERR);