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 /* 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) 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 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 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 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 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 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 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 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 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 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 686 dladm_free_props(dladm_arg_list_t *list) 687 { 688 dladm_free_args(list); 689 } 690 691 dladm_status_t 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