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(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 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 boolean_t 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 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 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 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 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 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 int 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 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 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 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(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 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