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 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <assert.h> 30 #include <ctype.h> 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <strings.h> 36 #include <syslog.h> 37 #include <sys/stat.h> 38 #include <pthread.h> 39 #include <unistd.h> 40 #include "dlmgmt_impl.h" 41 42 typedef enum dlmgmt_db_op { 43 DLMGMT_DB_OP_WRITE, 44 DLMGMT_DB_OP_DELETE, 45 DLMGMT_DB_OP_READ 46 } dlmgmt_db_op_t; 47 48 typedef struct dlmgmt_db_req_s { 49 struct dlmgmt_db_req_s *ls_next; 50 dlmgmt_db_op_t ls_op; 51 datalink_id_t ls_linkid; 52 uint32_t ls_flags; /* Either DLMGMT_ACTIVE or */ 53 /* DLMGMT_PERSIST, not both. */ 54 } dlmgmt_db_req_t; 55 56 /* 57 * List of pending db updates (e.g., because of a read-only filesystem). 58 */ 59 static dlmgmt_db_req_t *dlmgmt_db_req_head = NULL; 60 static dlmgmt_db_req_t *dlmgmt_db_req_tail = NULL; 61 62 static int dlmgmt_db_update(dlmgmt_db_op_t, datalink_id_t, 63 uint32_t); 64 static int dlmgmt_process_db_req(dlmgmt_db_req_t *); 65 static int dlmgmt_process_db_onereq(dlmgmt_db_req_t *, boolean_t); 66 static void *dlmgmt_db_update_thread(void *); 67 static boolean_t process_link_line(char *, dlmgmt_link_t **); 68 static int process_db_write(dlmgmt_db_req_t *, FILE *, FILE *); 69 static int process_db_read(dlmgmt_db_req_t *, FILE *, FILE *); 70 static void generate_link_line(dlmgmt_link_t *, boolean_t, char *); 71 72 #define BUFLEN(lim, ptr) (((lim) > (ptr)) ? ((lim) - (ptr)) : 0) 73 #define MAXLINELEN 1024 74 75 /* 76 * Translator functions to go from dladm_datatype_t to character strings. 77 * Each function takes a pointer to a buffer, the size of the buffer, 78 * the name of the attribute, and the value to be written. The functions 79 * return the number of bytes written to the buffer. If the buffer is not big 80 * enough to hold the string representing the value, then nothing is written 81 * and 0 is returned. 82 */ 83 typedef size_t write_func_t(char *, size_t, char *, void *); 84 85 /* 86 * Translator functions to read from a NULL terminated string buffer into 87 * something of the given DLADM_TYPE_*. The functions each return the number 88 * of bytes read from the string buffer. If there is an error reading data 89 * from the buffer, then 0 is returned. It is the caller's responsibility 90 * to free the data allocated by these functions. 91 */ 92 typedef size_t read_func_t(char *, void **); 93 94 typedef struct translator_s { 95 const char *type_name; 96 write_func_t *write_func; 97 read_func_t *read_func; 98 } translator_t; 99 100 /* 101 * Translator functions, defined later but declared here so that 102 * the translator table can be defined. 103 */ 104 static write_func_t write_str, write_boolean, write_uint64; 105 static read_func_t read_str, read_boolean, read_int64; 106 107 /* 108 * Translator table, indexed by dladm_datatype_t. 109 */ 110 static translator_t translators[] = { 111 { "string", write_str, read_str }, 112 { "boolean", write_boolean, read_boolean }, 113 { "int", write_uint64, read_int64 } 114 }; 115 116 static size_t ntranslators = sizeof (translators) / sizeof (translator_t); 117 118 #define LINK_PROPERTY_DELIMINATOR ";" 119 #define LINK_PROPERTY_TYPE_VALUE_SEP "," 120 #define BASE_PROPERTY_LENGTH(t, n) (strlen(translators[(t)].type_name) +\ 121 strlen(LINK_PROPERTY_TYPE_VALUE_SEP) +\ 122 strlen(LINK_PROPERTY_DELIMINATOR) +\ 123 strlen((n))) 124 #define GENERATE_PROPERTY_STRING(buf, length, conv, name, type, val) \ 125 (snprintf((buf), (length), "%s=%s%s" conv "%s", (name), \ 126 translators[(type)].type_name, \ 127 LINK_PROPERTY_TYPE_VALUE_SEP, (val), LINK_PROPERTY_DELIMINATOR)) 128 129 #define DLMGMT_DB_OWNER 15 130 #define DLMGMT_DB_GROUP 3 131 132 /* 133 * Name of the cache file to keep the active <link name, linkid> mapping 134 */ 135 static char cachefile[MAXPATHLEN]; 136 137 #define DLMGMT_TEMP_DB_DIR "/etc/svc/volatile" 138 #define DLMGMT_PERSISTENT_DB_PATH "/etc/dladm/datalink.conf" 139 #define DLMGMT_MAKE_FILE_DB_PATH(buffer, persistent) \ 140 (void) snprintf((buffer), MAXPATHLEN, "%s", \ 141 (persistent) ? DLMGMT_PERSISTENT_DB_PATH : cachefile); 142 143 static size_t 144 write_str(char *buffer, size_t buffer_length, char *name, void *value) 145 { 146 char *ptr = value; 147 size_t data_length = strnlen(ptr, buffer_length); 148 149 /* 150 * Strings are assumed to be NULL terminated. In order to fit in 151 * the buffer, the string's length must be less then buffer_length. 152 * If the value is empty, there's no point in writing it, in fact, 153 * we shouldn't even see that case. 154 */ 155 if (data_length + BASE_PROPERTY_LENGTH(DLADM_TYPE_STR, name) == 156 buffer_length || data_length == 0) 157 return (0); 158 159 /* 160 * Since we know the string will fit in the buffer, snprintf will 161 * always return less than buffer_length, so we can just return 162 * whatever snprintf returns. 163 */ 164 return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%s", 165 name, DLADM_TYPE_STR, ptr)); 166 } 167 168 static size_t 169 write_boolean(char *buffer, size_t buffer_length, char *name, void *value) 170 { 171 boolean_t *ptr = value; 172 173 /* 174 * Booleans are either zero or one, so we only need room for two 175 * characters in the buffer. 176 */ 177 if (buffer_length <= 1 + BASE_PROPERTY_LENGTH(DLADM_TYPE_BOOLEAN, name)) 178 return (0); 179 180 return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%d", 181 name, DLADM_TYPE_BOOLEAN, *ptr)); 182 } 183 184 static size_t 185 write_uint64(char *buffer, size_t buffer_length, char *name, void *value) 186 { 187 uint64_t *ptr = value; 188 189 /* 190 * Limit checking for uint64_t is a little trickier. 191 */ 192 if (snprintf(NULL, 0, "%lld", *ptr) + 193 BASE_PROPERTY_LENGTH(DLADM_TYPE_UINT64, name) >= buffer_length) 194 return (0); 195 196 return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%lld", 197 name, DLADM_TYPE_UINT64, *ptr)); 198 } 199 200 static size_t 201 read_str(char *buffer, void **value) 202 { 203 char *ptr = calloc(MAXLINKATTRLEN, sizeof (char)); 204 ssize_t len; 205 206 if (ptr == NULL || (len = snprintf(ptr, MAXLINKATTRLEN, "%s", buffer)) 207 >= MAXLINKATTRLEN) { 208 free(ptr); 209 return (0); 210 } 211 212 *(char **)value = ptr; 213 214 /* Account for NULL terminator */ 215 return (len + 1); 216 } 217 218 static size_t 219 read_boolean(char *buffer, void **value) 220 { 221 boolean_t *ptr = calloc(1, sizeof (boolean_t)); 222 223 if (ptr == NULL) 224 return (0); 225 226 *ptr = atoi(buffer); 227 *(boolean_t **)value = ptr; 228 229 return (sizeof (boolean_t)); 230 } 231 232 static size_t 233 read_int64(char *buffer, void **value) 234 { 235 int64_t *ptr = calloc(1, sizeof (int64_t)); 236 237 if (ptr == NULL) 238 return (0); 239 240 *ptr = (int64_t)atoll(buffer); 241 *(int64_t **)value = ptr; 242 243 return (sizeof (int64_t)); 244 } 245 246 static int 247 dlmgmt_db_update(dlmgmt_db_op_t op, datalink_id_t linkid, uint32_t flags) 248 { 249 dlmgmt_db_req_t *req; 250 int err; 251 252 /* 253 * It is either a persistent request or an active request, not both. 254 */ 255 assert((flags == DLMGMT_PERSIST) || (flags == DLMGMT_ACTIVE)); 256 257 if ((req = malloc(sizeof (dlmgmt_db_req_t))) == NULL) 258 return (ENOMEM); 259 260 req->ls_next = NULL; 261 req->ls_op = op; 262 req->ls_linkid = linkid; 263 req->ls_flags = flags; 264 265 /* 266 * If the return error is EINPROGRESS, this request is handled 267 * asynchronously; return success. 268 */ 269 err = dlmgmt_process_db_req(req); 270 if (err != EINPROGRESS) 271 free(req); 272 else 273 err = 0; 274 return (err); 275 } 276 277 #define DLMGMT_DB_OP_STR(op) \ 278 (((op) == DLMGMT_DB_OP_READ) ? "read" : \ 279 (((op) == DLMGMT_DB_OP_WRITE) ? "write" : "delete")) 280 281 #define DLMGMT_DB_CONF_STR(flag) \ 282 (((flag) == DLMGMT_ACTIVE) ? "active" : \ 283 (((flag) == DLMGMT_PERSIST) ? "persistent" : "")) 284 285 static int 286 dlmgmt_process_db_req(dlmgmt_db_req_t *req) 287 { 288 pthread_t tid; 289 boolean_t writeop; 290 int err; 291 292 /* 293 * If there are already pending "write" requests, queue this request in 294 * the pending list. Note that this function is called while the 295 * dlmgmt_rw_lock is held, so it is safe to access the global variables. 296 */ 297 writeop = (req->ls_op != DLMGMT_DB_OP_READ); 298 if (writeop && (req->ls_flags == DLMGMT_PERSIST) && 299 (dlmgmt_db_req_head != NULL)) { 300 dlmgmt_db_req_tail->ls_next = req; 301 dlmgmt_db_req_tail = req; 302 return (EINPROGRESS); 303 } 304 305 err = dlmgmt_process_db_onereq(req, writeop); 306 if (err != EINPROGRESS && err != 0 && 307 (req->ls_flags != DLMGMT_ACTIVE || errno != ENOENT)) { 308 309 /* 310 * Log the error unless the request processing: 311 * - is successful; 312 * - is still in progress; 313 * - has failed with ENOENT because the active configuration 314 * file is not created yet; 315 */ 316 dlmgmt_log(LOG_WARNING, "dlmgmt_process_db_onereq() %s " 317 "operation on %s configuration failed: %s", 318 DLMGMT_DB_OP_STR(req->ls_op), 319 DLMGMT_DB_CONF_STR(req->ls_flags), strerror(err)); 320 } 321 322 if (err == EINPROGRESS) { 323 assert(req->ls_flags == DLMGMT_PERSIST); 324 assert(writeop && dlmgmt_db_req_head == NULL); 325 dlmgmt_db_req_tail = dlmgmt_db_req_head = req; 326 err = pthread_create(&tid, NULL, dlmgmt_db_update_thread, NULL); 327 if (err == 0) 328 return (EINPROGRESS); 329 } 330 return (err); 331 } 332 333 static int 334 dlmgmt_process_db_onereq(dlmgmt_db_req_t *req, boolean_t writeop) 335 { 336 int err = 0; 337 FILE *fp, *nfp = NULL; 338 char file[MAXPATHLEN]; 339 char newfile[MAXPATHLEN]; 340 int nfd; 341 342 DLMGMT_MAKE_FILE_DB_PATH(file, (req->ls_flags == DLMGMT_PERSIST)); 343 if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) { 344 if (writeop && errno == EROFS) { 345 /* 346 * This can happen at boot when the file system is 347 * read-only. So add this request to the pending 348 * request list and start a retry thread. 349 */ 350 return (EINPROGRESS); 351 } else if (req->ls_flags == DLMGMT_ACTIVE && errno == ENOENT) { 352 /* 353 * It is fine if the file keeping active configuration 354 * does not exist. This happens during a new reboot. 355 */ 356 if (!writeop) 357 return (ENOENT); 358 /* 359 * If this is an update request for the active 360 * configuration, create the file. 361 */ 362 if ((fp = fopen(file, "w")) == NULL) 363 return (errno == EROFS ? EINPROGRESS : errno); 364 } else { 365 return (errno); 366 } 367 } 368 369 if (writeop) { 370 (void) snprintf(newfile, MAXPATHLEN, "%s.new", file); 371 if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC, 372 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) { 373 (void) fclose(fp); 374 return (errno); 375 } 376 377 if ((nfp = fdopen(nfd, "w")) == NULL) { 378 (void) close(nfd); 379 (void) fclose(fp); 380 (void) unlink(newfile); 381 return (errno); 382 } 383 } 384 if (writeop) 385 err = process_db_write(req, fp, nfp); 386 else 387 err = process_db_read(req, fp, nfp); 388 if (!writeop || err != 0) 389 goto done; 390 391 /* 392 * Configuration files need to be owned by the 'dladm' user. 393 * If we are invoked by root, the file ownership needs to be fixed. 394 */ 395 if (getuid() == 0 || geteuid() == 0) { 396 if (fchown(nfd, DLMGMT_DB_OWNER, DLMGMT_DB_GROUP) < 0) { 397 err = errno; 398 goto done; 399 } 400 } 401 402 if (fflush(nfp) == EOF) { 403 err = errno; 404 goto done; 405 } 406 (void) fclose(fp); 407 (void) fclose(nfp); 408 409 if (rename(newfile, file) < 0) { 410 (void) unlink(newfile); 411 return (errno); 412 } 413 414 return (0); 415 416 done: 417 if (nfp != NULL) { 418 (void) fclose(nfp); 419 if (err != 0) 420 (void) unlink(newfile); 421 } 422 (void) fclose(fp); 423 return (err); 424 } 425 426 /*ARGSUSED*/ 427 static void * 428 dlmgmt_db_update_thread(void *arg) 429 { 430 dlmgmt_db_req_t *req; 431 int err = 0; 432 433 dlmgmt_table_lock(B_TRUE); 434 435 assert(dlmgmt_db_req_head != NULL); 436 while ((req = dlmgmt_db_req_head) != NULL) { 437 assert(req->ls_flags == DLMGMT_PERSIST); 438 err = dlmgmt_process_db_onereq(req, B_TRUE); 439 if (err == EINPROGRESS) { 440 /* 441 * The filesystem is still read only. Go to sleep and 442 * try again. 443 */ 444 dlmgmt_table_unlock(); 445 (void) sleep(5); 446 dlmgmt_table_lock(B_TRUE); 447 continue; 448 } 449 450 /* 451 * The filesystem is no longer read only. Continue processing 452 * and remove the request from the pending list. 453 */ 454 dlmgmt_db_req_head = req->ls_next; 455 if (dlmgmt_db_req_tail == req) { 456 assert(dlmgmt_db_req_head == NULL); 457 dlmgmt_db_req_tail = NULL; 458 } 459 free(req); 460 } 461 462 dlmgmt_table_unlock(); 463 return (NULL); 464 } 465 466 static int 467 parse_linkprops(char *buf, dlmgmt_link_t *linkp) 468 { 469 boolean_t found_type = B_FALSE; 470 dladm_datatype_t type = DLADM_TYPE_STR; 471 int i, len; 472 int err = 0; 473 char *curr; 474 char attr_name[MAXLINKATTRLEN]; 475 size_t attr_buf_len = 0; 476 void *attr_buf = NULL; 477 478 curr = buf; 479 len = strlen(buf); 480 attr_name[0] = '\0'; 481 for (i = 0; i < len && err == 0; i++) { 482 char c = buf[i]; 483 boolean_t match = (c == '=' || 484 (c == ',' && !found_type) || c == ';'); 485 486 /* 487 * Move to the next character if there is no match and 488 * if we have not reached the last character. 489 */ 490 if (!match && i != len - 1) 491 continue; 492 493 if (match) { 494 /* 495 * NUL-terminate the string pointed to by 'curr'. 496 */ 497 buf[i] = '\0'; 498 if (*curr == '\0') 499 goto parse_fail; 500 } 501 502 if (attr_name[0] != '\0' && found_type) { 503 /* 504 * We get here after we have processed the "<prop>=" 505 * pattern. The pattern we are now interested in is 506 * "<val>;". 507 */ 508 if (c == '=') 509 goto parse_fail; 510 511 if (strcmp(attr_name, "name") == 0) { 512 (void) read_str(curr, &attr_buf); 513 (void) snprintf(linkp->ll_link, 514 MAXLINKNAMELEN, "%s", attr_buf); 515 } else if (strcmp(attr_name, "class") == 0) { 516 (void) read_int64(curr, &attr_buf); 517 linkp->ll_class = 518 (datalink_class_t)*(int64_t *)attr_buf; 519 } else if (strcmp(attr_name, "media") == 0) { 520 (void) read_int64(curr, &attr_buf); 521 linkp->ll_media = 522 (uint32_t)*(int64_t *)attr_buf; 523 } else { 524 attr_buf_len = translators[type].read_func(curr, 525 &attr_buf); 526 err = linkattr_set(&(linkp->ll_head), attr_name, 527 attr_buf, attr_buf_len, type); 528 } 529 530 free(attr_buf); 531 attr_name[0] = '\0'; 532 found_type = B_FALSE; 533 } else if (attr_name[0] != '\0') { 534 /* 535 * Non-zero length attr_name and found_type of false 536 * indicates that we have not found the type for this 537 * attribute. The pattern now is "<type>,<val>;", we 538 * want the <type> part of the pattern. 539 */ 540 for (type = 0; type < ntranslators; type++) { 541 if (strcmp(curr, 542 translators[type].type_name) == 0) { 543 found_type = B_TRUE; 544 break; 545 } 546 } 547 548 if (!found_type) 549 goto parse_fail; 550 } else { 551 /* 552 * A zero length attr_name indicates we are looking 553 * at the beginning of a link attribute. 554 */ 555 if (c != '=') 556 goto parse_fail; 557 558 (void) snprintf(attr_name, MAXLINKATTRLEN, "%s", curr); 559 } 560 curr = buf + i + 1; 561 } 562 563 return (err); 564 565 parse_fail: 566 return (-1); 567 } 568 569 static boolean_t 570 process_link_line(char *buf, dlmgmt_link_t **linkpp) 571 { 572 dlmgmt_link_t *linkp; 573 int i, len, llen; 574 char *str, *lasts; 575 char tmpbuf[MAXLINELEN]; 576 577 /* 578 * Use a copy of buf for parsing so that we can do whatever we want. 579 */ 580 (void) strlcpy(tmpbuf, buf, MAXLINELEN); 581 582 /* 583 * Skip leading spaces, blank lines, and comments. 584 */ 585 len = strlen(tmpbuf); 586 for (i = 0; i < len; i++) { 587 if (!isspace(tmpbuf[i])) 588 break; 589 } 590 if (i == len || tmpbuf[i] == '#') { 591 *linkpp = NULL; 592 return (B_TRUE); 593 } 594 595 linkp = calloc(1, sizeof (dlmgmt_link_t)); 596 if (linkp == NULL) 597 goto fail; 598 599 str = tmpbuf + i; 600 /* 601 * Find the link id and assign it to the link structure. 602 */ 603 if (strtok_r(str, " \n\t", &lasts) == NULL) 604 goto fail; 605 606 llen = strlen(str); 607 linkp->ll_linkid = atoi(str); 608 609 str += llen + 1; 610 if (str >= tmpbuf + len) 611 goto fail; 612 613 /* 614 * Now find the list of link properties. 615 */ 616 if ((str = strtok_r(str, " \n\t", &lasts)) == NULL) 617 goto fail; 618 619 if (parse_linkprops(str, linkp) < 0) 620 goto fail; 621 622 *linkpp = linkp; 623 return (B_TRUE); 624 625 fail: 626 link_destroy(linkp); 627 628 /* 629 * Delete corrupted line. 630 */ 631 buf[0] = '\0'; 632 return (B_FALSE); 633 } 634 635 static int 636 process_db_write(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp) 637 { 638 boolean_t done = B_FALSE; 639 int err = 0; 640 dlmgmt_link_t *linkp, *link_in_file, link; 641 char buf[MAXLINELEN]; 642 643 if (req->ls_op == DLMGMT_DB_OP_WRITE) { 644 /* 645 * find the link in the avl tree with the given linkid. 646 */ 647 link.ll_linkid = req->ls_linkid; 648 linkp = avl_find(&dlmgmt_id_avl, &link, NULL); 649 if (linkp == NULL || (linkp->ll_flags & req->ls_flags) == 0) { 650 /* 651 * This link has already been changed. This could 652 * happen if the request is pending because of 653 * read-only file-system. If so, we are done. 654 */ 655 return (0); 656 } 657 } 658 659 while (err == 0 && fgets(buf, sizeof (buf), fp) != NULL && 660 process_link_line(buf, &link_in_file)) { 661 if (link_in_file == NULL || done) { 662 /* 663 * this is a comment line, write it out. 664 */ 665 if (fputs(buf, nfp) == EOF) 666 err = errno; 667 continue; 668 } 669 670 switch (req->ls_op) { 671 case DLMGMT_DB_OP_WRITE: 672 /* 673 * For write operations, if the linkid of the link 674 * read from the file does not match the id of what 675 * req->ll_linkid points to, write out the buffer. 676 * Otherwise, generate a new line. If we get to the 677 * end and have not seen what req->ll_linkid points 678 * to, write it out then. 679 */ 680 if (linkp == NULL || 681 linkp->ll_linkid != link_in_file->ll_linkid) { 682 if (fputs(buf, nfp) == EOF) 683 err = errno; 684 } else { 685 generate_link_line(linkp, 686 req->ls_flags == DLMGMT_PERSIST, buf); 687 if (fputs(buf, nfp) == EOF) 688 err = errno; 689 done = B_TRUE; 690 } 691 break; 692 case DLMGMT_DB_OP_DELETE: 693 /* 694 * Delete is simple. If buf does not represent the 695 * link we're deleting, write it out. 696 */ 697 if (req->ls_linkid != link_in_file->ll_linkid) { 698 if (fputs(buf, nfp) == EOF) 699 err = errno; 700 } else { 701 done = B_TRUE; 702 } 703 break; 704 case DLMGMT_DB_OP_READ: 705 default: 706 err = EINVAL; 707 break; 708 } 709 link_destroy(link_in_file); 710 } 711 712 /* 713 * If we get to the end of the file and have not seen what 714 * req->ll_linkid points to, write it out then. 715 */ 716 if (req->ls_op == DLMGMT_DB_OP_WRITE && !done) { 717 generate_link_line(linkp, req->ls_flags == DLMGMT_PERSIST, buf); 718 done = B_TRUE; 719 if (fputs(buf, nfp) == EOF) 720 err = errno; 721 } 722 723 if (!done) 724 err = ENOENT; 725 726 return (err); 727 } 728 729 /* ARGSUSED1 */ 730 static int 731 process_db_read(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp) 732 { 733 avl_index_t name_where, id_where; 734 dlmgmt_link_t *link_in_file; 735 dlmgmt_link_t *linkp1, *linkp2; 736 char buf[MAXLINELEN]; 737 int err = 0; 738 739 /* 740 * This loop processes each line of the configuration file. 741 */ 742 while (fgets(buf, MAXLINELEN, fp) != NULL) { 743 if (!process_link_line(buf, &link_in_file)) { 744 err = EINVAL; 745 break; 746 } 747 748 /* 749 * Skip the comment line. 750 */ 751 if (link_in_file == NULL) 752 continue; 753 754 linkp1 = avl_find(&dlmgmt_name_avl, link_in_file, &name_where); 755 linkp2 = avl_find(&dlmgmt_id_avl, link_in_file, &id_where); 756 if ((linkp1 != NULL) || (linkp2 != NULL)) { 757 /* 758 * If any of the following conditions are met, this is 759 * a duplicate entry: 760 * 761 * 1. link2 (with the given name) and link2 (with the 762 * given id) are not the same link; 763 * 2. This is a persistent req and find the link with 764 * the given name and id. Note that persistent db 765 * is read before the active one. 766 * 3. Found the link with the given name and id but 767 * the link is already active. 768 */ 769 if ((linkp1 != linkp2) || 770 (req->ls_flags == DLMGMT_PERSIST) || 771 ((linkp1->ll_flags & DLMGMT_ACTIVE) != 0)) { 772 dlmgmt_log(LOG_WARNING, "Duplicate link " 773 "entries in repository: link name %s " 774 "link id %i", link_in_file->ll_link, 775 link_in_file->ll_linkid); 776 } else { 777 linkp1->ll_flags |= DLMGMT_ACTIVE; 778 } 779 link_destroy(link_in_file); 780 } else { 781 avl_insert(&dlmgmt_name_avl, link_in_file, name_where); 782 avl_insert(&dlmgmt_id_avl, link_in_file, id_where); 783 dlmgmt_advance(link_in_file); 784 link_in_file->ll_flags |= req->ls_flags; 785 } 786 } 787 788 return (err); 789 } 790 791 /* 792 * Generate an entry in the link database. 793 * Each entry has this format: 794 * <link id> <prop0>=<type>,<val>;...;<propn>=<type>,<val>; 795 */ 796 static void 797 generate_link_line(dlmgmt_link_t *linkp, boolean_t persist, char *buf) 798 { 799 char tmpbuf[MAXLINELEN]; 800 char *ptr; 801 char *lim = tmpbuf + MAXLINELEN; 802 char *name_to_write = NULL; 803 datalink_id_t id_to_write; 804 dlmgmt_linkattr_t *cur_p = NULL; 805 uint64_t u64; 806 807 ptr = tmpbuf; 808 id_to_write = linkp->ll_linkid; 809 ptr += snprintf(ptr, BUFLEN(lim, ptr), "%d\t", id_to_write); 810 name_to_write = linkp->ll_link; 811 ptr += write_str(ptr, BUFLEN(lim, ptr), "name", name_to_write); 812 u64 = linkp->ll_class; 813 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "class", &u64); 814 u64 = linkp->ll_media; 815 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "media", &u64); 816 817 /* 818 * The daemon does not keep any active link attribute. If this request 819 * is for active configuration, we are done. 820 */ 821 if (!persist) 822 goto done; 823 824 for (cur_p = linkp->ll_head; cur_p != NULL; cur_p = cur_p->lp_next) { 825 ptr += translators[cur_p->lp_type].write_func(ptr, 826 BUFLEN(lim, ptr), cur_p->lp_name, cur_p->lp_val); 827 } 828 done: 829 if (ptr > lim) 830 return; 831 (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf); 832 } 833 834 int 835 dlmgmt_delete_db_entry(datalink_id_t linkid, uint32_t flags) 836 { 837 return (dlmgmt_db_update(DLMGMT_DB_OP_DELETE, linkid, flags)); 838 } 839 840 int 841 dlmgmt_write_db_entry(datalink_id_t linkid, uint32_t flags) 842 { 843 int err; 844 845 if (flags & DLMGMT_PERSIST) { 846 if ((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, 847 linkid, DLMGMT_PERSIST)) != 0) { 848 return (err); 849 } 850 } 851 852 if (flags & DLMGMT_ACTIVE) { 853 if (((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, 854 linkid, DLMGMT_ACTIVE)) != 0) && 855 (flags & DLMGMT_PERSIST)) { 856 (void) dlmgmt_db_update(DLMGMT_DB_OP_DELETE, 857 linkid, DLMGMT_PERSIST); 858 return (err); 859 } 860 } 861 862 return (0); 863 } 864 865 /* 866 * Initialize the datalink <link name, linkid> mapping and the link's 867 * attributes list based on the configuration file /etc/dladm/datalink.conf 868 * and the active configuration cache file 869 * /etc/svc/volatile/datalink-management:default.cache. 870 * 871 * This function is called when the datalink-management service is started 872 * during reboot, and when the dlmgmtd daemon is restarted. 873 */ 874 int 875 dlmgmt_db_init() 876 { 877 char filename[MAXPATHLEN]; 878 dlmgmt_db_req_t req; 879 int err; 880 dlmgmt_link_t *linkp; 881 char *fmri, *c; 882 883 /* 884 * First derive the name of the cache file from the FMRI name. This 885 * cache name is used to keep active datalink configuration. 886 */ 887 if (debug) { 888 (void) snprintf(cachefile, MAXPATHLEN, "%s/%s%s", 889 DLMGMT_TEMP_DB_DIR, progname, ".debug.cache"); 890 } else { 891 if ((fmri = getenv("SMF_FMRI")) == NULL) { 892 dlmgmt_log(LOG_WARNING, "dlmgmtd is an smf(5) managed " 893 "service and should not be run from the command " 894 "line."); 895 return (EINVAL); 896 } 897 898 /* 899 * The FMRI name is in the form of 900 * svc:/service/service:instance. We need to remove the 901 * prefix "svc:/" and replace '/' with '-'. The cache file 902 * name is in the form of "service:instance.cache". 903 */ 904 if ((c = strchr(fmri, '/')) != NULL) 905 c++; 906 else 907 c = fmri; 908 (void) snprintf(filename, MAXPATHLEN, "%s.cache", c); 909 for (c = filename; *c != '\0'; c++) { 910 if (*c == '/') 911 *c = '-'; 912 } 913 914 (void) snprintf(cachefile, MAXPATHLEN, "%s/%s", 915 DLMGMT_TEMP_DB_DIR, filename); 916 } 917 918 dlmgmt_table_lock(B_TRUE); 919 920 req.ls_next = NULL; 921 req.ls_op = DLMGMT_DB_OP_READ; 922 req.ls_linkid = DATALINK_INVALID_LINKID; 923 req.ls_flags = DLMGMT_PERSIST; 924 925 if ((err = dlmgmt_process_db_req(&req)) != 0) 926 goto done; 927 928 req.ls_flags = DLMGMT_ACTIVE; 929 err = dlmgmt_process_db_req(&req); 930 if (err == ENOENT) { 931 /* 932 * The temporary datalink.conf does not exist. This is 933 * the first boot. Mark all the physical links active. 934 */ 935 for (linkp = avl_first(&dlmgmt_id_avl); linkp != NULL; 936 linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) { 937 if (linkp->ll_class == DATALINK_CLASS_PHYS) { 938 linkp->ll_flags |= DLMGMT_ACTIVE; 939 (void) dlmgmt_write_db_entry( 940 linkp->ll_linkid, DLMGMT_ACTIVE); 941 } 942 } 943 err = 0; 944 } 945 946 done: 947 dlmgmt_table_unlock(); 948 return (err); 949 } 950