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 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <assert.h> 28 #include <stdlib.h> 29 #include <strings.h> 30 #include <string.h> 31 32 #include "libnwam_impl.h" 33 #include <libintl.h> 34 #include <libnwam.h> 35 36 /* 37 * Generic object manipulation functions. Given an object handle and 38 * other parameters, create/destroy objects, walk them, walk their 39 * properties, modify/retrieve/delete properties, enable/disable them, 40 * etc. All object handles are "struct nwam_handle *" objects, sharing 41 * the same description based on the object type, name, original name 42 * (used in renaming) and associated data representing properties. 43 */ 44 45 nwam_error_t 46 nwam_handle_create(nwam_object_type_t type, const char *name, 47 struct nwam_handle **hpp) 48 { 49 50 assert(name != NULL && hpp != NULL); 51 52 if (strnlen(name, NWAM_MAX_NAME_LEN) > NWAM_MAX_NAME_LEN) { 53 *hpp = NULL; 54 return (NWAM_INVALID_ARG); 55 } 56 57 if ((*hpp = calloc(1, sizeof (struct nwam_handle))) == NULL) 58 return (NWAM_NO_MEMORY); 59 60 (*hpp)->nwh_object_type = type; 61 (void) strlcpy((*hpp)->nwh_name, name, strlen(name) + 1); 62 (*hpp)->nwh_committed = B_FALSE; 63 (*hpp)->nwh_data = NULL; 64 65 return (NWAM_SUCCESS); 66 } 67 68 /* 69 * Read object of specified type from dbname. 70 */ 71 nwam_error_t 72 nwam_read(nwam_object_type_t type, const char *dbname, const char *name, 73 uint64_t flags, struct nwam_handle **hpp) 74 { 75 nwam_error_t err; 76 char dbname_copy[MAXPATHLEN]; 77 78 assert(name != NULL && hpp != NULL); 79 80 if (dbname != NULL) 81 (void) strlcpy(dbname_copy, dbname, sizeof (dbname_copy)); 82 83 if ((err = nwam_valid_flags(flags, NWAM_FLAG_BLOCKING)) != NWAM_SUCCESS) 84 return (err); 85 if ((err = nwam_handle_create(type, name, hpp)) != NWAM_SUCCESS) 86 return (err); 87 88 if ((err = nwam_read_object_from_backend 89 (dbname != NULL ? dbname_copy : NULL, 90 type == NWAM_OBJECT_TYPE_NCP ? NULL : (*hpp)->nwh_name, flags, 91 &(*hpp)->nwh_data)) != NWAM_SUCCESS) { 92 free(*hpp); 93 *hpp = NULL; 94 return (err); 95 } 96 if (type == NWAM_OBJECT_TYPE_NCP && dbname != NULL) { 97 char *ncpname; 98 99 /* 100 * dbname_copy may have been changed due to case-insensitive 101 * match against the actual NCP configuration file. 102 */ 103 if (nwam_ncp_file_to_name(dbname_copy, &ncpname) 104 == NWAM_SUCCESS) { 105 (void) strlcpy((*hpp)->nwh_name, ncpname, 106 sizeof ((*hpp)->nwh_name)); 107 free(ncpname); 108 } 109 } 110 111 (*hpp)->nwh_committed = B_TRUE; 112 113 return (NWAM_SUCCESS); 114 } 115 116 /* 117 * Create simply creates the handle - the object-specific function must 118 * then fill in property values. 119 */ 120 nwam_error_t 121 nwam_create(nwam_object_type_t type, const char *dbname, const char *name, 122 struct nwam_handle **hpp) 123 { 124 struct nwam_handle *hp; 125 126 assert(hpp != NULL && name != NULL); 127 128 if (nwam_read(type, dbname, name, 0, &hp) == NWAM_SUCCESS) { 129 nwam_free(hp); 130 return (NWAM_ENTITY_EXISTS); 131 } 132 /* Create handle */ 133 return (nwam_handle_create(type, name, hpp)); 134 } 135 136 nwam_error_t 137 nwam_get_name(struct nwam_handle *hp, char **namep) 138 { 139 assert(hp != NULL && namep != NULL); 140 141 if ((*namep = strdup(hp->nwh_name)) == NULL) { 142 *namep = NULL; 143 return (NWAM_NO_MEMORY); 144 } 145 return (NWAM_SUCCESS); 146 } 147 148 nwam_error_t 149 nwam_set_name(struct nwam_handle *hp, const char *name) 150 { 151 assert(hp != NULL && name != NULL); 152 153 if (hp->nwh_committed) 154 return (NWAM_ENTITY_READ_ONLY); 155 156 if (strlen(name) >= sizeof (hp->nwh_name)) 157 return (NWAM_INVALID_ARG); 158 159 (void) strcpy(hp->nwh_name, name); 160 161 return (NWAM_SUCCESS); 162 } 163 164 /* Compare object names c1 and c2 using strcasecmp() */ 165 static int 166 name_cmp(const void *c1, const void *c2) 167 { 168 nwam_ncu_type_t t1, t2; 169 char *n1, *n2; 170 171 /* If c1 and c2 are typed NCU names, compare names without the types */ 172 if (nwam_ncu_typed_name_to_name(*(const char **)c1, &t1, &n1) 173 == NWAM_SUCCESS && 174 nwam_ncu_typed_name_to_name(*(const char **)c2, &t2, &n2) 175 == NWAM_SUCCESS) { 176 int ret = strcasecmp(n1, n2); 177 free(n1); 178 free(n2); 179 180 /* For NCUs with the same name, compare their types */ 181 if (ret == 0) { 182 if (t1 < t2) 183 ret = -1; 184 else if (t1 > t2) 185 ret = 1; 186 } 187 return (ret); 188 } 189 190 return (strcasecmp(*(const char **)c1, *(const char **)c2)); 191 } 192 193 /* 194 * Generic walk function takes the standard walk arguments, and in addition 195 * takes a selection callback that is object-specific. If this returns 196 * 0, the object is a valid selection for the walk and the callback is called. 197 * Otherwise, it is skipped. 198 */ 199 nwam_error_t 200 nwam_walk(nwam_object_type_t type, const char *dbname, 201 int(*cb)(struct nwam_handle *, void *), 202 void *data, uint64_t flags, int *retp, 203 int(*selectcb)(struct nwam_handle *, uint64_t, void *)) 204 { 205 void *objlist; 206 nwam_value_t value; 207 char **object_names; 208 uint_t i, num_objects = 0; 209 struct nwam_handle *hp; 210 nwam_error_t err; 211 int ret = 0; 212 213 assert(cb != NULL); 214 215 /* 216 * To walk a set of objects, call nwam_read_object_from_backend() 217 * with a "dbname" argument set to the container db name and 218 * the object name set to NULL. This returns an nvlist with one 219 * member - the NWAM_OBJECT_NAMES_STRING - and the values it contains 220 * represent the names of the objects. Read each in turn, calling 221 * the callback function. 222 */ 223 if ((err = nwam_read_object_from_backend((char *)dbname, NULL, flags, 224 &objlist)) != NWAM_SUCCESS) { 225 if (err == NWAM_ENTITY_NOT_FOUND) { 226 /* 227 * This indicates the dbname container is not present. 228 * Do not pass back an error in this case, since it is 229 * valid for a container not to exist. 230 */ 231 return (NWAM_SUCCESS); 232 } 233 return (err); 234 } 235 236 if ((err = nwam_get_prop_value(objlist, NWAM_OBJECT_NAMES_STRING, 237 &value)) != NWAM_SUCCESS) { 238 nwam_free_object_list(objlist); 239 return (err); 240 } 241 err = nwam_value_get_string_array(value, &object_names, &num_objects); 242 nwam_free_object_list(objlist); 243 if (err != NWAM_SUCCESS) { 244 nwam_value_free(value); 245 return (err); 246 } 247 248 /* sort the object names alphabetically */ 249 qsort(object_names, num_objects, sizeof (char *), name_cmp); 250 251 for (i = 0; i < num_objects; i++) { 252 err = nwam_read(type, dbname, object_names[i], 253 flags & NWAM_FLAG_GLOBAL_MASK, &hp); 254 /* An object may have disappeared. If so, skip it. */ 255 if (err == NWAM_ENTITY_NOT_FOUND) 256 continue; 257 if (err != NWAM_SUCCESS) { 258 nwam_value_free(value); 259 return (err); 260 } 261 if ((selectcb == NULL) || (selectcb(hp, flags, data) == 0)) { 262 ret = cb(hp, data); 263 if (ret != 0) { 264 nwam_free(hp); 265 nwam_value_free(value); 266 if (retp != NULL) 267 *retp = ret; 268 return (NWAM_WALK_HALTED); 269 } 270 } 271 nwam_free(hp); 272 } 273 nwam_value_free(value); 274 275 if (retp != NULL) 276 *retp = ret; 277 return (err); 278 } 279 280 void 281 nwam_free(struct nwam_handle *hp) 282 { 283 if (hp != NULL) { 284 if (hp->nwh_data != NULL) 285 nwam_free_object_list(hp->nwh_data); 286 free(hp); 287 } 288 } 289 290 /* 291 * Copy object represented by oldhp to an object newname, all in container 292 * dbname. 293 */ 294 nwam_error_t 295 nwam_copy(const char *dbname, struct nwam_handle *oldhp, const char *newname, 296 struct nwam_handle **newhpp) 297 { 298 nwam_error_t err; 299 struct nwam_handle *hp; 300 301 assert(oldhp != NULL && newname != NULL && newhpp != NULL); 302 303 if (nwam_read(oldhp->nwh_object_type, dbname, newname, 0, &hp) 304 == NWAM_SUCCESS) { 305 nwam_free(hp); 306 return (NWAM_ENTITY_EXISTS); 307 } 308 309 if ((err = nwam_handle_create(oldhp->nwh_object_type, newname, newhpp)) 310 != NWAM_SUCCESS) 311 return (err); 312 if ((err = nwam_dup_object_list(oldhp->nwh_data, 313 &((*newhpp)->nwh_data))) != NWAM_SUCCESS) { 314 nwam_free(*newhpp); 315 *newhpp = NULL; 316 return (err); 317 } 318 319 return (NWAM_SUCCESS); 320 } 321 322 /* ARGSUSED3 */ 323 nwam_error_t 324 nwam_walk_props(struct nwam_handle *hp, 325 int (*cb)(const char *, nwam_value_t, void *), 326 void *data, uint64_t flags, int *retp) 327 { 328 char *lastpropname = NULL, *propname; 329 nwam_value_t value; 330 nwam_error_t err; 331 int ret = 0; 332 333 assert(hp != NULL && hp->nwh_data != NULL && cb != NULL); 334 335 if ((err = nwam_valid_flags(flags, 0)) != NWAM_SUCCESS) 336 return (err); 337 while ((err = nwam_next_object_prop(hp->nwh_data, lastpropname, 338 &propname, &value)) == NWAM_SUCCESS) { 339 340 ret = cb(propname, value, data); 341 if (ret != 0) 342 err = NWAM_WALK_HALTED; 343 344 /* Free value */ 345 nwam_value_free(value); 346 347 if (err != NWAM_SUCCESS) 348 break; 349 350 lastpropname = propname; 351 } 352 353 if (retp != NULL) 354 *retp = ret; 355 if (err == NWAM_SUCCESS || err == NWAM_LIST_END) 356 return (NWAM_SUCCESS); 357 return (err); 358 } 359 360 /* 361 * Note that prior to calling the generic commit function, object-specific 362 * validation should be carried out. 363 */ 364 nwam_error_t 365 nwam_commit(const char *dbname, struct nwam_handle *hp, uint64_t flags) 366 { 367 nwam_error_t err; 368 uint64_t iflags = flags; 369 boolean_t is_ncu; 370 struct nwam_handle *testhp; 371 nwam_action_t action; 372 373 assert(hp != NULL); 374 375 /* 376 * NWAM_FLAG_ENTITY_KNOWN_WLAN is only used for Known WLANs and 377 * NWAM_FLAG_ENTITY_ENABLE is used for other objects (during enable 378 * and disable). 379 */ 380 if ((err = nwam_valid_flags(flags, 381 NWAM_FLAG_BLOCKING | NWAM_FLAG_CREATE | 382 (hp->nwh_object_type == NWAM_OBJECT_TYPE_KNOWN_WLAN ? 383 NWAM_FLAG_ENTITY_KNOWN_WLAN : NWAM_FLAG_ENTITY_ENABLE))) 384 != NWAM_SUCCESS) 385 return (err); 386 387 is_ncu = (hp->nwh_object_type == NWAM_OBJECT_TYPE_NCU); 388 389 /* 390 * Does object already exist? If not, action is ADD, otherwise REFRESH. 391 */ 392 switch (nwam_read(hp->nwh_object_type, (char *)dbname, hp->nwh_name, 0, 393 &testhp)) { 394 case NWAM_ENTITY_NOT_FOUND: 395 action = NWAM_ACTION_ADD; 396 break; 397 case NWAM_SUCCESS: 398 nwam_free(testhp); 399 if (hp->nwh_object_type == NWAM_OBJECT_TYPE_NCP) 400 return (NWAM_ENTITY_EXISTS); 401 /* FALLTHRU */ 402 default: 403 action = NWAM_ACTION_REFRESH; 404 break; 405 } 406 407 err = nwam_update_object_in_backend((char *)dbname, 408 hp->nwh_object_type == NWAM_OBJECT_TYPE_NCP ? NULL : hp->nwh_name, 409 iflags, hp->nwh_data); 410 if (err != NWAM_SUCCESS) 411 return (err); 412 413 hp->nwh_committed = B_TRUE; 414 415 /* 416 * Tell nwamd to reread this object. For NCUs, we need to convert 417 * the dbname to the NCP name in order to pass it to nwamd. 418 */ 419 if (is_ncu) { 420 char *ncpname; 421 422 if (nwam_ncp_file_to_name(dbname, &ncpname) == NWAM_SUCCESS) { 423 (void) nwam_request_action(hp->nwh_object_type, 424 hp->nwh_name, ncpname, action); 425 free(ncpname); 426 } 427 } else { 428 (void) nwam_request_action(hp->nwh_object_type, hp->nwh_name, 429 NULL, action); 430 } 431 return (NWAM_SUCCESS); 432 } 433 434 static boolean_t 435 nwam_is_active(struct nwam_handle *hp) 436 { 437 nwam_state_t state; 438 nwam_aux_state_t aux; 439 440 return ((nwam_get_state(NULL, hp, &state, &aux) == NWAM_SUCCESS && 441 state == NWAM_STATE_ONLINE)); 442 } 443 444 nwam_error_t 445 nwam_destroy(const char *dbname, struct nwam_handle *hp, uint64_t flags) 446 { 447 nwam_error_t err; 448 char *name; 449 boolean_t is_ncp, is_ncu; 450 451 assert(hp != NULL); 452 453 /* NWAM_FLAG_ENTITY_KNOWN_WLAN is only used for Known WLANs */ 454 if ((err = nwam_valid_flags(flags, 455 NWAM_FLAG_BLOCKING | NWAM_FLAG_DO_NOT_FREE | 456 (hp->nwh_object_type == NWAM_OBJECT_TYPE_KNOWN_WLAN ? 457 NWAM_FLAG_ENTITY_KNOWN_WLAN : 0))) != NWAM_SUCCESS) 458 return (err); 459 460 is_ncp = hp->nwh_object_type == NWAM_OBJECT_TYPE_NCP; 461 is_ncu = hp->nwh_object_type == NWAM_OBJECT_TYPE_NCU; 462 name = hp->nwh_name; 463 464 /* Check if object is active */ 465 if (!is_ncp && !is_ncu && nwam_is_active(hp)) 466 return (NWAM_ENTITY_IN_USE); 467 468 /* For NCPs, just remove the dbname file, otherwise remove the object */ 469 err = nwam_remove_object_from_backend((char *)dbname, 470 is_ncp ? NULL : name, flags); 471 472 /* 473 * Tell nwamd to remove this object. For NCUs, we need to convert the 474 * dbname filename to the NCP name to pass it to nwamd. 475 */ 476 if (is_ncu) { 477 char *ncpname; 478 479 if (nwam_ncp_file_to_name(dbname, &ncpname) == NWAM_SUCCESS) { 480 (void) nwam_request_action(hp->nwh_object_type, name, 481 ncpname, NWAM_ACTION_DESTROY); 482 free(ncpname); 483 } 484 } else { 485 (void) nwam_request_action(hp->nwh_object_type, name, NULL, 486 NWAM_ACTION_DESTROY); 487 } 488 489 if ((err == NWAM_SUCCESS) && !(flags & NWAM_FLAG_DO_NOT_FREE)) 490 nwam_free(hp); 491 492 return (err); 493 } 494 495 /* 496 * Enable/disable functions assume prior checking of activation mode 497 * to ensure an enable/disable action is valid for the object. "parent" in these 498 * functions specifies the NCP for NCUs. 499 */ 500 nwam_error_t 501 nwam_enable(const char *parent, struct nwam_handle *hp) 502 { 503 return (nwam_request_action(hp->nwh_object_type, hp->nwh_name, 504 parent, NWAM_ACTION_ENABLE)); 505 } 506 507 nwam_error_t 508 nwam_disable(const char *parent, struct nwam_handle *hp) 509 { 510 return (nwam_request_action(hp->nwh_object_type, hp->nwh_name, 511 parent, NWAM_ACTION_DISABLE)); 512 } 513 514 nwam_error_t 515 nwam_get_state(const char *parent, struct nwam_handle *hp, nwam_state_t *statep, 516 nwam_aux_state_t *auxp) 517 { 518 return (nwam_request_state(hp->nwh_object_type, hp->nwh_name, parent, 519 statep, auxp)); 520 } 521 522 struct nwam_prop_table_entry * 523 nwam_get_prop_table_entry(struct nwam_prop_table table, const char *propname) 524 { 525 struct nwam_prop_table_entry *cur = table.entries; 526 struct nwam_prop_table_entry *end = cur + table.num_entries; 527 528 assert(propname != NULL); 529 530 for (; cur < end; cur++) { 531 if (strcmp(propname, cur->prop_name) == 0) 532 return (cur); 533 } 534 return (NULL); 535 } 536 537 nwam_error_t 538 nwam_get_prop_description(struct nwam_prop_table table, const char *propname, 539 const char **descriptionp) 540 { 541 struct nwam_prop_table_entry *pte; 542 543 assert(propname != NULL && descriptionp != NULL); 544 545 if ((pte = nwam_get_prop_table_entry(table, propname)) == NULL) { 546 *descriptionp = NULL; 547 return (NWAM_INVALID_ARG); 548 } 549 550 *descriptionp = dgettext(TEXT_DOMAIN, pte->prop_description); 551 return (NWAM_SUCCESS); 552 } 553 554 nwam_error_t 555 nwam_get_prop_type(struct nwam_prop_table table, const char *propname, 556 nwam_value_type_t *typep) 557 { 558 struct nwam_prop_table_entry *pte; 559 560 assert(propname != NULL && typep != NULL); 561 562 if ((pte = nwam_get_prop_table_entry(table, propname)) == NULL) 563 return (NWAM_INVALID_ARG); 564 565 *typep = pte->prop_type; 566 567 return (NWAM_SUCCESS); 568 } 569 570 nwam_error_t 571 nwam_prop_multivalued(struct nwam_prop_table table, const char *propname, 572 boolean_t *multip) 573 { 574 struct nwam_prop_table_entry *pte; 575 576 assert(propname != NULL && multip != NULL); 577 578 if ((pte = nwam_get_prop_table_entry(table, propname)) == NULL) 579 return (NWAM_INVALID_ARG); 580 581 if (pte->prop_max_numvalues > 1) 582 *multip = B_TRUE; 583 else 584 *multip = B_FALSE; 585 586 return (NWAM_SUCCESS); 587 } 588 589 nwam_error_t 590 nwam_prop_read_only(struct nwam_prop_table table, const char *propname, 591 boolean_t *readp) 592 { 593 struct nwam_prop_table_entry *pte; 594 595 assert(propname != NULL && readp != NULL); 596 597 if ((pte = nwam_get_prop_table_entry(table, propname)) == NULL) 598 return (NWAM_INVALID_ARG); 599 600 *readp = (pte->prop_is_readonly && !nwam_uid_is_netadm()); 601 602 return (NWAM_SUCCESS); 603 } 604 605 /* 606 * Structure used to pass in prop table and errprop string pointer to internal 607 * validate function. 608 */ 609 struct validate_internal_arg { 610 struct nwam_prop_table table; 611 const char **errpropp; 612 }; 613 614 /* 615 * Callback used by nwam_walk_props() in nwam_validate(), and 616 * by nwam_validate_prop() to determine that the number, type and 617 * range of values are correct, and that validation function (if present) 618 * succeeds. 619 */ 620 static int 621 nwam_validate_prop_internal(const char *propname, nwam_value_t value, 622 void *arg) 623 { 624 struct validate_internal_arg *via = arg; 625 struct nwam_prop_table table = via->table; 626 const char **errpropp = via->errpropp; 627 struct nwam_prop_table_entry *pte; 628 nwam_error_t err; 629 nwam_value_type_t type; 630 uint_t numvalues; 631 int i; 632 633 if ((err = nwam_value_get_numvalues(value, &numvalues)) 634 != NWAM_SUCCESS || 635 (err = nwam_value_get_type(value, &type)) != NWAM_SUCCESS) { 636 if (errpropp != NULL) 637 *errpropp = propname; 638 return (err); 639 } 640 if ((pte = nwam_get_prop_table_entry(table, propname)) == NULL) 641 return (NWAM_INVALID_ARG); 642 643 /* have we get expected number of values? */ 644 if (numvalues < pte->prop_min_numvalues || 645 numvalues > pte->prop_max_numvalues) { 646 if (errpropp != NULL) 647 *errpropp = propname; 648 if (numvalues < 1) 649 return (NWAM_ENTITY_NO_VALUE); 650 else 651 return (NWAM_ENTITY_INVALID_VALUE); 652 } 653 /* Ensure type matches */ 654 if (numvalues > 0) { 655 for (i = 0; i < numvalues; i++) { 656 if (pte->prop_type != type) { 657 if (errpropp != NULL) 658 *errpropp = propname; 659 return (NWAM_ENTITY_TYPE_MISMATCH); 660 661 } 662 } 663 } 664 /* Call property-specific validation function */ 665 if (pte->prop_validate != NULL) { 666 err = pte->prop_validate(value); 667 if (err != NWAM_SUCCESS && errpropp != NULL) 668 *errpropp = propname; 669 return (err); 670 } 671 672 return (NWAM_SUCCESS); 673 } 674 675 nwam_error_t 676 nwam_validate_prop(struct nwam_prop_table table, struct nwam_handle *hp, 677 const char *propname, nwam_value_t value) 678 { 679 struct validate_internal_arg via; 680 681 assert(hp != NULL && propname != NULL); 682 683 via.table = table; 684 via.errpropp = NULL; 685 686 return ((nwam_error_t)nwam_validate_prop_internal(propname, 687 value, &via)); 688 } 689 690 nwam_error_t 691 nwam_validate(struct nwam_prop_table table, struct nwam_handle *hp, 692 const char **errpropp) 693 { 694 struct validate_internal_arg via; 695 nwam_error_t err1, err2; 696 697 assert(hp != NULL); 698 699 via.table = table; 700 via.errpropp = errpropp; 701 702 err1 = nwam_walk_props(hp, nwam_validate_prop_internal, &via, 703 0, (int *)&err2); 704 if (err1 != NWAM_SUCCESS) 705 return (err2); 706 return (NWAM_SUCCESS); 707 } 708 709 /* 710 * Given the type and class flag representations, return the list of properties 711 * that can be set for that type/class combination. Note this list is a complete 712 * property list that includes both the required and the optional properties. 713 * The type and class flags are only used for NCU objects at present. 714 * 715 * Caller needs to free prop_list. 716 */ 717 nwam_error_t 718 nwam_get_default_proplist(struct nwam_prop_table table, 719 uint64_t type, uint64_t class, const char ***prop_list, uint_t *numvalues) 720 { 721 struct nwam_prop_table_entry *cur = table.entries; 722 struct nwam_prop_table_entry *end = cur + table.num_entries; 723 int i = 0; 724 const char **list = NULL; 725 726 assert(prop_list != NULL && numvalues != NULL); 727 728 /* Construct a list of all properties for required type/class */ 729 list = calloc(table.num_entries, sizeof (char *)); 730 if (list == NULL) { 731 *prop_list = NULL; 732 *numvalues = 0; 733 return (NWAM_NO_MEMORY); 734 } 735 for (; cur < end; cur++) { 736 if (((type & cur->prop_type_membership) == 0) || 737 ((class & cur->prop_class_membership) == 0)) 738 continue; 739 list[i++] = cur->prop_name; 740 } 741 *numvalues = i; 742 *prop_list = list; 743 return (NWAM_SUCCESS); 744 } 745