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