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 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 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 118 process_prop_set(prop_db_state_t *lsp, char *buf, 119 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 int 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 /* ARGSUSED */ 210 boolean_t 211 process_prop_get(prop_db_state_t *lsp, char *buf, 212 prop_db_info_t *listp, dladm_status_t *statusp) 213 { 214 prop_db_info_t *lip = listp; 215 prop_val_t *lvp; 216 uint_t valcnt = 0; 217 218 /* 219 * Find the prop we want to get. 220 */ 221 for (; lip != NULL; lip = lip->li_nextprop) { 222 if (strcmp(lip->li_name, lsp->ls_propname) == 0) 223 break; 224 } 225 if (lip == NULL) { 226 *statusp = DLADM_STATUS_NOTFOUND; 227 return (B_FALSE); 228 } 229 230 for (lvp = lip->li_val; lvp != NULL; lvp = lvp->lv_nextval) { 231 (void) strncpy(lsp->ls_propval[valcnt], lvp->lv_name, 232 DLADM_PROP_VAL_MAX); 233 234 if (++valcnt >= *lsp->ls_valcntp && lvp->lv_nextval != NULL) { 235 *statusp = DLADM_STATUS_TOOSMALL; 236 return (B_FALSE); 237 } 238 } 239 /* 240 * This function is meant to be called at most once for each call 241 * to process_prop_db(). For this reason, it's ok to overwrite 242 * the caller's valcnt array size with the actual number of values 243 * returned. 244 */ 245 *lsp->ls_valcntp = valcnt; 246 return (B_FALSE); 247 } 248 249 /* 250 * This is used for initializing properties. 251 * Unlike the other routines, this gets called for every entry in the 252 * database. lsp->ls_name is not user-specified but instead is set to 253 * the current name being processed. 254 */ 255 /* ARGSUSED */ 256 boolean_t 257 process_prop_init(prop_db_state_t *lsp, char *buf, 258 prop_db_info_t *listp, dladm_status_t *statusp) 259 { 260 dladm_status_t status = DLADM_STATUS_OK; 261 prop_db_info_t *lip = listp; 262 prop_val_t *lvp; 263 uint_t valcnt, i; 264 char **propval; 265 266 for (; lip != NULL; lip = lip->li_nextprop) { 267 /* 268 * Construct the propval array and fill it with 269 * values from listp. 270 */ 271 for (lvp = lip->li_val, valcnt = 0; 272 lvp != NULL; lvp = lvp->lv_nextval, valcnt++) { 273 } 274 275 propval = malloc(sizeof (char *) * valcnt); 276 if (propval == NULL) { 277 *statusp = DLADM_STATUS_NOMEM; 278 break; 279 } 280 lvp = lip->li_val; 281 for (i = 0; i < valcnt; i++, lvp = lvp->lv_nextval) 282 propval[i] = (char *)lvp->lv_name; 283 284 status = (*lsp->ls_initop)(lsp->ls_name, lip->li_name, 285 propval, valcnt, DLADM_OPT_ACTIVE, NULL); 286 287 /* 288 * We continue with initializing other properties even 289 * after encountering an error. This error will be 290 * propagated to the caller via 'statusp'. 291 */ 292 if (status != DLADM_STATUS_OK) 293 *statusp = status; 294 295 free(propval); 296 } 297 return (B_TRUE); 298 } 299 300 static int 301 parse_props(char *buf, prop_db_info_t **lipp) 302 { 303 int i, len; 304 char *curr; 305 prop_db_info_t *lip = NULL; 306 prop_db_info_t **tailp = lipp; 307 prop_val_t *lvp = NULL; 308 prop_val_t **vtailp = NULL; 309 310 curr = buf; 311 len = strlen(buf); 312 for (i = 0; i < len; i++) { 313 char c = buf[i]; 314 boolean_t match = (c == '=' || c == ',' || c == ';'); 315 316 /* 317 * Move to the next character if there is no match and 318 * if we have not reached the last character. 319 */ 320 if (!match && i != len - 1) 321 continue; 322 323 if (match) { 324 /* 325 * Nul-terminate the string pointed to by 'curr'. 326 */ 327 buf[i] = '\0'; 328 if (*curr == '\0') 329 goto fail; 330 } 331 332 if (lip != NULL) { 333 /* 334 * We get here after we have processed the "<prop>=" 335 * pattern. The pattern we are now interested in is 336 * "<val0>,<val1>,...,<valn>;". For each value we 337 * find, a prop_val_t will be allocated and 338 * added to the current 'lip'. 339 */ 340 if (c == '=') 341 goto fail; 342 343 lvp = malloc(sizeof (*lvp)); 344 if (lvp == NULL) 345 goto fail; 346 347 lvp->lv_name = curr; 348 lvp->lv_nextval = NULL; 349 *vtailp = lvp; 350 vtailp = &lvp->lv_nextval; 351 352 if (c == ';') { 353 tailp = &lip->li_nextprop; 354 vtailp = NULL; 355 lip = NULL; 356 } 357 } else { 358 /* 359 * lip == NULL indicates that 'curr' must be refering 360 * to a property name. We allocate a new prop_db_info_t 361 * append it to the list given by the caller. 362 */ 363 if (c != '=') 364 goto fail; 365 366 lip = malloc(sizeof (*lip)); 367 if (lip == NULL) 368 goto fail; 369 370 lip->li_name = curr; 371 lip->li_val = NULL; 372 lip->li_nextprop = NULL; 373 *tailp = lip; 374 vtailp = &lip->li_val; 375 } 376 curr = buf + i + 1; 377 } 378 /* 379 * The list must be non-empty and the last character must be ';'. 380 */ 381 if (*lipp == NULL || lip != NULL) 382 goto fail; 383 384 return (0); 385 386 fail: 387 free_props(*lipp); 388 *lipp = NULL; 389 return (-1); 390 } 391 392 static boolean_t 393 process_prop_line(prop_db_state_t *lsp, char *buf, 394 dladm_status_t *statusp) 395 { 396 prop_db_info_t *lip = NULL; 397 int i, len, llen; 398 char *str, *lasts; 399 boolean_t cont, noname = B_FALSE; 400 401 /* 402 * Skip leading spaces, blank lines, and comments. 403 */ 404 len = strlen(buf); 405 for (i = 0; i < len; i++) { 406 if (!isspace(buf[i])) 407 break; 408 } 409 if (i == len || buf[i] == '#') 410 return (B_TRUE); 411 412 str = buf + i; 413 if (lsp->ls_name != NULL) { 414 /* 415 * Skip names we're not interested in. 416 * Note that strncmp() and isspace() are used here 417 * instead of strtok() and strcmp() because we don't 418 * want to modify buf in case it does not contain the 419 * specified name. 420 */ 421 llen = strlen(lsp->ls_name); 422 if (strncmp(str, lsp->ls_name, llen) != 0 || 423 !isspace(str[llen])) 424 return (B_TRUE); 425 } else { 426 /* 427 * If a name is not specified, find the name 428 * and assign it to lsp->ls_name. 429 */ 430 if (strtok_r(str, " \n\t", &lasts) == NULL) 431 goto fail; 432 433 llen = strlen(str); 434 lsp->ls_name = str; 435 noname = B_TRUE; 436 } 437 str += llen + 1; 438 if (str >= buf + len) 439 goto fail; 440 441 /* 442 * Now find the list of properties. 443 */ 444 if ((str = strtok_r(str, " \n\t", &lasts)) == NULL) 445 goto fail; 446 447 if (parse_props(str, &lip) < 0) 448 goto fail; 449 450 cont = (*lsp->ls_op)(lsp, buf, lip, statusp); 451 free_props(lip); 452 if (noname) 453 lsp->ls_name = NULL; 454 return (cont); 455 456 fail: 457 free_props(lip); 458 if (noname) 459 lsp->ls_name = NULL; 460 461 /* 462 * Delete corrupted line. 463 */ 464 buf[0] = '\0'; 465 return (B_TRUE); 466 } 467 468 dladm_status_t 469 process_prop_db(void *arg, FILE *fp, FILE *nfp) 470 { 471 prop_db_state_t *lsp = arg; 472 dladm_status_t status = DLADM_STATUS_OK; 473 char buf[MAXLINELEN]; 474 boolean_t cont = B_TRUE; 475 476 /* 477 * This loop processes each line of the configuration file. 478 * buf can potentially be modified by process_prop_line(). 479 * If this is a write operation and buf is not truncated, buf will 480 * be written to disk. process_prop_line() will no longer be 481 * called after it returns B_FALSE; at which point the remainder 482 * of the file will continue to be read and, if necessary, written 483 * to disk as well. 484 */ 485 while (fgets(buf, MAXLINELEN, fp) != NULL) { 486 if (cont) 487 cont = process_prop_line(lsp, buf, &status); 488 489 if (nfp != NULL && buf[0] != '\0' && fputs(buf, nfp) == EOF) { 490 status = dladm_errno2status(errno); 491 break; 492 } 493 } 494 495 if (status != DLADM_STATUS_OK || !cont) 496 return (status); 497 498 if (lsp->ls_op == process_prop_set) { 499 /* 500 * If the specified name is not found above, we add the 501 * name and its properties to the configuration file. 502 */ 503 (void) (*lsp->ls_op)(lsp, buf, NULL, &status); 504 if (status == DLADM_STATUS_OK && fputs(buf, nfp) == EOF) 505 status = dladm_errno2status(errno); 506 } 507 508 if (lsp->ls_op == process_prop_get) 509 status = DLADM_STATUS_NOTFOUND; 510 511 return (status); 512 } 513 514 dladm_status_t 515 i_dladm_get_prop_temp(const char *name, prop_type_t type, 516 const char *prop_name, char **prop_val, uint_t *val_cntp, 517 prop_table_t *prop_tbl) 518 { 519 int i; 520 dladm_status_t status; 521 uint_t cnt; 522 fprop_desc_t *pdp; 523 524 if (name == NULL || prop_name == NULL || prop_val == NULL || 525 val_cntp == NULL || *val_cntp == 0) 526 return (DLADM_STATUS_BADARG); 527 528 for (i = 0; i < prop_tbl->pt_size; i++) 529 if (strcasecmp(prop_name, prop_tbl->pt_table[i].pd_name) == 0) 530 break; 531 532 if (i == prop_tbl->pt_size) 533 return (DLADM_STATUS_NOTFOUND); 534 535 pdp = &prop_tbl->pt_table[i]; 536 status = DLADM_STATUS_OK; 537 538 switch (type) { 539 case DLADM_PROP_VAL_CURRENT: 540 status = pdp->pd_get(name, prop_val, val_cntp); 541 break; 542 case DLADM_PROP_VAL_DEFAULT: 543 if (pdp->pd_defval.vd_name == NULL) { 544 status = DLADM_STATUS_NOTSUP; 545 break; 546 } 547 (void) strcpy(*prop_val, pdp->pd_defval.vd_name); 548 *val_cntp = 1; 549 break; 550 551 case DLADM_PROP_VAL_MODIFIABLE: 552 if (pdp->pd_getmod != NULL) { 553 status = pdp->pd_getmod(name, prop_val, val_cntp); 554 break; 555 } 556 cnt = pdp->pd_nmodval; 557 if (cnt == 0) { 558 status = DLADM_STATUS_NOTSUP; 559 } else if (cnt > *val_cntp) { 560 status = DLADM_STATUS_TOOSMALL; 561 } else { 562 for (i = 0; i < cnt; i++) { 563 (void) strcpy(prop_val[i], 564 pdp->pd_modval[i].vd_name); 565 } 566 *val_cntp = cnt; 567 } 568 break; 569 default: 570 status = DLADM_STATUS_BADARG; 571 break; 572 } 573 574 return (status); 575 } 576 577 static dladm_status_t 578 i_dladm_set_one_prop_temp(const char *name, fprop_desc_t *pdp, char **prop_val, 579 uint_t val_cnt, uint_t flags) 580 { 581 dladm_status_t status; 582 val_desc_t *vdp = NULL; 583 uint_t cnt; 584 585 if (pdp->pd_temponly && (flags & DLADM_OPT_PERSIST) != 0) 586 return (DLADM_STATUS_TEMPONLY); 587 588 if (pdp->pd_set == NULL) 589 return (DLADM_STATUS_PROPRDONLY); 590 591 if (prop_val != NULL) { 592 if (pdp->pd_check != NULL) 593 status = pdp->pd_check(pdp, prop_val, val_cnt, &vdp); 594 else 595 status = DLADM_STATUS_BADARG; 596 597 if (status != DLADM_STATUS_OK) 598 return (status); 599 600 cnt = val_cnt; 601 } else { 602 if (pdp->pd_defval.vd_name == NULL) 603 return (DLADM_STATUS_NOTSUP); 604 605 if ((vdp = malloc(sizeof (val_desc_t))) == NULL) 606 return (DLADM_STATUS_NOMEM); 607 608 (void) memcpy(vdp, &pdp->pd_defval, sizeof (val_desc_t)); 609 cnt = 1; 610 } 611 612 status = pdp->pd_set(name, vdp, cnt); 613 614 free(vdp); 615 return (status); 616 } 617 618 dladm_status_t 619 i_dladm_set_prop_temp(const char *name, const char *prop_name, char **prop_val, 620 uint_t val_cnt, uint_t flags, char **errprop, prop_table_t *prop_tbl) 621 { 622 int 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(name, pdp, prop_val, val_cnt, 636 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 659 i_dladm_is_prop_temponly(const char *prop_name, char **errprop, 660 prop_table_t *prop_tbl) 661 { 662 int 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 683 dladm_free_props(dladm_arg_list_t *list) 684 { 685 dladm_free_args(list); 686 } 687 688 dladm_status_t 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