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 /* ARGSUSED */
118 boolean_t
process_prop_set(dladm_handle_t handle,prop_db_state_t * lsp,char * buf,prop_db_info_t * listp,dladm_status_t * statusp)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)
121 {
122 dladm_status_t status;
123 prop_db_info_t *lastp = NULL, *lip = listp, *nlip = NULL;
124 prop_val_t **lvpp;
125 int i;
126
127 if (lsp->ls_propname == NULL) {
128 buf[0] = '\0';
129 return (B_FALSE);
130 }
131
132 /*
133 * Find the prop we want to change.
134 */
135 for (; lip != NULL; lip = lip->li_nextprop) {
136 if (strcmp(lip->li_name, lsp->ls_propname) == 0)
137 break;
138
139 lastp = lip;
140 }
141
142 if (lip == NULL) {
143 /*
144 * If the prop is not found, append it to the list.
145 */
146 if ((nlip = malloc(sizeof (prop_db_info_t))) == NULL) {
147 status = DLADM_STATUS_NOMEM;
148 goto fail;
149 }
150 /*
151 * nlip will need to be freed later if there is no list to
152 * append to.
153 */
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;
162
163 /*
164 * If the prop is found, delete the existing values from it.
165 */
166 for (lvp = lip->li_val; lvp != NULL; lvp = lvp_next) {
167 lvp_next = lvp->lv_nextval;
168 free(lvp);
169 }
170 lip->li_val = NULL;
171 lvpp = &lip->li_val;
172 }
173
174 /*
175 * Fill our prop with the specified values.
176 */
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;
181 }
182 (*lvpp)->lv_name = lsp->ls_propval[i];
183 (*lvpp)->lv_nextval = NULL;
184 lvpp = &(*lvpp)->lv_nextval;
185 }
186
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);
192 }
193 return (B_FALSE);
194
195 fail:
196 *statusp = status;
197 if (listp == NULL)
198 free_props(nlip);
199
200 return (B_FALSE);
201 }
202
203 /*
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.
209 */
210 /* ARGSUSED */
211 boolean_t
process_prop_get(dladm_handle_t handle,prop_db_state_t * lsp,char * buf,prop_db_info_t * listp,dladm_status_t * statusp)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)
214 {
215 prop_db_info_t *lip = listp;
216 prop_val_t *lvp;
217 uint_t valcnt = 0;
218
219 /*
220 * Find the prop we want to get.
221 */
222 for (; lip != NULL; lip = lip->li_nextprop) {
223 if (strcmp(lip->li_name, lsp->ls_propname) == 0)
224 break;
225 }
226 if (lip == NULL) {
227 *statusp = DLADM_STATUS_NOTFOUND;
228 return (B_FALSE);
229 }
230
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);
234
235 if (++valcnt >= *lsp->ls_valcntp && lvp->lv_nextval != NULL) {
236 *statusp = DLADM_STATUS_TOOSMALL;
237 return (B_FALSE);
238 }
239 }
240 /*
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.
245 */
246 *lsp->ls_valcntp = valcnt;
247 return (B_FALSE);
248 }
249
250 /*
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.
255 */
256 /* ARGSUSED */
257 boolean_t
process_prop_init(dladm_handle_t handle,prop_db_state_t * lsp,char * buf,prop_db_info_t * listp,dladm_status_t * statusp)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)
260 {
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;
266
267 for (; lip != NULL; lip = lip->li_nextprop) {
268 /*
269 * Construct the propval array and fill it with
270 * values from listp.
271 */
272 for (lvp = lip->li_val, valcnt = 0;
273 lvp != NULL; lvp = lvp->lv_nextval, valcnt++) {
274 }
275
276 propval = malloc(sizeof (char *) * valcnt);
277 if (propval == NULL) {
278 *statusp = DLADM_STATUS_NOMEM;
279 break;
280 }
281 lvp = lip->li_val;
282 for (i = 0; i < valcnt; i++, lvp = lvp->lv_nextval)
283 propval[i] = (char *)lvp->lv_name;
284
285 status = (*lsp->ls_initop)(handle, lsp->ls_name, lip->li_name,
286 propval, valcnt, DLADM_OPT_ACTIVE, NULL);
287
288 /*
289 * We continue with initializing other properties even
290 * after encountering an error. This error will be
291 * propagated to the caller via 'statusp'.
292 */
293 if (status != DLADM_STATUS_OK)
294 *statusp = status;
295
296 free(propval);
297 }
298 return (B_TRUE);
299 }
300
301 static int
parse_props(char * buf,prop_db_info_t ** lipp)302 parse_props(char *buf, prop_db_info_t **lipp)
303 {
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;
310
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 == ';');
316
317 /*
318 * Move to the next character if there is no match and
319 * if we have not reached the last character.
320 */
321 if (!match && i != len - 1)
322 continue;
323
324 if (match) {
325 /*
326 * Nul-terminate the string pointed to by 'curr'.
327 */
328 buf[i] = '\0';
329 if (*curr == '\0')
330 goto fail;
331 }
332
333 if (lip != NULL) {
334 /*
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'.
340 */
341 if (c == '=')
342 goto fail;
343
344 lvp = malloc(sizeof (*lvp));
345 if (lvp == NULL)
346 goto fail;
347
348 lvp->lv_name = curr;
349 lvp->lv_nextval = NULL;
350 *vtailp = lvp;
351 vtailp = &lvp->lv_nextval;
352
353 if (c == ';') {
354 tailp = &lip->li_nextprop;
355 vtailp = NULL;
356 lip = NULL;
357 }
358 } else {
359 /*
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.
363 */
364 if (c != '=')
365 goto fail;
366
367 lip = malloc(sizeof (*lip));
368 if (lip == NULL)
369 goto fail;
370
371 lip->li_name = curr;
372 lip->li_val = NULL;
373 lip->li_nextprop = NULL;
374 *tailp = lip;
375 vtailp = &lip->li_val;
376 }
377 curr = buf + i + 1;
378 }
379 /*
380 * The list must be non-empty and the last character must be ';'.
381 */
382 if (*lipp == NULL || lip != NULL)
383 goto fail;
384
385 return (0);
386
387 fail:
388 free_props(*lipp);
389 *lipp = NULL;
390 return (-1);
391 }
392
393 static boolean_t
process_prop_line(dladm_handle_t handle,prop_db_state_t * lsp,char * buf,dladm_status_t * statusp)394 process_prop_line(dladm_handle_t handle, prop_db_state_t *lsp, char *buf,
395 dladm_status_t *statusp)
396 {
397 prop_db_info_t *lip = NULL;
398 int i, len, llen;
399 char *str, *lasts;
400 boolean_t cont, noname = B_FALSE;
401
402 /*
403 * Skip leading spaces, blank lines, and comments.
404 */
405 len = strlen(buf);
406 for (i = 0; i < len; i++) {
407 if (!isspace(buf[i]))
408 break;
409 }
410 if (i == len || buf[i] == '#')
411 return (B_TRUE);
412
413 str = buf + i;
414 if (lsp->ls_name != NULL) {
415 /*
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.
421 */
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 {
427 /*
428 * If a name is not specified, find the name
429 * and assign it to lsp->ls_name.
430 */
431 if (strtok_r(str, " \n\t", &lasts) == NULL)
432 goto fail;
433
434 llen = strlen(str);
435 lsp->ls_name = str;
436 noname = B_TRUE;
437 }
438 str += llen + 1;
439 if (str >= buf + len)
440 goto fail;
441
442 /*
443 * Now find the list of properties.
444 */
445 if ((str = strtok_r(str, " \n\t", &lasts)) == NULL)
446 goto fail;
447
448 if (parse_props(str, &lip) < 0)
449 goto fail;
450
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);
456
457 fail:
458 free_props(lip);
459 if (noname)
460 lsp->ls_name = NULL;
461
462 /*
463 * Delete corrupted line.
464 */
465 buf[0] = '\0';
466 return (B_TRUE);
467 }
468
469 dladm_status_t
process_prop_db(dladm_handle_t handle,void * arg,FILE * fp,FILE * nfp)470 process_prop_db(dladm_handle_t handle, void *arg, FILE *fp, FILE *nfp)
471 {
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;
476
477 /*
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.
485 */
486 while (fgets(buf, MAXLINELEN, fp) != NULL) {
487 if (cont)
488 cont = process_prop_line(handle, lsp, buf, &status);
489
490 if (nfp != NULL && buf[0] != '\0' && fputs(buf, nfp) == EOF) {
491 status = dladm_errno2status(errno);
492 break;
493 }
494 }
495
496 if (status != DLADM_STATUS_OK || !cont)
497 return (status);
498
499 if (lsp->ls_op == process_prop_set) {
500 /*
501 * If the specified name is not found above, we add the
502 * name and its properties to the configuration file.
503 */
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);
507 }
508
509 if (lsp->ls_op == process_prop_get)
510 status = DLADM_STATUS_NOTFOUND;
511
512 return (status);
513 }
514
515 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)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)
519 {
520 int i;
521 dladm_status_t status;
522 uint_t cnt;
523 fprop_desc_t *pdp;
524
525 if (name == NULL || prop_name == NULL || prop_val == NULL ||
526 val_cntp == NULL || *val_cntp == 0)
527 return (DLADM_STATUS_BADARG);
528
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;
532
533 if (i == prop_tbl->pt_size)
534 return (DLADM_STATUS_NOTFOUND);
535
536 pdp = &prop_tbl->pt_table[i];
537 status = DLADM_STATUS_OK;
538
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;
547 }
548 (void) strcpy(*prop_val, pdp->pd_defval.vd_name);
549 *val_cntp = 1;
550 break;
551
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;
557 }
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);
567 }
568 *val_cntp = cnt;
569 }
570 break;
571 default:
572 status = DLADM_STATUS_BADARG;
573 break;
574 }
575
576 return (status);
577 }
578
579 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)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)
582 {
583 dladm_status_t status;
584 val_desc_t *vdp = NULL;
585 uint_t cnt;
586
587 if (pdp->pd_temponly && (flags & DLADM_OPT_PERSIST) != 0)
588 return (DLADM_STATUS_TEMPONLY);
589
590 if (pdp->pd_set == NULL)
591 return (DLADM_STATUS_PROPRDONLY);
592
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;
598
599 if (status != DLADM_STATUS_OK)
600 return (status);
601
602 cnt = val_cnt;
603 } else {
604 if (pdp->pd_defval.vd_name == NULL)
605 return (DLADM_STATUS_NOTSUP);
606
607 if ((vdp = malloc(sizeof (val_desc_t))) == NULL)
608 return (DLADM_STATUS_NOMEM);
609
610 (void) memcpy(vdp, &pdp->pd_defval, sizeof (val_desc_t));
611 cnt = 1;
612 }
613
614 status = pdp->pd_set(handle, name, vdp, cnt);
615
616 free(vdp);
617 return (status);
618 }
619
620 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)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)
624 {
625 int i;
626 dladm_status_t status = DLADM_STATUS_OK;
627 boolean_t found = B_FALSE;
628
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;
632
633 if (prop_name != NULL &&
634 (strcasecmp(prop_name, pdp->pd_name) != 0))
635 continue;
636
637 found = B_TRUE;
638 s = i_dladm_set_one_prop_temp(handle, name, pdp, prop_val,
639 val_cnt, flags);
640
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;
651 }
652 }
653 }
654
655 if (!found)
656 status = DLADM_STATUS_NOTFOUND;
657
658 return (status);
659 }
660
661 boolean_t
i_dladm_is_prop_temponly(const char * prop_name,char ** errprop,prop_table_t * prop_tbl)662 i_dladm_is_prop_temponly(const char *prop_name, char **errprop,
663 prop_table_t *prop_tbl)
664 {
665 int i;
666
667 if (prop_name == NULL)
668 return (B_FALSE);
669
670 for (i = 0; i < prop_tbl->pt_size; i++) {
671 fprop_desc_t *pdp = &prop_tbl->pt_table[i];
672
673 if (strcasecmp(prop_name, pdp->pd_name) != 0)
674 continue;
675
676 if (errprop != NULL)
677 *errprop = pdp->pd_name;
678
679 if (pdp->pd_temponly)
680 return (B_TRUE);
681 }
682
683 return (B_FALSE);
684 }
685 void
dladm_free_props(dladm_arg_list_t * list)686 dladm_free_props(dladm_arg_list_t *list)
687 {
688 dladm_free_args(list);
689 }
690
691 dladm_status_t
dladm_parse_props(char * str,dladm_arg_list_t ** listp,boolean_t novalues)692 dladm_parse_props(char *str, dladm_arg_list_t **listp, boolean_t novalues)
693 {
694 if (dladm_parse_args(str, listp, novalues) != DLADM_STATUS_OK)
695 goto fail;
696
697 return (DLADM_STATUS_OK);
698
699 fail:
700 dladm_free_args(*listp);
701 return (DLADM_STATUS_PROP_PARSE_ERR);
702 }
703