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 (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * Contains DB walker functions, which are of type `db_wfunc_t'; 28 * 29 * typedef boolean_t db_wfunc_t(void *cbarg, nvlist_t *db_nvl, char *buf, 30 * size_t bufsize, int *errp); 31 * 32 * ipadm_rw_db() walks through the data store, one line at a time and calls 33 * these call back functions with: 34 * `cbarg' - callback argument 35 * `db_nvl' - representing a line from DB in nvlist_t form 36 * `buf' - character buffer to hold modified line 37 * `bufsize'- size of the buffer 38 * `errp' - captures any error inside the walker function. 39 * 40 * All the 'write' callback functions modify `db_nvl' based on `cbarg' and 41 * copy string representation of `db_nvl' (using ipadm_nvlist2str()) into `buf'. 42 * To delete a line from the DB, buf[0] is set to `\0'. Inside ipadm_rw_db(), 43 * the modified `buf' is written back into DB. 44 * 45 * All the 'read' callback functions, retrieve the information from the DB, by 46 * reading `db_nvl' and then populate the `cbarg'. 47 */ 48 49 #include <stdlib.h> 50 #include <strings.h> 51 #include <errno.h> 52 #include <assert.h> 53 #include <sys/types.h> 54 #include <sys/socket.h> 55 #include <netinet/in.h> 56 #include <arpa/inet.h> 57 #include <unistd.h> 58 #include "ipmgmt_impl.h" 59 #include <libscf.h> 60 61 #define ATYPE "_atype" /* name of the address type nvpair */ 62 #define FLAGS "_flags" /* name of the flags nvpair */ 63 64 /* 65 * flag used by ipmgmt_persist_aobjmap() to indicate address type is 66 * IPADM_ADDR_IPV6_ADDRCONF. 67 */ 68 #define IPMGMT_ATYPE_V6ACONF 0x1 69 70 extern pthread_rwlock_t ipmgmt_dbconf_lock; 71 72 /* 73 * Checks if the database nvl, `db_nvl' contains and matches ALL of the passed 74 * in private nvpairs `proto', `ifname' & `aobjname'. 75 */ 76 static boolean_t 77 ipmgmt_nvlist_match(nvlist_t *db_nvl, const char *proto, const char *ifname, 78 const char *aobjname) 79 { 80 char *db_proto = NULL, *db_ifname = NULL; 81 char *db_aobjname = NULL; 82 nvpair_t *nvp; 83 char *name; 84 85 /* walk through db_nvl and retrieve all its private nvpairs */ 86 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; 87 nvp = nvlist_next_nvpair(db_nvl, nvp)) { 88 name = nvpair_name(nvp); 89 if (strcmp(IPADM_NVP_PROTONAME, name) == 0) 90 (void) nvpair_value_string(nvp, &db_proto); 91 else if (strcmp(IPADM_NVP_IFNAME, name) == 0) 92 (void) nvpair_value_string(nvp, &db_ifname); 93 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) 94 (void) nvpair_value_string(nvp, &db_aobjname); 95 } 96 97 if (proto != NULL && proto[0] == '\0') 98 proto = NULL; 99 if (ifname != NULL && ifname[0] == '\0') 100 ifname = NULL; 101 if (aobjname != NULL && aobjname[0] == '\0') 102 aobjname = NULL; 103 104 if ((proto == NULL && db_proto != NULL) || 105 (proto != NULL && db_proto == NULL) || 106 strcmp(proto, db_proto) != 0) { 107 /* no intersection - different protocols. */ 108 return (B_FALSE); 109 } 110 if ((ifname == NULL && db_ifname != NULL) || 111 (ifname != NULL && db_ifname == NULL) || 112 strcmp(ifname, db_ifname) != 0) { 113 /* no intersection - different interfaces. */ 114 return (B_FALSE); 115 } 116 if ((aobjname == NULL && db_aobjname != NULL) || 117 (aobjname != NULL && db_aobjname == NULL) || 118 strcmp(aobjname, db_aobjname) != 0) { 119 /* no intersection - different address objects */ 120 return (B_FALSE); 121 } 122 123 return (B_TRUE); 124 } 125 126 /* 127 * Checks if the database nvl, `db_nvl' and the input nvl, `in_nvl' intersects. 128 */ 129 static boolean_t 130 ipmgmt_nvlist_intersects(nvlist_t *db_nvl, nvlist_t *in_nvl) 131 { 132 nvpair_t *nvp; 133 char *name; 134 char *proto = NULL, *ifname = NULL, *aobjname = NULL; 135 136 /* walk through in_nvl and retrieve all its private nvpairs */ 137 for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL; 138 nvp = nvlist_next_nvpair(in_nvl, nvp)) { 139 name = nvpair_name(nvp); 140 if (strcmp(IPADM_NVP_PROTONAME, name) == 0) 141 (void) nvpair_value_string(nvp, &proto); 142 else if (strcmp(IPADM_NVP_IFNAME, name) == 0) 143 (void) nvpair_value_string(nvp, &ifname); 144 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) 145 (void) nvpair_value_string(nvp, &aobjname); 146 } 147 148 return (ipmgmt_nvlist_match(db_nvl, proto, ifname, aobjname)); 149 } 150 151 /* 152 * Checks if the database nvl, `db_nvl', contains and matches ANY of the passed 153 * in private nvpairs `proto', `ifname' & `aobjname'. 154 */ 155 static boolean_t 156 ipmgmt_nvlist_contains(nvlist_t *db_nvl, const char *proto, 157 const char *ifname, char *aobjname) 158 { 159 char *db_ifname = NULL, *db_proto = NULL; 160 char *db_aobjname = NULL; 161 nvpair_t *nvp; 162 char *name; 163 164 /* walk through db_nvl and retrieve all private nvpairs */ 165 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; 166 nvp = nvlist_next_nvpair(db_nvl, nvp)) { 167 name = nvpair_name(nvp); 168 if (strcmp(IPADM_NVP_PROTONAME, name) == 0) 169 (void) nvpair_value_string(nvp, &db_proto); 170 else if (strcmp(IPADM_NVP_IFNAME, name) == 0) 171 (void) nvpair_value_string(nvp, &db_ifname); 172 else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) 173 (void) nvpair_value_string(nvp, &db_aobjname); 174 } 175 176 if (proto != NULL && proto[0] != '\0') { 177 if ((db_proto == NULL || strcmp(proto, db_proto) != 0)) 178 return (B_FALSE); 179 } 180 if (ifname != NULL && ifname[0] != '\0') { 181 if ((db_ifname == NULL || strcmp(ifname, db_ifname) != 0)) 182 return (B_FALSE); 183 } 184 if (aobjname != NULL && aobjname[0] != '\0') { 185 if ((db_aobjname == NULL || strcmp(aobjname, db_aobjname) != 0)) 186 return (B_FALSE); 187 } 188 189 return (B_TRUE); 190 } 191 192 /* 193 * Retrieves the property value from the DB. The property whose value is to be 194 * retrieved is in `pargp->ia_pname'. 195 */ 196 /* ARGSUSED */ 197 boolean_t 198 ipmgmt_db_getprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 199 int *errp) 200 { 201 ipmgmt_prop_arg_t *pargp = arg; 202 boolean_t cont = B_TRUE; 203 char *pval; 204 int err = 0; 205 206 *errp = 0; 207 208 if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module, 209 pargp->ia_ifname, pargp->ia_aobjname)) 210 return (B_TRUE); 211 212 if ((err = nvlist_lookup_string(db_nvl, pargp->ia_pname, 213 &pval)) == 0) { 214 (void) strlcpy(pargp->ia_pval, pval, sizeof (pargp->ia_pval)); 215 /* 216 * We have retrieved what we are looking for. 217 * Stop the walker. 218 */ 219 cont = B_FALSE; 220 } else { 221 if (err == ENOENT) 222 err = 0; 223 *errp = err; 224 } 225 226 return (cont); 227 } 228 229 /* 230 * Removes the property value from the DB. The property whose value is to be 231 * removed is in `pargp->ia_pname'. 232 */ 233 /* ARGSUSED */ 234 boolean_t 235 ipmgmt_db_resetprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 236 int *errp) 237 { 238 ipmgmt_prop_arg_t *pargp = arg; 239 240 *errp = 0; 241 if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module, 242 pargp->ia_ifname, pargp->ia_aobjname)) 243 return (B_TRUE); 244 245 if (!nvlist_exists(db_nvl, pargp->ia_pname)) 246 return (B_TRUE); 247 248 /* 249 * We found the property in the DB. If IPMGMT_REMOVE is not set then 250 * delete the entry from the db. If it is set, then the property is a 251 * multi-valued property so just remove the specified values from DB. 252 */ 253 if (pargp->ia_flags & IPMGMT_REMOVE) { 254 char *dbpval = NULL; 255 char *inpval = pargp->ia_pval; 256 char pval[MAXPROPVALLEN]; 257 char *val, *lasts; 258 259 *errp = nvlist_lookup_string(db_nvl, pargp->ia_pname, &dbpval); 260 if (*errp != 0) 261 return (B_FALSE); 262 263 /* 264 * multi-valued properties are represented as comma separated 265 * values. Use string tokenizer functions to split them and 266 * search for the value to be removed. 267 */ 268 bzero(pval, sizeof (pval)); 269 if ((val = strtok_r(dbpval, ",", &lasts)) != NULL) { 270 if (strcmp(val, inpval) != 0) 271 (void) strlcat(pval, val, MAXPROPVALLEN); 272 while ((val = strtok_r(NULL, ",", &lasts)) != NULL) { 273 if (strcmp(val, inpval) != 0) { 274 if (pval[0] != '\0') 275 (void) strlcat(pval, ",", 276 MAXPROPVALLEN); 277 (void) strlcat(pval, val, 278 MAXPROPVALLEN); 279 } 280 } 281 } else { 282 if (strcmp(dbpval, inpval) != 0) 283 *errp = ENOENT; 284 else 285 buf[0] = '\0'; 286 return (B_FALSE); 287 } 288 *errp = nvlist_add_string(db_nvl, pargp->ia_pname, pval); 289 if (*errp != 0) 290 return (B_FALSE); 291 292 (void) memset(buf, 0, buflen); 293 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) { 294 /* buffer overflow */ 295 *errp = ENOBUFS; 296 } 297 } else { 298 buf[0] = '\0'; 299 } 300 301 /* stop the search */ 302 return (B_FALSE); 303 } 304 305 /* 306 * Input arguments can have IPADM_NVP_AOBJNAME or IPADM_NVP_IFNAME. A match is 307 * found, when one of the following occurs first. 308 * - the input aobjname matches the db aobjname. Return the db address. 309 * - the input interface matches the db interface. Return all the 310 * matching db lines with addresses. 311 */ 312 /* ARGSUSED */ 313 boolean_t 314 ipmgmt_db_getaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 315 int *errp) 316 { 317 ipmgmt_getaddr_cbarg_t *cbarg = arg; 318 char *db_aobjname = NULL; 319 char *db_ifname = NULL; 320 nvlist_t *db_addr = NULL; 321 char name[IPMGMT_STRSIZE]; 322 nvpair_t *nvp; 323 boolean_t add_nvl = B_FALSE; 324 325 /* Parse db nvlist */ 326 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; 327 nvp = nvlist_next_nvpair(db_nvl, nvp)) { 328 if (nvpair_type(nvp) == DATA_TYPE_NVLIST) 329 (void) nvpair_value_nvlist(nvp, &db_addr); 330 else if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0) 331 (void) nvpair_value_string(nvp, &db_ifname); 332 else if (strcmp(nvpair_name(nvp), IPADM_NVP_AOBJNAME) == 0) 333 (void) nvpair_value_string(nvp, &db_aobjname); 334 } 335 336 if (db_aobjname == NULL) /* Not an address */ 337 return (B_TRUE); 338 339 /* Check for a match between the aobjnames or the interface name */ 340 if (cbarg->cb_aobjname[0] != '\0') { 341 if (strcmp(cbarg->cb_aobjname, db_aobjname) == 0) 342 add_nvl = B_TRUE; 343 } else if (cbarg->cb_ifname[0] != '\0') { 344 if (strcmp(cbarg->cb_ifname, db_ifname) == 0) 345 add_nvl = B_TRUE; 346 } else { 347 add_nvl = B_TRUE; 348 } 349 350 if (add_nvl) { 351 (void) snprintf(name, sizeof (name), "%s_%d", db_ifname, 352 cbarg->cb_ocnt); 353 *errp = nvlist_add_nvlist(cbarg->cb_onvl, name, db_nvl); 354 if (*errp == 0) 355 cbarg->cb_ocnt++; 356 } 357 return (B_TRUE); 358 } 359 360 /* 361 * This function takes the appropriate lock, read or write, based on the 362 * `db_op' and then calls DB walker ipadm_rw_db(). 363 */ 364 extern int 365 ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op) 366 { 367 int err; 368 boolean_t writeop; 369 mode_t mode; 370 371 writeop = (db_op != IPADM_DB_READ); 372 373 if (writeop) { 374 (void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock); 375 mode = IPADM_FILE_MODE; 376 } else { 377 (void) pthread_rwlock_rdlock(&ipmgmt_dbconf_lock); 378 mode = 0; 379 } 380 381 err = ipadm_rw_db(db_walk_func, db_warg, IPADM_DB_FILE, mode, db_op); 382 (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock); 383 return (err); 384 } 385 386 /* 387 * Used to add an entry towards the end of DB. It just returns B_TRUE for 388 * every line of the DB. When we reach the end, ipadm_rw_db() adds the 389 * line at the end. 390 */ 391 /* ARGSUSED */ 392 boolean_t 393 ipmgmt_db_add(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, int *errp) 394 { 395 return (B_TRUE); 396 } 397 398 /* 399 * This function is used to update or create an entry in DB. The nvlist_t, 400 * `in_nvl', represents the line we are looking for. Once we ensure the right 401 * line from DB, we update that entry. 402 */ 403 boolean_t 404 ipmgmt_db_update(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 405 int *errp) 406 { 407 ipadm_dbwrite_cbarg_t *cb = arg; 408 uint_t flags = cb->dbw_flags; 409 nvlist_t *in_nvl = cb->dbw_nvl; 410 nvpair_t *nvp; 411 char *name, *instrval = NULL, *dbstrval = NULL; 412 char pval[MAXPROPVALLEN]; 413 414 if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl)) 415 return (B_TRUE); 416 417 for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL; 418 nvp = nvlist_next_nvpair(in_nvl, nvp)) { 419 name = nvpair_name(nvp); 420 if (!IPADM_PRIV_NVP(name) && nvlist_exists(db_nvl, name)) 421 break; 422 } 423 424 if (nvp == NULL) 425 return (B_TRUE); 426 427 assert(nvpair_type(nvp) == DATA_TYPE_STRING); 428 429 if ((*errp = nvpair_value_string(nvp, &instrval)) != 0) 430 return (B_FALSE); 431 432 /* 433 * If IPMGMT_APPEND is set then we are dealing with multi-valued 434 * properties. We append to the entry from the db, with the new value. 435 */ 436 if (flags & IPMGMT_APPEND) { 437 if ((*errp = nvlist_lookup_string(db_nvl, name, 438 &dbstrval)) != 0) 439 return (B_FALSE); 440 (void) snprintf(pval, MAXPROPVALLEN, "%s,%s", dbstrval, 441 instrval); 442 if ((*errp = nvlist_add_string(db_nvl, name, pval)) != 0) 443 return (B_FALSE); 444 } else { 445 /* case of in-line update of a db entry */ 446 if ((*errp = nvlist_add_string(db_nvl, name, instrval)) != 0) 447 return (B_FALSE); 448 } 449 450 (void) memset(buf, 0, buflen); 451 if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) { 452 /* buffer overflow */ 453 *errp = ENOBUFS; 454 } 455 *errp = 0; 456 457 /* we updated the DB entry, so do not continue */ 458 return (B_FALSE); 459 } 460 461 /* 462 * For the given `cbarg->cb_ifname' interface, retrieves any persistent 463 * interface information (used in 'ipadm show-if') 464 */ 465 /* ARGSUSED */ 466 boolean_t 467 ipmgmt_db_getif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 468 int *errp) 469 { 470 ipmgmt_getif_cbarg_t *cbarg = arg; 471 char *ifname = cbarg->cb_ifname; 472 char *intf = NULL; 473 ipadm_if_info_t *ifp = NULL; 474 sa_family_t af; 475 char *afstr; 476 477 *errp = 0; 478 if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) != 0 || 479 nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &intf) != 0 || 480 (ifname[0] != '\0' && strcmp(ifname, intf) != 0)) { 481 return (B_TRUE); 482 } 483 af = atoi(afstr); 484 for (ifp = cbarg->cb_ifinfo; ifp != NULL; ifp = ifp->ifi_next) { 485 if (strcmp(ifp->ifi_name, intf) == 0) 486 break; 487 } 488 if (ifp == NULL) { 489 ipadm_if_info_t *new; 490 491 if ((new = calloc(1, sizeof (*new))) == NULL) { 492 *errp = ENOMEM; 493 return (B_FALSE); /* don't continue the walk */ 494 } 495 new->ifi_next = cbarg->cb_ifinfo; 496 cbarg->cb_ifinfo = new; 497 ifp = new; 498 (void) strlcpy(ifp->ifi_name, intf, sizeof (ifp->ifi_name)); 499 } 500 501 if (af == AF_INET) { 502 ifp->ifi_pflags |= IFIF_IPV4; 503 } else { 504 assert(af == AF_INET6); 505 ifp->ifi_pflags |= IFIF_IPV6; 506 } 507 508 /* Terminate the walk if we found both v4 and v6 interfaces. */ 509 if (ifname[0] != '\0' && (ifp->ifi_pflags & IFIF_IPV4) && 510 (ifp->ifi_pflags & IFIF_IPV6)) 511 return (B_FALSE); 512 513 return (B_TRUE); 514 } 515 516 /* 517 * Deletes those entries from the database for which interface name 518 * matches with the given `cbarg->cb_ifname' 519 */ 520 /* ARGSUSED */ 521 boolean_t 522 ipmgmt_db_resetif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 523 int *errp) 524 { 525 ipmgmt_if_cbarg_t *cbarg = arg; 526 boolean_t isv6 = (cbarg->cb_family == AF_INET6); 527 char *ifname = cbarg->cb_ifname; 528 char *modstr = NULL; 529 char *afstr; 530 char *aobjname; 531 uint_t proto; 532 ipmgmt_aobjmap_t *head; 533 boolean_t aobjfound = B_FALSE; 534 535 *errp = 0; 536 537 if (!ipmgmt_nvlist_contains(db_nvl, NULL, ifname, NULL)) 538 return (B_TRUE); 539 540 if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) == 0) { 541 if (atoi(afstr) == cbarg->cb_family) 542 goto delete; 543 return (B_TRUE); 544 } 545 546 /* Reset all the interface configurations for 'ifname' */ 547 if (isv6 && (nvlist_exists(db_nvl, IPADM_NVP_IPV6ADDR) || 548 nvlist_exists(db_nvl, IPADM_NVP_INTFID))) { 549 goto delete; 550 } 551 if (!isv6 && 552 (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) || 553 nvlist_exists(db_nvl, IPADM_NVP_DHCP))) { 554 goto delete; 555 } 556 557 if (nvlist_lookup_string(db_nvl, IPADM_NVP_AOBJNAME, &aobjname) == 0) { 558 /* 559 * This must be an address property. Delete this 560 * line if there is a match in the address family. 561 */ 562 head = aobjmap.aobjmap_head; 563 while (head != NULL) { 564 if (strcmp(head->am_aobjname, aobjname) == 0) { 565 aobjfound = B_TRUE; 566 if (head->am_family == cbarg->cb_family) 567 goto delete; 568 } 569 head = head->am_next; 570 } 571 /* 572 * If aobjfound = B_FALSE, then this address is not 573 * available in active configuration. We should go ahead 574 * and delete it. 575 */ 576 if (!aobjfound) 577 goto delete; 578 } 579 580 /* 581 * If we are removing both v4 and v6 interface, then we get rid of 582 * all the properties for that interface. On the other hand, if we 583 * are deleting only v4 instance of an interface, then we delete v4 584 * properties only. 585 */ 586 if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME, &modstr) == 0) { 587 proto = ipadm_str2proto(modstr); 588 switch (proto) { 589 case MOD_PROTO_IPV6: 590 if (isv6) 591 goto delete; 592 break; 593 case MOD_PROTO_IPV4: 594 if (!isv6) 595 goto delete; 596 break; 597 case MOD_PROTO_IP: 598 /* this should never be the case, today */ 599 assert(0); 600 break; 601 } 602 } 603 /* Not found a match yet. Continue processing the db */ 604 return (B_TRUE); 605 delete: 606 /* delete the line from the db */ 607 buf[0] = '\0'; 608 return (B_TRUE); 609 } 610 611 /* 612 * Deletes those entries from the database for which address object name 613 * matches with the given `cbarg->cb_aobjname' 614 */ 615 /* ARGSUSED */ 616 boolean_t 617 ipmgmt_db_resetaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 618 int *errp) 619 { 620 ipmgmt_resetaddr_cbarg_t *cbarg = arg; 621 char *aobjname = cbarg->cb_aobjname; 622 623 *errp = 0; 624 if (!ipmgmt_nvlist_contains(db_nvl, NULL, NULL, aobjname)) 625 return (B_TRUE); 626 627 /* delete the line from the db */ 628 buf[0] = '\0'; 629 return (B_TRUE); 630 } 631 632 /* 633 * Retrieves all interface props, including addresses, for given interface(s). 634 * `invl' contains the list of interfaces, for which information need to be 635 * retrieved. 636 */ 637 /* ARGSUSED */ 638 boolean_t 639 ipmgmt_db_initif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 640 int *errp) 641 { 642 ipmgmt_initif_cbarg_t *cbarg = arg; 643 nvlist_t *onvl = cbarg->cb_onvl; 644 nvlist_t *invl = cbarg->cb_invl; 645 sa_family_t in_af = cbarg->cb_family; 646 char *db_ifname; 647 648 *errp = 0; 649 if (nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &db_ifname) == 0 && 650 nvlist_exists(invl, db_ifname)) { 651 char name[IPMGMT_STRSIZE]; 652 sa_family_t db_af = in_af; 653 uint_t proto; 654 char *pstr; 655 656 if (in_af != AF_UNSPEC) { 657 if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME, 658 &pstr) == 0) { 659 proto = ipadm_str2proto(pstr); 660 if (proto == MOD_PROTO_IPV4) 661 db_af = AF_INET; 662 else if (proto == MOD_PROTO_IPV6) 663 db_af = AF_INET6; 664 else 665 db_af = in_af; 666 } else { 667 if (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) || 668 nvlist_exists(db_nvl, IPADM_NVP_DHCP)) 669 db_af = AF_INET; 670 else 671 db_af = AF_INET6; 672 } 673 } 674 if (in_af == db_af) { 675 (void) snprintf(name, sizeof (name), "%s_%d", db_ifname, 676 cbarg->cb_ocnt); 677 *errp = nvlist_add_nvlist(onvl, name, db_nvl); 678 if (*errp == 0) 679 cbarg->cb_ocnt++; 680 } 681 } 682 return (B_TRUE); 683 } 684 685 /* 686 * helper function for ipmgmt_aobjmap_op(). Adds the node pointed by `nodep' 687 * into `aobjmap' structure. 688 */ 689 static int 690 i_ipmgmt_add_amnode(ipmgmt_aobjmap_t *nodep) 691 { 692 ipmgmt_aobjmap_t *new, *head; 693 694 head = aobjmap.aobjmap_head; 695 if ((new = malloc(sizeof (ipmgmt_aobjmap_t))) == NULL) 696 return (ENOMEM); 697 *new = *nodep; 698 new->am_next = NULL; 699 700 /* Add the node at the beginning of the list */ 701 if (head == NULL) { 702 aobjmap.aobjmap_head = new; 703 } else { 704 new->am_next = aobjmap.aobjmap_head; 705 aobjmap.aobjmap_head = new; 706 } 707 return (0); 708 } 709 710 /* 711 * A recursive function to generate alphabetized number given a decimal number. 712 * Decimal 0 to 25 maps to 'a' to 'z' and then the counting continues with 'aa', 713 * 'ab', 'ac', et al. 714 */ 715 static void 716 i_ipmgmt_num2priv_aobjname(uint32_t num, char **cp, char *endp) 717 { 718 if (num >= 26) 719 i_ipmgmt_num2priv_aobjname(num / 26 - 1, cp, endp); 720 if (*cp != endp) { 721 *cp[0] = 'a' + (num % 26); 722 (*cp)++; 723 } 724 } 725 726 /* 727 * This function generates an `aobjname', when required, and then does 728 * lookup-add. If `nodep->am_aobjname' is not an empty string, then it walks 729 * through the `aobjmap' to check if an address object with the same 730 * `nodep->am_aobjname' exists. If it exists, EEXIST is returned as duplicate 731 * `aobjname's are not allowed. 732 * 733 * If `nodep->am_aobjname' is an empty string then the daemon generates an 734 * `aobjname' using the `am_nextnum', which contains the next number to be 735 * used to generate `aobjname'. `am_nextnum' is converted to base26 using 736 * `a-z' alphabets in i_ipmgmt_num2priv_aobjname(). 737 * 738 * `am_nextnum' will be 0 to begin with. Every time an address object that 739 * needs `aobjname' is added it's incremented by 1. So for the first address 740 * object on net0 the `am_aobjname' will be net0/_a and `am_nextnum' will be 1. 741 * For the second address object on that interface `am_aobjname' will be net0/_b 742 * and `am_nextnum' will incremented to 2. 743 */ 744 static int 745 i_ipmgmt_lookupadd_amnode(ipmgmt_aobjmap_t *nodep) 746 { 747 ipmgmt_aobjmap_t *head; 748 uint32_t nextnum; 749 750 for (head = aobjmap.aobjmap_head; head != NULL; head = head->am_next) 751 if (strcmp(head->am_ifname, nodep->am_ifname) == 0) 752 break; 753 nextnum = (head == NULL ? 0 : head->am_nextnum); 754 755 /* 756 * if `aobjname' is empty, then the daemon has to generate the 757 * next `aobjname' for the given interface and family. 758 */ 759 if (nodep->am_aobjname[0] == '\0') { 760 char tmpstr[IPADM_AOBJ_USTRSIZ - 1]; /* 1 for leading '_' */ 761 char *cp = tmpstr; 762 char *endp = tmpstr + sizeof (tmpstr); 763 764 i_ipmgmt_num2priv_aobjname(nextnum, &cp, endp); 765 766 if (cp == endp) 767 return (EINVAL); 768 cp[0] = '\0'; 769 770 if (snprintf(nodep->am_aobjname, IPADM_AOBJSIZ, "%s/_%s", 771 nodep->am_ifname, tmpstr) >= IPADM_AOBJSIZ) { 772 return (EINVAL); 773 } 774 nodep->am_nextnum = ++nextnum; 775 } else { 776 for (head = aobjmap.aobjmap_head; head != NULL; 777 head = head->am_next) { 778 if (strcmp(head->am_aobjname, nodep->am_aobjname) == 0) 779 return (EEXIST); 780 } 781 nodep->am_nextnum = nextnum; 782 } 783 return (i_ipmgmt_add_amnode(nodep)); 784 } 785 786 /* 787 * Performs following operations on the global `aobjmap' linked list. 788 * (a) ADDROBJ_ADD: add or update address object in `aobjmap' 789 * (b) ADDROBJ_DELETE: delete address object from `aobjmap' 790 * (c) ADDROBJ_LOOKUPADD: place a stub address object in `aobjmap' 791 * (d) ADDROBJ_SETLIFNUM: Sets the lifnum for an address object in `aobjmap' 792 */ 793 int 794 ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *nodep, uint32_t op) 795 { 796 ipmgmt_aobjmap_t *head, *prev, *matched = NULL; 797 boolean_t update = B_TRUE; 798 int err = 0; 799 ipadm_db_op_t db_op; 800 801 (void) pthread_rwlock_wrlock(&aobjmap.aobjmap_rwlock); 802 803 head = aobjmap.aobjmap_head; 804 switch (op) { 805 case ADDROBJ_ADD: 806 /* 807 * check for stub nodes (added by ADDROBJ_LOOKUPADD) and 808 * update, else add the new node. 809 */ 810 for (; head != NULL; head = head->am_next) { 811 /* 812 * For IPv6, we need to distinguish between the 813 * linklocal and non-linklocal nodes 814 */ 815 if (strcmp(head->am_aobjname, 816 nodep->am_aobjname) == 0 && 817 (head->am_atype != IPADM_ADDR_IPV6_ADDRCONF || 818 head->am_linklocal == nodep->am_linklocal)) 819 break; 820 } 821 822 if (head != NULL) { 823 /* update the node */ 824 (void) strlcpy(head->am_ifname, nodep->am_ifname, 825 sizeof (head->am_ifname)); 826 head->am_lnum = nodep->am_lnum; 827 head->am_family = nodep->am_family; 828 head->am_flags = nodep->am_flags; 829 head->am_atype = nodep->am_atype; 830 if (head->am_atype == IPADM_ADDR_IPV6_ADDRCONF) { 831 head->am_ifid = nodep->am_ifid; 832 head->am_linklocal = nodep->am_linklocal; 833 } 834 } else { 835 for (head = aobjmap.aobjmap_head; head != NULL; 836 head = head->am_next) { 837 if (strcmp(head->am_ifname, 838 nodep->am_ifname) == 0) 839 break; 840 } 841 nodep->am_nextnum = (head == NULL ? 0 : 842 head->am_nextnum); 843 err = i_ipmgmt_add_amnode(nodep); 844 } 845 db_op = IPADM_DB_WRITE; 846 break; 847 case ADDROBJ_DELETE: 848 prev = head; 849 while (head != NULL) { 850 if (strcmp(head->am_aobjname, 851 nodep->am_aobjname) == 0) { 852 nodep->am_atype = head->am_atype; 853 /* 854 * There could be multiple IPV6_ADDRCONF nodes, 855 * with same address object name, so check for 856 * logical number also. 857 */ 858 if (head->am_atype != 859 IPADM_ADDR_IPV6_ADDRCONF || 860 nodep->am_lnum == head->am_lnum) 861 break; 862 } 863 prev = head; 864 head = head->am_next; 865 } 866 if (head != NULL) { 867 /* 868 * If the address object is in both active and 869 * persistent configuration and the user is deleting it 870 * only from active configuration then mark this node 871 * for deletion by reseting IPMGMT_ACTIVE bit. 872 * With this the same address object name cannot 873 * be reused until it is permanently removed. 874 */ 875 if (head->am_flags == (IPMGMT_ACTIVE|IPMGMT_PERSIST) && 876 nodep->am_flags == IPMGMT_ACTIVE) { 877 /* Update flags in the in-memory map. */ 878 head->am_flags &= ~IPMGMT_ACTIVE; 879 head->am_lnum = -1; 880 881 /* Update info in file. */ 882 db_op = IPADM_DB_WRITE; 883 *nodep = *head; 884 } else { 885 (void) strlcpy(nodep->am_ifname, 886 head->am_ifname, 887 sizeof (nodep->am_ifname)); 888 /* otherwise delete the node */ 889 if (head == aobjmap.aobjmap_head) 890 aobjmap.aobjmap_head = head->am_next; 891 else 892 prev->am_next = head->am_next; 893 free(head); 894 db_op = IPADM_DB_DELETE; 895 } 896 } else { 897 err = ENOENT; 898 } 899 break; 900 case ADDROBJ_LOOKUPADD: 901 err = i_ipmgmt_lookupadd_amnode(nodep); 902 update = B_FALSE; 903 break; 904 case ADDROBJ_SETLIFNUM: 905 update = B_FALSE; 906 for (; head != NULL; head = head->am_next) { 907 if (strcmp(head->am_ifname, 908 nodep->am_ifname) == 0 && 909 head->am_family == nodep->am_family && 910 head->am_lnum == nodep->am_lnum) { 911 err = EEXIST; 912 break; 913 } 914 if (strcmp(head->am_aobjname, 915 nodep->am_aobjname) == 0) { 916 matched = head; 917 } 918 } 919 if (err == EEXIST) 920 break; 921 if (matched != NULL) { 922 /* update the lifnum */ 923 matched->am_lnum = nodep->am_lnum; 924 } else { 925 err = ENOENT; 926 } 927 break; 928 default: 929 assert(0); 930 } 931 932 if (err == 0 && update) 933 err = ipmgmt_persist_aobjmap(nodep, db_op); 934 935 (void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock); 936 937 return (err); 938 } 939 940 /* 941 * Given a node in `aobjmap', this function converts it into nvlist_t structure. 942 * The content to be written to DB must be represented as nvlist_t. 943 */ 944 static int 945 i_ipmgmt_node2nvl(nvlist_t **nvl, ipmgmt_aobjmap_t *np) 946 { 947 int err; 948 char strval[IPMGMT_STRSIZE]; 949 950 *nvl = NULL; 951 if ((err = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0)) != 0) 952 goto fail; 953 954 if ((err = nvlist_add_string(*nvl, IPADM_NVP_AOBJNAME, 955 np->am_aobjname)) != 0) 956 goto fail; 957 958 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IFNAME, 959 np->am_ifname)) != 0) 960 goto fail; 961 962 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_lnum); 963 if ((err = nvlist_add_string(*nvl, IPADM_NVP_LIFNUM, strval)) != 0) 964 goto fail; 965 966 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_family); 967 if ((err = nvlist_add_string(*nvl, IPADM_NVP_FAMILY, strval)) != 0) 968 goto fail; 969 970 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_flags); 971 if ((err = nvlist_add_string(*nvl, FLAGS, strval)) != 0) 972 goto fail; 973 974 (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_atype); 975 if ((err = nvlist_add_string(*nvl, ATYPE, strval)) != 0) 976 goto fail; 977 978 if (np->am_atype == IPADM_ADDR_IPV6_ADDRCONF) { 979 struct sockaddr_in6 *in6; 980 981 in6 = (struct sockaddr_in6 *)&np->am_ifid; 982 if (np->am_linklocal && 983 IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) { 984 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR, 985 "default")) != 0) 986 goto fail; 987 } else { 988 if (inet_ntop(AF_INET6, &in6->sin6_addr, strval, 989 IPMGMT_STRSIZE) == NULL) { 990 err = errno; 991 goto fail; 992 } 993 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR, 994 strval)) != 0) 995 goto fail; 996 } 997 } else { 998 if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR, 999 "")) != 0) 1000 goto fail; 1001 } 1002 return (err); 1003 fail: 1004 nvlist_free(*nvl); 1005 return (err); 1006 } 1007 1008 /* 1009 * Read the aobjmap data store and build the in-memory representation 1010 * of the aobjmap. We don't need to hold any locks while building this as 1011 * we do this in very early stage of daemon coming up, even before the door 1012 * is opened. 1013 */ 1014 /* ARGSUSED */ 1015 extern boolean_t 1016 ipmgmt_aobjmap_init(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, 1017 int *errp) 1018 { 1019 nvpair_t *nvp = NULL; 1020 char *name, *strval = NULL; 1021 ipmgmt_aobjmap_t node; 1022 struct sockaddr_in6 *in6; 1023 1024 *errp = 0; 1025 node.am_next = NULL; 1026 for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; 1027 nvp = nvlist_next_nvpair(db_nvl, nvp)) { 1028 name = nvpair_name(nvp); 1029 1030 if ((*errp = nvpair_value_string(nvp, &strval)) != 0) 1031 return (B_TRUE); 1032 if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) { 1033 (void) strlcpy(node.am_aobjname, strval, 1034 sizeof (node.am_aobjname)); 1035 } else if (strcmp(IPADM_NVP_IFNAME, name) == 0) { 1036 (void) strlcpy(node.am_ifname, strval, 1037 sizeof (node.am_ifname)); 1038 } else if (strcmp(IPADM_NVP_LIFNUM, name) == 0) { 1039 node.am_lnum = atoi(strval); 1040 } else if (strcmp(IPADM_NVP_FAMILY, name) == 0) { 1041 node.am_family = (sa_family_t)atoi(strval); 1042 } else if (strcmp(FLAGS, name) == 0) { 1043 node.am_flags = atoi(strval); 1044 } else if (strcmp(ATYPE, name) == 0) { 1045 node.am_atype = (ipadm_addr_type_t)atoi(strval); 1046 } else if (strcmp(IPADM_NVP_IPNUMADDR, name) == 0) { 1047 if (node.am_atype == IPADM_ADDR_IPV6_ADDRCONF) { 1048 in6 = (struct sockaddr_in6 *)&node.am_ifid; 1049 if (strcmp(strval, "default") == 0) { 1050 bzero(in6, sizeof (node.am_ifid)); 1051 node.am_linklocal = B_TRUE; 1052 } else { 1053 (void) inet_pton(AF_INET6, strval, 1054 &in6->sin6_addr); 1055 if (IN6_IS_ADDR_UNSPECIFIED( 1056 &in6->sin6_addr)) 1057 node.am_linklocal = B_TRUE; 1058 } 1059 } 1060 } 1061 } 1062 1063 /* we have all the information we need, add the node */ 1064 *errp = i_ipmgmt_add_amnode(&node); 1065 1066 return (B_TRUE); 1067 } 1068 1069 /* 1070 * Updates an entry from the temporary cache file, which matches the given 1071 * address object name. 1072 */ 1073 /* ARGSUSED */ 1074 static boolean_t 1075 ipmgmt_update_aobjmap(void *arg, nvlist_t *db_nvl, char *buf, 1076 size_t buflen, int *errp) 1077 { 1078 ipadm_dbwrite_cbarg_t *cb = arg; 1079 nvlist_t *in_nvl = cb->dbw_nvl; 1080 uint32_t flags = cb->dbw_flags; 1081 char *db_lifnumstr = NULL, *in_lifnumstr = NULL; 1082 1083 *errp = 0; 1084 if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl)) 1085 return (B_TRUE); 1086 1087 if (flags & IPMGMT_ATYPE_V6ACONF) { 1088 if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM, 1089 &db_lifnumstr) != 0 || 1090 nvlist_lookup_string(in_nvl, IPADM_NVP_LIFNUM, 1091 &in_lifnumstr) != 0 || 1092 (atoi(db_lifnumstr) != -1 && atoi(in_lifnumstr) != -1 && 1093 strcmp(db_lifnumstr, in_lifnumstr) != 0)) 1094 return (B_TRUE); 1095 } 1096 1097 /* we found the match */ 1098 (void) memset(buf, 0, buflen); 1099 if (ipadm_nvlist2str(in_nvl, buf, buflen) == 0) { 1100 /* buffer overflow */ 1101 *errp = ENOBUFS; 1102 } 1103 1104 /* stop the walker */ 1105 return (B_FALSE); 1106 } 1107 1108 /* 1109 * Deletes an entry from the temporary cache file, which matches the given 1110 * address object name. 1111 */ 1112 /* ARGSUSED */ 1113 static boolean_t 1114 ipmgmt_delete_aobjmap(void *arg, nvlist_t *db_nvl, char *buf, 1115 size_t buflen, int *errp) 1116 { 1117 ipmgmt_aobjmap_t *nodep = arg; 1118 char *db_lifnumstr = NULL; 1119 1120 *errp = 0; 1121 if (!ipmgmt_nvlist_match(db_nvl, NULL, nodep->am_ifname, 1122 nodep->am_aobjname)) 1123 return (B_TRUE); 1124 1125 if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF) { 1126 if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM, 1127 &db_lifnumstr) != 0 || atoi(db_lifnumstr) != nodep->am_lnum) 1128 return (B_TRUE); 1129 } 1130 1131 /* we found the match, delete the line from the db */ 1132 buf[0] = '\0'; 1133 1134 /* stop the walker */ 1135 return (B_FALSE); 1136 } 1137 1138 /* 1139 * Adds or deletes aobjmap node information into a temporary cache file. 1140 */ 1141 extern int 1142 ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *nodep, ipadm_db_op_t op) 1143 { 1144 int err; 1145 ipadm_dbwrite_cbarg_t cb; 1146 nvlist_t *nvl = NULL; 1147 1148 if (op == IPADM_DB_WRITE) { 1149 if ((err = i_ipmgmt_node2nvl(&nvl, nodep)) != 0) 1150 return (err); 1151 cb.dbw_nvl = nvl; 1152 if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF) 1153 cb.dbw_flags = IPMGMT_ATYPE_V6ACONF; 1154 else 1155 cb.dbw_flags = 0; 1156 1157 err = ipadm_rw_db(ipmgmt_update_aobjmap, &cb, 1158 ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_WRITE); 1159 nvlist_free(nvl); 1160 } else { 1161 assert(op == IPADM_DB_DELETE); 1162 1163 err = ipadm_rw_db(ipmgmt_delete_aobjmap, nodep, 1164 ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_DELETE); 1165 } 1166 return (err); 1167 } 1168 1169 typedef struct scf_resources { 1170 scf_handle_t *sr_handle; 1171 scf_instance_t *sr_inst; 1172 scf_propertygroup_t *sr_pg; 1173 scf_property_t *sr_prop; 1174 scf_value_t *sr_val; 1175 scf_transaction_t *sr_tx; 1176 scf_transaction_entry_t *sr_ent; 1177 } scf_resources_t; 1178 1179 /* 1180 * Inputs: 1181 * res is a pointer to the scf_resources_t to be released. 1182 */ 1183 static void 1184 ipmgmt_release_scf_resources(scf_resources_t *res) 1185 { 1186 scf_entry_destroy(res->sr_ent); 1187 scf_transaction_destroy(res->sr_tx); 1188 scf_value_destroy(res->sr_val); 1189 scf_property_destroy(res->sr_prop); 1190 scf_pg_destroy(res->sr_pg); 1191 scf_instance_destroy(res->sr_inst); 1192 (void) scf_handle_unbind(res->sr_handle); 1193 scf_handle_destroy(res->sr_handle); 1194 } 1195 1196 /* 1197 * Inputs: 1198 * fmri is the instance to look up 1199 * Outputs: 1200 * res is a pointer to an scf_resources_t. This is an internal 1201 * structure that holds all the handles needed to get a specific 1202 * property from the running snapshot; on a successful return it 1203 * contains the scf_value_t that should be passed to the desired 1204 * scf_value_get_foo() function, and must be freed after use by 1205 * calling release_scf_resources(). On a failure return, any 1206 * resources that may have been assigned to res are released, so 1207 * the caller does not need to do any cleanup in the failure case. 1208 * Returns: 1209 * 0 on success 1210 * -1 on failure 1211 */ 1212 1213 static int 1214 ipmgmt_create_scf_resources(const char *fmri, scf_resources_t *res) 1215 { 1216 res->sr_tx = NULL; 1217 res->sr_ent = NULL; 1218 res->sr_inst = NULL; 1219 res->sr_pg = NULL; 1220 res->sr_prop = NULL; 1221 res->sr_val = NULL; 1222 1223 if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL) { 1224 return (-1); 1225 } 1226 1227 if (scf_handle_bind(res->sr_handle) != 0) { 1228 scf_handle_destroy(res->sr_handle); 1229 return (-1); 1230 } 1231 if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL) { 1232 goto failure; 1233 } 1234 if (scf_handle_decode_fmri(res->sr_handle, fmri, NULL, NULL, 1235 res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) { 1236 goto failure; 1237 } 1238 if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) { 1239 goto failure; 1240 } 1241 if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL) { 1242 goto failure; 1243 } 1244 if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL) { 1245 goto failure; 1246 } 1247 if ((res->sr_tx = scf_transaction_create(res->sr_handle)) == NULL) { 1248 goto failure; 1249 } 1250 if ((res->sr_ent = scf_entry_create(res->sr_handle)) == NULL) { 1251 goto failure; 1252 } 1253 return (0); 1254 1255 failure: 1256 ipmgmt_release_scf_resources(res); 1257 return (-1); 1258 } 1259 1260 static int 1261 ipmgmt_set_property_value(scf_resources_t *res, const char *propname, 1262 scf_type_t proptype) 1263 { 1264 int result = -1; 1265 boolean_t new; 1266 1267 retry: 1268 new = (scf_pg_get_property(res->sr_pg, propname, res->sr_prop) != 0); 1269 1270 if (scf_transaction_start(res->sr_tx, res->sr_pg) == -1) { 1271 goto failure; 1272 } 1273 if (new) { 1274 if (scf_transaction_property_new(res->sr_tx, res->sr_ent, 1275 propname, proptype) == -1) { 1276 goto failure; 1277 } 1278 } else { 1279 if (scf_transaction_property_change(res->sr_tx, res->sr_ent, 1280 propname, proptype) == -1) { 1281 goto failure; 1282 } 1283 } 1284 1285 if (scf_entry_add_value(res->sr_ent, res->sr_val) != 0) { 1286 goto failure; 1287 } 1288 1289 result = scf_transaction_commit(res->sr_tx); 1290 if (result == 0) { 1291 scf_transaction_reset(res->sr_tx); 1292 if (scf_pg_update(res->sr_pg) == -1) { 1293 goto failure; 1294 } 1295 goto retry; 1296 } 1297 if (result == -1) 1298 goto failure; 1299 return (0); 1300 1301 failure: 1302 return (-1); 1303 } 1304 1305 /* 1306 * Returns TRUE if this is the first boot, else return FALSE. The 1307 * "ipmgmtd/first_boot_done" property is persistently set up on 1308 * IPMGMTD_FMRI on the first boot. Note that the presence of 1309 * "first_boot_done" itself is sufficient to indicate that this is 1310 * not the first boot i.e., the value of the property is immaterial. 1311 */ 1312 extern boolean_t 1313 ipmgmt_first_boot() 1314 { 1315 scf_simple_prop_t *prop; 1316 ssize_t numvals; 1317 scf_resources_t res; 1318 scf_error_t err; 1319 1320 if (ipmgmt_create_scf_resources(IPMGMTD_FMRI, &res) != 0) 1321 return (B_TRUE); /* err on the side of caution */ 1322 prop = scf_simple_prop_get(res.sr_handle, 1323 IPMGMTD_FMRI, "ipmgmtd", "first_boot_done"); 1324 numvals = scf_simple_prop_numvalues(prop); 1325 if (numvals > 0) { 1326 scf_simple_prop_free(prop); 1327 ipmgmt_release_scf_resources(&res); 1328 return (B_FALSE); 1329 } 1330 1331 /* 1332 * mark the first boot by setting ipmgmtd/first_boot_done to true 1333 */ 1334 if (scf_instance_add_pg(res.sr_inst, "ipmgmtd", SCF_GROUP_APPLICATION, 1335 0, res.sr_pg) != 0) { 1336 if ((err = scf_error()) != SCF_ERROR_EXISTS) 1337 goto failure; 1338 /* 1339 * err == SCF_ERROR_EXISTS is by itself sufficient to declare 1340 * that this is not the first boot, but we create a simple 1341 * property as a place-holder, so that we don't leave an 1342 * empty process group behind. 1343 */ 1344 if (scf_instance_get_pg_composed(res.sr_inst, NULL, "ipmgmtd", 1345 res.sr_pg) != 0) { 1346 err = scf_error(); 1347 goto failure; 1348 } 1349 } 1350 1351 if (scf_value_set_astring(res.sr_val, "true") != 0) { 1352 err = scf_error(); 1353 goto failure; 1354 } 1355 1356 if (ipmgmt_set_property_value(&res, "first_boot_done", 1357 SCF_TYPE_ASTRING) != 0) { 1358 ipmgmt_log(LOG_WARNING, 1359 "Could not set rval of first_boot_done"); 1360 } 1361 1362 failure: 1363 ipmgmt_log(LOG_WARNING, "ipmgmt_first_boot scf error %s", 1364 scf_strerror(err)); 1365 ipmgmt_release_scf_resources(&res); 1366 return (B_TRUE); 1367 } 1368