1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
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>
37
38 /*
39 * XXX duplicate defines
40 */
41 #define DLADM_PROP_VAL_MAX 32
42 #define DLADM_MAX_PROPS 32
43
44 static void
free_props(prop_db_info_t * lip)45 free_props(prop_db_info_t *lip)
46 {
47 prop_db_info_t *lip_next;
48 prop_val_t *lvp, *lvp_next;
49
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);
55 }
56 free(lip);
57 }
58 }
59
60 /*
61 * Generate an entry in the property database.
62 * Each entry has this format:
63 * <name> <prop0>=<val0>,...,<valn>;...;<propn>=<val0>,...,<valn>;
64 */
65 static void
generate_prop_line(const char * name,char * buf,prop_db_info_t * listp,dladm_status_t * statusp)66 generate_prop_line(const char *name, char *buf,
67 prop_db_info_t *listp, dladm_status_t *statusp)
68 {
69 char tmpbuf[MAXLINELEN];
70 char *ptr, *lim = tmpbuf + MAXLINELEN;
71 prop_db_info_t *lip = listp;
72 prop_val_t *lvp = NULL;
73
74 /*
75 * Delete line if there are no properties left.
76 */
77 if (lip == NULL ||
78 (lip->li_val == NULL && lip->li_nextprop == NULL)) {
79 buf[0] = '\0';
80 return;
81 }
82 ptr = tmpbuf;
83 ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", name);
84 for (; lip != NULL; lip = lip->li_nextprop) {
85 /*
86 * Skip properties without values.
87 */
88 if (lip->li_val == NULL)
89 continue;
90
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) ? ';' : ','));
96 }
97 }
98 if (ptr > lim) {
99 *statusp = DLADM_STATUS_TOOSMALL;
100 return;
101 }
102 (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf);
103 }
104
105 /*
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.
116 */
117 boolean_t
process_prop_set(dladm_handle_t handle __unused,prop_db_state_t * lsp,char * buf,prop_db_info_t * listp,dladm_status_t * statusp)118 process_prop_set(dladm_handle_t handle __unused, prop_db_state_t *lsp,
119 char *buf, prop_db_info_t *listp, dladm_status_t *statusp)
120 {
121 dladm_status_t status;
122 prop_db_info_t *lastp = NULL, *lip = listp, *nlip = NULL;
123 prop_val_t **lvpp;
124 uint_t i;
125
126 if (lsp->ls_propname == NULL) {
127 buf[0] = '\0';
128 return (B_FALSE);
129 }
130
131 /*
132 * Find the prop we want to change.
133 */
134 for (; lip != NULL; lip = lip->li_nextprop) {
135 if (strcmp(lip->li_name, lsp->ls_propname) == 0)
136 break;
137
138 lastp = lip;
139 }
140
141 if (lip == NULL) {
142 /*
143 * If the prop is not found, append it to the list.
144 */
145 if ((nlip = malloc(sizeof (prop_db_info_t))) == NULL) {
146 status = DLADM_STATUS_NOMEM;
147 goto fail;
148 }
149 /*
150 * nlip will need to be freed later if there is no list to
151 * append to.
152 */
153 if (lastp != NULL)
154 lastp->li_nextprop = nlip;
155 nlip->li_name = lsp->ls_propname;
156 nlip->li_nextprop = NULL;
157 nlip->li_val = NULL;
158 lvpp = &nlip->li_val;
159 } else {
160 prop_val_t *lvp, *lvp_next;
161
162 /*
163 * If the prop is found, delete the existing values from it.
164 */
165 for (lvp = lip->li_val; lvp != NULL; lvp = lvp_next) {
166 lvp_next = lvp->lv_nextval;
167 free(lvp);
168 }
169 lip->li_val = NULL;
170 lvpp = &lip->li_val;
171 }
172
173 /*
174 * Fill our prop with the specified values.
175 */
176 for (i = 0; i < *lsp->ls_valcntp; i++) {
177 if ((*lvpp = malloc(sizeof (prop_val_t))) == NULL) {
178 status = DLADM_STATUS_NOMEM;
179 goto fail;
180 }
181 (*lvpp)->lv_name = lsp->ls_propval[i];
182 (*lvpp)->lv_nextval = NULL;
183 lvpp = &(*lvpp)->lv_nextval;
184 }
185
186 if (listp != NULL) {
187 generate_prop_line(lsp->ls_name, buf, listp, statusp);
188 } else {
189 generate_prop_line(lsp->ls_name, buf, nlip, statusp);
190 free_props(nlip);
191 }
192 return (B_FALSE);
193
194 fail:
195 *statusp = status;
196 if (listp == NULL)
197 free_props(nlip);
198
199 return (B_FALSE);
200 }
201
202 /*
203 * This function is used for retrieving the values for a specific property.
204 * It gets called if an entry matching the specified name exists in the db.
205 * The entry is converted into a linked-list listp. This list is then scanned
206 * for the specified property name; if a matching property exists, its
207 * associated values are copied to the array lsp->ls_propval.
208 */
209 boolean_t
process_prop_get(dladm_handle_t handle __unused,prop_db_state_t * lsp,char * buf __unused,prop_db_info_t * listp,dladm_status_t * statusp)210 process_prop_get(dladm_handle_t handle __unused, prop_db_state_t *lsp,
211 char *buf __unused, prop_db_info_t *listp, dladm_status_t *statusp)
212 {
213 prop_db_info_t *lip = listp;
214 prop_val_t *lvp;
215 uint_t valcnt = 0;
216
217 /*
218 * Find the prop we want to get.
219 */
220 for (; lip != NULL; lip = lip->li_nextprop) {
221 if (strcmp(lip->li_name, lsp->ls_propname) == 0)
222 break;
223 }
224 if (lip == NULL) {
225 *statusp = DLADM_STATUS_NOTFOUND;
226 return (B_FALSE);
227 }
228
229 for (lvp = lip->li_val; lvp != NULL; lvp = lvp->lv_nextval) {
230 (void) strncpy(lsp->ls_propval[valcnt], lvp->lv_name,
231 DLADM_PROP_VAL_MAX);
232
233 if (++valcnt >= *lsp->ls_valcntp && lvp->lv_nextval != NULL) {
234 *statusp = DLADM_STATUS_TOOSMALL;
235 return (B_FALSE);
236 }
237 }
238 /*
239 * This function is meant to be called at most once for each call
240 * to process_prop_db(). For this reason, it's ok to overwrite
241 * the caller's valcnt array size with the actual number of values
242 * returned.
243 */
244 *lsp->ls_valcntp = valcnt;
245 return (B_FALSE);
246 }
247
248 /*
249 * This is used for initializing properties.
250 * Unlike the other routines, this gets called for every entry in the
251 * database. lsp->ls_name is not user-specified but instead is set to
252 * the current name being processed.
253 */
254 boolean_t
process_prop_init(dladm_handle_t handle,prop_db_state_t * lsp,char * buf __unused,prop_db_info_t * listp,dladm_status_t * statusp)255 process_prop_init(dladm_handle_t handle, prop_db_state_t *lsp,
256 char *buf __unused, prop_db_info_t *listp, dladm_status_t *statusp)
257 {
258 dladm_status_t status = DLADM_STATUS_OK;
259 prop_db_info_t *lip = listp;
260 prop_val_t *lvp;
261 uint_t valcnt, i;
262 char **propval;
263
264 for (; lip != NULL; lip = lip->li_nextprop) {
265 /*
266 * Construct the propval array and fill it with
267 * values from listp.
268 */
269 for (lvp = lip->li_val, valcnt = 0;
270 lvp != NULL; lvp = lvp->lv_nextval, valcnt++) {
271 }
272
273 propval = malloc(sizeof (char *) * valcnt);
274 if (propval == NULL) {
275 *statusp = DLADM_STATUS_NOMEM;
276 break;
277 }
278 lvp = lip->li_val;
279 for (i = 0; i < valcnt; i++, lvp = lvp->lv_nextval)
280 propval[i] = (char *)lvp->lv_name;
281
282 status = (*lsp->ls_initop)(handle, lsp->ls_name, lip->li_name,
283 propval, valcnt, DLADM_OPT_ACTIVE, NULL);
284
285 /*
286 * We continue with initializing other properties even
287 * after encountering an error. This error will be
288 * propagated to the caller via 'statusp'.
289 */
290 if (status != DLADM_STATUS_OK)
291 *statusp = status;
292
293 free(propval);
294 }
295 return (B_TRUE);
296 }
297
298 static int
parse_props(char * buf,prop_db_info_t ** lipp)299 parse_props(char *buf, prop_db_info_t **lipp)
300 {
301 int i, len;
302 char *curr;
303 prop_db_info_t *lip = NULL;
304 prop_db_info_t **tailp = lipp;
305 prop_val_t *lvp = NULL;
306 prop_val_t **vtailp = NULL;
307
308 curr = buf;
309 len = strlen(buf);
310 for (i = 0; i < len; i++) {
311 char c = buf[i];
312 boolean_t match = (c == '=' || c == ',' || c == ';');
313
314 /*
315 * Move to the next character if there is no match and
316 * if we have not reached the last character.
317 */
318 if (!match && i != len - 1)
319 continue;
320
321 if (match) {
322 /*
323 * Nul-terminate the string pointed to by 'curr'.
324 */
325 buf[i] = '\0';
326 if (*curr == '\0')
327 goto fail;
328 }
329
330 if (lip != NULL) {
331 /*
332 * We get here after we have processed the "<prop>="
333 * pattern. The pattern we are now interested in is
334 * "<val0>,<val1>,...,<valn>;". For each value we
335 * find, a prop_val_t will be allocated and
336 * added to the current 'lip'.
337 */
338 if (c == '=')
339 goto fail;
340
341 lvp = malloc(sizeof (*lvp));
342 if (lvp == NULL)
343 goto fail;
344
345 lvp->lv_name = curr;
346 lvp->lv_nextval = NULL;
347 *vtailp = lvp;
348 vtailp = &lvp->lv_nextval;
349
350 if (c == ';') {
351 tailp = &lip->li_nextprop;
352 vtailp = NULL;
353 lip = NULL;
354 }
355 } else {
356 /*
357 * lip == NULL indicates that 'curr' must be refering
358 * to a property name. We allocate a new prop_db_info_t
359 * append it to the list given by the caller.
360 */
361 if (c != '=')
362 goto fail;
363
364 lip = malloc(sizeof (*lip));
365 if (lip == NULL)
366 goto fail;
367
368 lip->li_name = curr;
369 lip->li_val = NULL;
370 lip->li_nextprop = NULL;
371 *tailp = lip;
372 vtailp = &lip->li_val;
373 }
374 curr = buf + i + 1;
375 }
376 /*
377 * The list must be non-empty and the last character must be ';'.
378 */
379 if (*lipp == NULL || lip != NULL)
380 goto fail;
381
382 return (0);
383
384 fail:
385 free_props(*lipp);
386 *lipp = NULL;
387 return (-1);
388 }
389
390 static boolean_t
process_prop_line(dladm_handle_t handle,prop_db_state_t * lsp,char * buf,dladm_status_t * statusp)391 process_prop_line(dladm_handle_t handle, prop_db_state_t *lsp, char *buf,
392 dladm_status_t *statusp)
393 {
394 prop_db_info_t *lip = NULL;
395 int i, len, llen;
396 char *str, *lasts;
397 boolean_t cont, noname = B_FALSE;
398
399 /*
400 * Skip leading spaces, blank lines, and comments.
401 */
402 len = strlen(buf);
403 for (i = 0; i < len; i++) {
404 if (!isspace(buf[i]))
405 break;
406 }
407 if (i == len || buf[i] == '#')
408 return (B_TRUE);
409
410 str = buf + i;
411 if (lsp->ls_name != NULL) {
412 /*
413 * Skip names we're not interested in.
414 * Note that strncmp() and isspace() are used here
415 * instead of strtok() and strcmp() because we don't
416 * want to modify buf in case it does not contain the
417 * specified name.
418 */
419 llen = strlen(lsp->ls_name);
420 if (strncmp(str, lsp->ls_name, llen) != 0 ||
421 !isspace(str[llen]))
422 return (B_TRUE);
423 } else {
424 /*
425 * If a name is not specified, find the name
426 * and assign it to lsp->ls_name.
427 */
428 if (strtok_r(str, " \n\t", &lasts) == NULL)
429 goto fail;
430
431 llen = strlen(str);
432 lsp->ls_name = str;
433 noname = B_TRUE;
434 }
435 str += llen + 1;
436 if (str >= buf + len)
437 goto fail;
438
439 /*
440 * Now find the list of properties.
441 */
442 if ((str = strtok_r(str, " \n\t", &lasts)) == NULL)
443 goto fail;
444
445 if (parse_props(str, &lip) < 0)
446 goto fail;
447
448 cont = (*lsp->ls_op)(handle, lsp, buf, lip, statusp);
449 free_props(lip);
450 if (noname)
451 lsp->ls_name = NULL;
452 return (cont);
453
454 fail:
455 free_props(lip);
456 if (noname)
457 lsp->ls_name = NULL;
458
459 /*
460 * Delete corrupted line.
461 */
462 buf[0] = '\0';
463 return (B_TRUE);
464 }
465
466 dladm_status_t
process_prop_db(dladm_handle_t handle,void * arg,FILE * fp,FILE * nfp)467 process_prop_db(dladm_handle_t handle, void *arg, FILE *fp, FILE *nfp)
468 {
469 prop_db_state_t *lsp = arg;
470 dladm_status_t status = DLADM_STATUS_OK;
471 char buf[MAXLINELEN];
472 boolean_t cont = B_TRUE;
473
474 /*
475 * This loop processes each line of the configuration file.
476 * buf can potentially be modified by process_prop_line().
477 * If this is a write operation and buf is not truncated, buf will
478 * be written to disk. process_prop_line() will no longer be
479 * called after it returns B_FALSE; at which point the remainder
480 * of the file will continue to be read and, if necessary, written
481 * to disk as well.
482 */
483 while (fgets(buf, MAXLINELEN, fp) != NULL) {
484 if (cont)
485 cont = process_prop_line(handle, lsp, buf, &status);
486
487 if (nfp != NULL && buf[0] != '\0' && fputs(buf, nfp) == EOF) {
488 status = dladm_errno2status(errno);
489 break;
490 }
491 }
492
493 if (status != DLADM_STATUS_OK || !cont)
494 return (status);
495
496 if (lsp->ls_op == process_prop_set) {
497 /*
498 * If the specified name is not found above, we add the
499 * name and its properties to the configuration file.
500 */
501 (void) (*lsp->ls_op)(handle, lsp, buf, NULL, &status);
502 if (status == DLADM_STATUS_OK && fputs(buf, nfp) == EOF)
503 status = dladm_errno2status(errno);
504 }
505
506 if (lsp->ls_op == process_prop_get)
507 status = DLADM_STATUS_NOTFOUND;
508
509 return (status);
510 }
511
512 dladm_status_t
i_dladm_get_prop_temp(dladm_handle_t handle,const char * name,prop_type_t type,const char * prop_name,char ** prop_val,uint_t * val_cntp,prop_table_t * prop_tbl)513 i_dladm_get_prop_temp(dladm_handle_t handle, const char *name, prop_type_t type,
514 const char *prop_name, char **prop_val, uint_t *val_cntp,
515 prop_table_t *prop_tbl)
516 {
517 uint_t i;
518 dladm_status_t status;
519 uint_t cnt;
520 fprop_desc_t *pdp;
521
522 if (name == NULL || prop_name == NULL || prop_val == NULL ||
523 val_cntp == NULL || *val_cntp == 0)
524 return (DLADM_STATUS_BADARG);
525
526 for (i = 0; i < prop_tbl->pt_size; i++)
527 if (strcasecmp(prop_name, prop_tbl->pt_table[i].pd_name) == 0)
528 break;
529
530 if (i == prop_tbl->pt_size)
531 return (DLADM_STATUS_NOTFOUND);
532
533 pdp = &prop_tbl->pt_table[i];
534 status = DLADM_STATUS_OK;
535
536 switch (type) {
537 case DLADM_PROP_VAL_CURRENT:
538 status = pdp->pd_get(handle, name, prop_val, val_cntp);
539 break;
540 case DLADM_PROP_VAL_DEFAULT:
541 if (pdp->pd_defval.vd_name == NULL) {
542 status = DLADM_STATUS_NOTSUP;
543 break;
544 }
545 (void) strcpy(*prop_val, pdp->pd_defval.vd_name);
546 *val_cntp = 1;
547 break;
548
549 case DLADM_PROP_VAL_MODIFIABLE:
550 if (pdp->pd_getmod != NULL) {
551 status = pdp->pd_getmod(handle, name, prop_val,
552 val_cntp);
553 break;
554 }
555 cnt = pdp->pd_nmodval;
556 if (cnt == 0) {
557 status = DLADM_STATUS_NOTSUP;
558 } else if (cnt > *val_cntp) {
559 status = DLADM_STATUS_TOOSMALL;
560 } else {
561 for (i = 0; i < cnt; i++) {
562 (void) strcpy(prop_val[i],
563 pdp->pd_modval[i].vd_name);
564 }
565 *val_cntp = cnt;
566 }
567 break;
568 default:
569 status = DLADM_STATUS_BADARG;
570 break;
571 }
572
573 return (status);
574 }
575
576 static dladm_status_t
i_dladm_set_one_prop_temp(dladm_handle_t handle,const char * name,fprop_desc_t * pdp,char ** prop_val,uint_t val_cnt,uint_t flags)577 i_dladm_set_one_prop_temp(dladm_handle_t handle, const char *name,
578 fprop_desc_t *pdp, char **prop_val, uint_t val_cnt, uint_t flags)
579 {
580 dladm_status_t status;
581 val_desc_t *vdp = NULL;
582 uint_t cnt;
583
584 if (pdp->pd_temponly && (flags & DLADM_OPT_PERSIST) != 0)
585 return (DLADM_STATUS_TEMPONLY);
586
587 if (pdp->pd_set == NULL)
588 return (DLADM_STATUS_PROPRDONLY);
589
590 if (prop_val != NULL) {
591 if (pdp->pd_check != NULL)
592 status = pdp->pd_check(pdp, prop_val, val_cnt, &vdp);
593 else
594 status = DLADM_STATUS_BADARG;
595
596 if (status != DLADM_STATUS_OK)
597 return (status);
598
599 cnt = val_cnt;
600 } else {
601 if (pdp->pd_defval.vd_name == NULL)
602 return (DLADM_STATUS_NOTSUP);
603
604 if ((vdp = malloc(sizeof (val_desc_t))) == NULL)
605 return (DLADM_STATUS_NOMEM);
606
607 (void) memcpy(vdp, &pdp->pd_defval, sizeof (val_desc_t));
608 cnt = 1;
609 }
610
611 status = pdp->pd_set(handle, name, vdp, cnt);
612
613 free(vdp);
614 return (status);
615 }
616
617 dladm_status_t
i_dladm_set_prop_temp(dladm_handle_t handle,const char * name,const char * prop_name,char ** prop_val,uint_t val_cnt,uint_t flags,char ** errprop,prop_table_t * prop_tbl)618 i_dladm_set_prop_temp(dladm_handle_t handle, const char *name,
619 const char *prop_name, char **prop_val, uint_t val_cnt, uint_t flags,
620 char **errprop, prop_table_t *prop_tbl)
621 {
622 uint_t i;
623 dladm_status_t status = DLADM_STATUS_OK;
624 boolean_t found = B_FALSE;
625
626 for (i = 0; i < prop_tbl->pt_size; i++) {
627 fprop_desc_t *pdp = &prop_tbl->pt_table[i];
628 dladm_status_t s;
629
630 if (prop_name != NULL &&
631 (strcasecmp(prop_name, pdp->pd_name) != 0))
632 continue;
633
634 found = B_TRUE;
635 s = i_dladm_set_one_prop_temp(handle, name, pdp, prop_val,
636 val_cnt, flags);
637
638 if (prop_name != NULL) {
639 status = s;
640 break;
641 } else {
642 if (s != DLADM_STATUS_OK &&
643 s != DLADM_STATUS_NOTSUP) {
644 if (errprop != NULL)
645 *errprop = pdp->pd_name;
646 status = s;
647 break;
648 }
649 }
650 }
651
652 if (!found)
653 status = DLADM_STATUS_NOTFOUND;
654
655 return (status);
656 }
657
658 boolean_t
i_dladm_is_prop_temponly(const char * prop_name,char ** errprop,prop_table_t * prop_tbl)659 i_dladm_is_prop_temponly(const char *prop_name, char **errprop,
660 prop_table_t *prop_tbl)
661 {
662 uint_t i;
663
664 if (prop_name == NULL)
665 return (B_FALSE);
666
667 for (i = 0; i < prop_tbl->pt_size; i++) {
668 fprop_desc_t *pdp = &prop_tbl->pt_table[i];
669
670 if (strcasecmp(prop_name, pdp->pd_name) != 0)
671 continue;
672
673 if (errprop != NULL)
674 *errprop = pdp->pd_name;
675
676 if (pdp->pd_temponly)
677 return (B_TRUE);
678 }
679
680 return (B_FALSE);
681 }
682 void
dladm_free_props(dladm_arg_list_t * list)683 dladm_free_props(dladm_arg_list_t *list)
684 {
685 dladm_free_args(list);
686 }
687
688 dladm_status_t
dladm_parse_props(char * str,dladm_arg_list_t ** listp,boolean_t novalues)689 dladm_parse_props(char *str, dladm_arg_list_t **listp, boolean_t novalues)
690 {
691 if (dladm_parse_args(str, listp, novalues) != DLADM_STATUS_OK)
692 goto fail;
693
694 return (DLADM_STATUS_OK);
695
696 fail:
697 dladm_free_args(*listp);
698 return (DLADM_STATUS_PROP_PARSE_ERR);
699 }
700