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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 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 /* 30 * libfru is divided into the following modules: 31 * 1) This file. Support for the API and ties together all the sub-modules. 32 * 2) The parser which parses the field_paths supplied by the user. 33 * 3) The data_source sub-libraries which provide payloads(tags) and the tree 34 * structure of frus and locations. 35 * 4) The PayloadReader which given a payload and a path definition can extract 36 * the exact field the user is looking for. 37 * 5) The Registry which provides the definitions for all the Data Elements 38 * supported. 39 * 40 * The basic algorithim for reading/updating fields is this: 41 * 1) Parse the field_path given by the user. 42 * 2) Using the registry determine which payloads this data MAY appear in. 43 * 3) Figure out which tags of this type are in the container. 44 * 4) Find the specific tag which contains the instance of this data the user 45 * requested. 46 * 5) Get this tag from the data source and read it with the PayloadReader to 47 * read/write data. 48 * 6) For UPDATES write this tag back to the data source. 49 * 50 * This algorithim is altered only when dealing with "UNKNOWN" payloads where 51 * it simplifies slightly. 52 */ 53 54 #include <assert.h> 55 #include <string.h> 56 #include <stdlib.h> 57 #include <stdio.h> 58 #include <libintl.h> 59 #include <pthread.h> 60 #include <stdarg.h> 61 #include <dlfcn.h> 62 #include <alloca.h> 63 #include <limits.h> 64 65 #include "libfru.h" 66 #include "libfrup.h" 67 #include "libfruds.h" 68 #include "Ancestor.h" 69 #include "libfrureg.h" 70 #include "Parser.h" 71 #include "PayloadReader.h" 72 73 #define DATA_SOURCE_OBJ_NAME "data_source" 74 75 #define ENCRYPTION_LIB_NAME "libfrucrypt.so.1" 76 #define FRU_ENCRYPT_FUNC_NAME "fru_encrypt_func" 77 78 #define UNKNOWN_PATH "UNKNOWN" 79 #define IS_UNKNOWN_PATH(path) \ 80 ((strcmp(path, "/UNKNOWN") == 0) || (strcmp(path, "UNKNOWN") == 0)) 81 82 #define NODEHDL_TO_TREEHDL(nodehdl) (fru_treehdl_t)nodehdl 83 #define TREEHDL_TO_NODEHDL(treehdl) (fru_nodehdl_t)treehdl 84 85 /* ========================================================================= */ 86 /* 87 * Define a hash of rwlocks for each container. 88 */ 89 struct cont_lock 90 { 91 fru_nodehdl_t handle; 92 pthread_rwlock_t lock; 93 struct cont_lock *next; 94 }; 95 typedef struct cont_lock cont_lock_t; 96 97 #define CONT_LOCK_HASH_NUM 128 98 cont_lock_t *cont_lock_hash[CONT_LOCK_HASH_NUM]; 99 pthread_mutex_t cont_lock_hash_lock; 100 101 typedef enum { WRITE_LOCK, READ_LOCK } lock_mode_t; 102 103 /* 104 * These control the Data sources available. 105 */ 106 static pthread_mutex_t ds_lock; 107 static fru_datasource_t *data_source = NULL; 108 static void *ds_lib = NULL; 109 static int ds_lib_ref_cnt = 0; 110 static char *ds_lib_name = NULL; 111 112 #define FRU_NORESPONSE_RETRY 500 113 114 #define RETRY(expr) \ 115 { for (int loop = 0; loop < FRU_NORESPONSE_RETRY && \ 116 (expr) == FRU_NORESPONSE; loop++) ; \ 117 } 118 119 /* ========================================================================= */ 120 static const char *fru_errmsg[] = 121 { 122 "Success", 123 "Node not found", 124 "IO error", 125 "No registry definition for this element", 126 "Not container", 127 "Invalid handle", 128 "Invalid Segment", 129 "Invalid Path", 130 "Invalid Element", 131 "Invalid Data size (does not match registry definition)", 132 "Duplicate Segment", 133 "Not Field", 134 "No space available", 135 "Data could not be found", 136 "Iteration full", 137 "Invalid Permisions", 138 "Feature not Supported", 139 "Element is not Tagged", 140 "Failed to read container device", 141 "Segment Corrupt", 142 "Data Corrupt", 143 "General LIBFRU FAILURE", 144 "Walk terminated", 145 "FRU No response", 146 "Unknown error" 147 }; 148 149 fru_errno_t 150 fru_encryption_supported(void) 151 { 152 if (encrypt_func == NULL) 153 return (FRU_NOTSUP); 154 else 155 return (FRU_SUCCESS); 156 } 157 158 extern "C" { 159 void 160 init_libfru(void) 161 { 162 // attempt to find the encryption library. 163 void *crypt_lib = NULL; 164 encrypt_func = NULL; 165 crypt_lib = dlopen(ENCRYPTION_LIB_NAME, RTLD_LAZY); 166 if (crypt_lib != NULL) { 167 encrypt_func = (fru_encrypt_func_t)dlsym(crypt_lib, 168 FRU_ENCRYPT_FUNC_NAME); 169 } 170 } 171 #pragma init(init_libfru) 172 } 173 174 /* ========================================================================= */ 175 static void 176 add_cont_lock(cont_lock_t *lock) 177 { 178 cont_lock_t *prev = NULL; 179 int hash_bucket = lock->handle % CONT_LOCK_HASH_NUM; 180 181 /* insert at tail */ 182 if (cont_lock_hash[hash_bucket] == NULL) { 183 cont_lock_hash[hash_bucket] = lock; 184 } else { 185 cont_lock_t *prev = cont_lock_hash[hash_bucket]; 186 while (prev->next != NULL) { 187 prev = prev->next; 188 } 189 prev->next = lock; 190 } 191 } 192 193 /* ========================================================================= */ 194 static cont_lock_t * 195 find_cont_lock(fru_nodehdl_t handle) 196 { 197 int hash_bucket = handle % CONT_LOCK_HASH_NUM; 198 cont_lock_t *which = cont_lock_hash[hash_bucket]; 199 200 while (which != NULL) { 201 if (which->handle == handle) { 202 break; 203 } 204 which = which->next; 205 } 206 return (which); 207 } 208 209 /* ========================================================================= */ 210 static cont_lock_t * 211 alloc_cont_lock(fru_nodehdl_t handle) 212 { 213 cont_lock_t *lock = (cont_lock_t *)malloc(sizeof (cont_lock_t)); 214 if (lock == NULL) { 215 return (NULL); 216 } 217 lock->handle = handle; 218 if (pthread_rwlock_init(&(lock->lock), NULL) != 0) { 219 free(lock); 220 return (NULL); 221 } 222 lock->next = NULL; 223 return (lock); 224 } 225 226 /* ========================================================================= */ 227 static fru_errno_t 228 lock_container(lock_mode_t mode, fru_nodehdl_t handle) 229 { 230 cont_lock_t *which = NULL; 231 int hash_bucket = 0; 232 int lock_rc; 233 234 pthread_mutex_lock(&cont_lock_hash_lock); 235 236 which = find_cont_lock(handle); 237 238 /* if not found add to hash */ 239 if (which == NULL) { 240 if ((which = alloc_cont_lock(handle)) == NULL) { 241 pthread_mutex_unlock(&cont_lock_hash_lock); 242 return (FRU_FAILURE); 243 } 244 add_cont_lock(which); 245 } 246 247 /* execute lock */ 248 lock_rc = 0; 249 switch (mode) { 250 case READ_LOCK: 251 lock_rc = pthread_rwlock_rdlock(&(which->lock)); 252 break; 253 case WRITE_LOCK: 254 lock_rc = pthread_rwlock_wrlock(&(which->lock)); 255 break; 256 } 257 258 pthread_mutex_unlock(&cont_lock_hash_lock); 259 if (lock_rc != 0) { 260 return (FRU_FAILURE); 261 } 262 return (FRU_SUCCESS); 263 } 264 265 /* ========================================================================= */ 266 /* 267 * Macro to make checking unlock_conatiner error code easier 268 */ 269 #define CHK_UNLOCK_CONTAINER(handle) \ 270 if (unlock_container(handle) != FRU_SUCCESS) { \ 271 return (FRU_FAILURE); \ 272 } 273 static fru_errno_t 274 unlock_container(fru_nodehdl_t handle) 275 { 276 cont_lock_t *which = NULL; 277 pthread_mutex_lock(&cont_lock_hash_lock); 278 279 which = find_cont_lock(handle); 280 if (which == NULL) { 281 pthread_mutex_unlock(&cont_lock_hash_lock); 282 return (FRU_NODENOTFOUND); 283 } 284 285 if (pthread_rwlock_unlock(&(which->lock)) != 0) { 286 pthread_mutex_unlock(&cont_lock_hash_lock); 287 return (FRU_FAILURE); 288 } 289 290 pthread_mutex_unlock(&cont_lock_hash_lock); 291 return (FRU_SUCCESS); 292 } 293 294 /* ========================================================================= */ 295 static fru_errno_t 296 clear_cont_locks(void) 297 { 298 pthread_mutex_lock(&cont_lock_hash_lock); 299 300 // for each bucket 301 for (int i = 0; i < CONT_LOCK_HASH_NUM; i++) { 302 // free all the locks 303 cont_lock_t *cur = cont_lock_hash[i]; 304 while (cur != NULL) { 305 cont_lock_t *tmp = cur; 306 cur = cur->next; 307 pthread_rwlock_destroy(&(tmp->lock)); 308 free(tmp); 309 } 310 cont_lock_hash[i] = NULL; 311 } 312 313 pthread_mutex_unlock(&cont_lock_hash_lock); 314 return (FRU_SUCCESS); 315 } 316 317 318 /* ========================================================================= */ 319 /* VARARGS */ 320 fru_errno_t 321 fru_open_data_source(const char *name, ...) 322 { 323 fru_errno_t err = FRU_SUCCESS; 324 325 va_list args; 326 int num_args = 0; 327 char **init_args = NULL; 328 char *tmp; 329 int i = 0; 330 331 char ds_name[PATH_MAX]; 332 fru_datasource_t *ds = NULL; 333 void *tmp_lib = NULL; 334 335 pthread_mutex_lock(&ds_lock); 336 337 if ((ds_lib_name != NULL) && (data_source != NULL)) { 338 // we already have a DS assigned. 339 if ((strcmp(ds_lib_name, name) == 0)) { 340 // user wants to open the same one... ok. 341 ds_lib_ref_cnt++; 342 pthread_mutex_unlock(&ds_lock); 343 return (FRU_SUCCESS); 344 } else { 345 pthread_mutex_unlock(&ds_lock); 346 return (FRU_FAILURE); 347 } 348 } 349 350 snprintf(ds_name, sizeof (ds_name), "libfru%s.so.%d", 351 name, LIBFRU_DS_VER); 352 tmp_lib = dlopen(ds_name, RTLD_LAZY); 353 if (tmp_lib == NULL) { 354 pthread_mutex_unlock(&ds_lock); 355 return (FRU_NOTSUP); 356 } 357 ds = (fru_datasource_t *)dlsym(tmp_lib, 358 DATA_SOURCE_OBJ_NAME); 359 if (ds == NULL) { 360 pthread_mutex_unlock(&ds_lock); 361 return (FRU_FAILURE); 362 } 363 364 va_start(args, name); 365 tmp = va_arg(args, char *); 366 while (tmp != NULL) { 367 num_args++; 368 tmp = va_arg(args, char *); 369 } 370 va_end(args); 371 372 init_args = (char **)malloc(sizeof (char *) * num_args); 373 if (init_args == NULL) { 374 pthread_mutex_unlock(&ds_lock); 375 return (FRU_FAILURE); 376 } 377 378 va_start(args, name); 379 for (tmp = va_arg(args, char *), i = 0; 380 (tmp != NULL) && (i < num_args); 381 tmp = va_arg(args, char *), i++) { 382 init_args[i] = tmp; 383 } 384 va_end(args); 385 386 if ((err = ds->initialize(num_args, init_args)) == FRU_SUCCESS) { 387 // don't switch unless the source connects ok. 388 ds_lib = tmp_lib; 389 data_source = ds; 390 ds_lib_name = strdup(name); 391 ds_lib_ref_cnt++; 392 } 393 394 free(init_args); 395 pthread_mutex_unlock(&ds_lock); 396 return (err); 397 } 398 399 400 /* ========================================================================= */ 401 fru_errno_t 402 fru_close_data_source(void) 403 { 404 fru_errno_t err = FRU_SUCCESS; 405 406 if (ds_lib_ref_cnt == 0) { 407 return (FRU_FAILURE); 408 } 409 410 pthread_mutex_lock(&ds_lock); 411 if ((--ds_lib_ref_cnt) == 0) { 412 /* don't check err code here */ 413 err = data_source->shutdown(); 414 /* continue to clean up libfru and return the err at the end */ 415 clear_cont_locks(); 416 dlclose(ds_lib); 417 ds_lib = NULL; 418 free(ds_lib_name); 419 ds_lib_name = NULL; 420 data_source = NULL; 421 } 422 423 pthread_mutex_unlock(&ds_lock); 424 return (err); 425 } 426 427 /* ========================================================================= */ 428 int 429 segment_is_encrypted(fru_nodehdl_t container, const char *seg_name) 430 { 431 fru_errno_t err = FRU_SUCCESS; 432 fru_segdef_t segdef; 433 434 if (data_source == NULL) { 435 return (0); 436 } 437 438 RETRY(err = data_source->get_seg_def(NODEHDL_TO_TREEHDL(container), 439 seg_name, &segdef)) 440 441 if (err != FRU_SUCCESS) { 442 return (0); 443 } 444 445 return (segdef.desc.field.encrypted == 1); 446 } 447 448 /* ========================================================================= */ 449 static fru_errno_t 450 get_seg_list_from_ds(fru_nodehdl_t node, fru_strlist_t *list) 451 { 452 fru_errno_t err = FRU_SUCCESS; 453 fru_strlist_t raw_list; 454 if (data_source == NULL) { 455 return (FRU_FAILURE); 456 } 457 458 /* get a list of all segments */ 459 RETRY(err = data_source->get_seg_list(NODEHDL_TO_TREEHDL(node), 460 &raw_list)) 461 462 if (err != FRU_SUCCESS) { 463 return (err); 464 } 465 466 /* leave out the encrypted segments if necessary */ 467 list->num = 0; 468 list->strs = (char **)malloc(sizeof (*(list->strs)) * raw_list.num); 469 if (list->strs == NULL) { 470 fru_destroy_strlist(&raw_list); 471 return (err); 472 } 473 for (int i = 0; i < raw_list.num; i++) { 474 if (segment_is_encrypted(node, raw_list.strs[i])) { 475 if (fru_encryption_supported() == FRU_SUCCESS) { 476 list->strs[list->num] 477 = strdup(raw_list.strs[i]); 478 list->num++; 479 } // else leave it out. 480 } else { 481 list->strs[list->num] = strdup(raw_list.strs[i]); 482 list->num++; 483 } 484 } 485 486 fru_destroy_strlist(&raw_list); 487 return (FRU_SUCCESS); 488 } 489 490 491 /* ========================================================================= */ 492 const char * 493 fru_strerror(fru_errno_t errnum) 494 { 495 if ((errnum < (sizeof (fru_errmsg)/sizeof (*fru_errmsg))) && 496 (errnum >= 0)) { 497 return (gettext(fru_errmsg[errnum])); 498 } 499 return (gettext 500 (fru_errmsg[(sizeof (fru_errmsg)/sizeof (*fru_errmsg))])); 501 } 502 503 /* ========================================================================= */ 504 fru_errno_t 505 fru_get_root(fru_nodehdl_t *handle) 506 { 507 fru_errno_t err = FRU_SUCCESS; 508 fru_treehdl_t tr_root; 509 if (data_source == NULL) { 510 return (FRU_FAILURE); 511 } 512 513 RETRY(err = data_source->get_root(&tr_root)) 514 if (err == FRU_SUCCESS) { 515 *handle = TREEHDL_TO_NODEHDL(tr_root); 516 } 517 return (err); 518 } 519 520 /* ========================================================================= */ 521 fru_errno_t 522 fru_get_child(fru_nodehdl_t handle, fru_nodehdl_t *child) 523 { 524 fru_errno_t err = FRU_SUCCESS; 525 fru_treehdl_t tr_child; 526 fru_node_t type; 527 if (data_source == NULL) { 528 return (FRU_FAILURE); 529 } 530 531 RETRY(err = data_source->get_child(NODEHDL_TO_TREEHDL(handle), 532 &tr_child)) 533 if (err != FRU_SUCCESS) { 534 return (err); 535 } 536 537 RETRY(err = data_source->get_node_type(tr_child, &type)) 538 539 if (err != FRU_SUCCESS) { 540 return (err); 541 } 542 if ((type == FRU_NODE_LOCATION) || 543 (type == FRU_NODE_FRU) || 544 (type == FRU_NODE_CONTAINER)) { 545 *child = TREEHDL_TO_NODEHDL(tr_child); 546 return (FRU_SUCCESS); 547 } 548 549 /* 550 * if the child is not valid try and find a peer of the child which is 551 * valid 552 */ 553 do { 554 RETRY(err = data_source->get_peer(tr_child, &tr_child)) 555 if (err != FRU_SUCCESS) { 556 return (err); 557 } 558 559 RETRY(err = data_source->get_node_type(tr_child, &type)) 560 if (err != FRU_SUCCESS) { 561 return (err); 562 } 563 if ((type == FRU_NODE_LOCATION) || 564 (type == FRU_NODE_FRU) || 565 (type == FRU_NODE_CONTAINER)) { 566 *child = TREEHDL_TO_NODEHDL(tr_child); 567 return (FRU_SUCCESS); 568 } 569 } while (1); 570 } 571 572 /* ========================================================================= */ 573 fru_errno_t 574 fru_get_peer(fru_nodehdl_t handle, fru_nodehdl_t *peer) 575 { 576 fru_errno_t err = FRU_SUCCESS; 577 fru_treehdl_t tr_peer = NODEHDL_TO_TREEHDL(handle); 578 fru_node_t type; 579 580 if (data_source == NULL) { 581 return (FRU_FAILURE); 582 } 583 584 do { 585 RETRY(err = data_source->get_peer(tr_peer, &tr_peer)) 586 587 if (err != FRU_SUCCESS) { 588 return (err); 589 } 590 591 RETRY(err = data_source->get_node_type(tr_peer, &type)) 592 if (err != FRU_SUCCESS) { 593 return (err); 594 } 595 if ((type == FRU_NODE_LOCATION) || 596 (type == FRU_NODE_FRU) || 597 (type == FRU_NODE_CONTAINER)) { 598 *peer = TREEHDL_TO_NODEHDL(tr_peer); 599 return (FRU_SUCCESS); 600 } 601 } while (1); 602 } 603 /* ========================================================================= */ 604 fru_errno_t 605 fru_get_parent(fru_nodehdl_t handle, fru_nodehdl_t *parent) 606 { 607 fru_errno_t err = FRU_SUCCESS; 608 fru_treehdl_t tr_parent; 609 if (data_source == NULL) { 610 return (FRU_FAILURE); 611 } 612 613 RETRY(err = data_source->get_parent(NODEHDL_TO_TREEHDL(handle), 614 &tr_parent)) 615 if (err == FRU_SUCCESS) { 616 *parent = TREEHDL_TO_NODEHDL(tr_parent); 617 } 618 return (err); 619 } 620 621 622 /* ========================================================================= */ 623 fru_errno_t 624 fru_get_name_from_hdl(fru_nodehdl_t handle, char **name) 625 { 626 fru_errno_t err = FRU_SUCCESS; 627 628 if (data_source == NULL) { 629 return (FRU_FAILURE); 630 } 631 632 RETRY(err = data_source->get_name_from_hdl(NODEHDL_TO_TREEHDL(handle), 633 name)) 634 return (err); 635 } 636 637 /* ========================================================================= */ 638 /* 639 * Project-private interface 640 * 641 * Apply process_node() to each node in the tree rooted at "node". 642 * 643 * process_node() has available the handle, path (in the subtree from the root 644 * "node" passed to fru_walk_tree()), and name of the node to which it is 645 * applied, as well as any arguments provided via the generic pointer "args". 646 * process_node() also takes a pointer to an end_node() function pointer 647 * argument and a pointer to a generic pointer "end_args" argument. If 648 * non-null, end_node() is called after the node and its children have been 649 * processed, but before the node's siblings are visited. 650 */ 651 extern "C" fru_errno_t 652 fru_walk_tree(fru_nodehdl_t node, const char *prior_path, 653 fru_errno_t (*process_node)(fru_nodehdl_t node, 654 const char *path, 655 const char *name, void *args, 656 end_node_fp_t *end_node, 657 void **end_args), 658 void *args) 659 { 660 void *end_args = NULL; 661 662 char *name = NULL, *path; 663 664 int prior_length; 665 666 fru_errno_t status; 667 668 fru_nodehdl_t next; 669 670 end_node_fp_t end_node = NULL; 671 672 673 /* Build node's path */ 674 if ((status = fru_get_name_from_hdl(node, &name)) != FRU_SUCCESS) 675 return (status); 676 else if (name == NULL) 677 return (FRU_FAILURE); 678 679 prior_length = strlen(prior_path); 680 path = (char *)alloca(prior_length + sizeof ("/") + strlen(name)); 681 (void) sprintf(path, "%s/%s", prior_path, name); 682 free(name); 683 name = path + prior_length + 1; 684 685 686 /* Process node */ 687 assert(process_node != NULL); 688 if ((status = process_node(node, path, name, args, 689 &end_node, &end_args)) 690 != FRU_SUCCESS) { 691 if (end_node) end_node(node, path, name, end_args); 692 return (status); 693 } 694 695 696 /* Process children */ 697 if ((status = fru_get_child(node, &next)) == FRU_SUCCESS) 698 status = fru_walk_tree(next, path, process_node, args); 699 else if (status == FRU_NODENOTFOUND) 700 status = FRU_SUCCESS; 701 702 /* "Close" node */ 703 if (end_node) end_node(node, path, name, end_args); 704 if (status != FRU_SUCCESS) 705 return (status); 706 707 /* Process siblings */ 708 if ((status = fru_get_peer(node, &next)) == FRU_SUCCESS) 709 status = fru_walk_tree(next, prior_path, process_node, args); 710 else if (status == FRU_NODENOTFOUND) 711 status = FRU_SUCCESS; 712 713 return (status); 714 } 715 716 /* ========================================================================= */ 717 /* 718 * Project-private interface 719 * 720 * Return true if "searchpath" equals "path" or is a tail of "path" and 721 * begins at a component name within "path" 722 */ 723 int 724 fru_pathmatch(const char *path, const char *searchpath) 725 { 726 const char *match; 727 728 if (((match = strstr(path, searchpath)) != NULL) && 729 ((match + strlen(searchpath)) == (path + strlen(path))) && 730 ((match == path) || (*(match - 1) == '/'))) 731 return (1); 732 733 return (0); 734 } 735 736 /* ========================================================================= */ 737 fru_errno_t 738 fru_get_node_type(fru_nodehdl_t handle, fru_node_t *type) 739 { 740 fru_errno_t err = FRU_SUCCESS; 741 fru_node_t tmp; 742 if (data_source == NULL) { 743 return (FRU_FAILURE); 744 } 745 746 RETRY(err = data_source->get_node_type(NODEHDL_TO_TREEHDL(handle), 747 &tmp)) 748 if (err == FRU_SUCCESS) { 749 *type = tmp; 750 } 751 return (err); 752 } 753 754 /* ========================================================================= */ 755 static fru_errno_t 756 is_container(fru_nodehdl_t handle) 757 { 758 fru_errno_t err = FRU_SUCCESS; 759 fru_node_t type; 760 if ((err = fru_get_node_type(handle, &type)) != FRU_SUCCESS) { 761 return (err); 762 } 763 if (type == FRU_NODE_CONTAINER) { 764 return (FRU_SUCCESS); 765 } 766 return (FRU_NOTCONTAINER); 767 } 768 769 /* ========================================================================= */ 770 fru_errno_t 771 fru_destroy_enum(fru_enum_t *e) 772 { 773 if (e == NULL) { 774 return (FRU_SUCCESS); 775 } 776 if (e->text != NULL) 777 free(e->text); 778 779 return (FRU_SUCCESS); 780 } 781 782 /* ========================================================================= */ 783 /* 784 * NOTE: does not free list. This is allocated by the user and should be 785 * deallocated by the user. 786 */ 787 fru_errno_t 788 fru_destroy_strlist(fru_strlist_t *list) 789 { 790 if (list == NULL) { 791 return (FRU_SUCCESS); 792 } 793 if (list->strs != NULL) { 794 for (int i = 0; i < list->num; i++) { 795 if (list->strs[i] != NULL) 796 free(list->strs[i]); 797 } 798 free(list->strs); 799 } 800 801 list->num = 0; 802 803 return (FRU_SUCCESS); 804 } 805 806 /* ========================================================================= */ 807 fru_errno_t 808 fru_destroy_elemdef(fru_elemdef_t *def) 809 { 810 if (def == NULL) { 811 return (FRU_SUCCESS); 812 } 813 if (def->enum_table != NULL) { 814 for (int i = 0; i < def->enum_count; i++) 815 fru_destroy_enum(&(def->enum_table[i])); 816 free(def->enum_table); 817 } 818 def->enum_count = 0; 819 820 if (def->example_string != NULL) 821 free(def->example_string); 822 823 return (FRU_SUCCESS); 824 } 825 826 /* ========================================================================= */ 827 fru_errno_t 828 fru_list_segments(fru_nodehdl_t container, fru_strlist_t *list) 829 { 830 fru_errno_t err = FRU_SUCCESS; 831 832 if ((err = is_container(container)) != FRU_SUCCESS) { 833 return (err); 834 } 835 836 if (lock_container(READ_LOCK, container) != FRU_SUCCESS) { 837 return (FRU_FAILURE); 838 } 839 840 err = get_seg_list_from_ds(container, list); 841 842 CHK_UNLOCK_CONTAINER(container); 843 return (err); 844 } 845 846 /* ========================================================================= */ 847 fru_errno_t 848 fru_create_segment(fru_nodehdl_t container, fru_segdef_t *def) 849 { 850 fru_errno_t err = FRU_SUCCESS; 851 int i = 0; 852 853 if (data_source == NULL) { 854 return (FRU_FAILURE); 855 } 856 857 if ((def->desc.field.encrypted == 1) && 858 (fru_encryption_supported() == FRU_NOTSUP)) { 859 return (FRU_NOTSUP); 860 } 861 862 if ((err = is_container(container)) != FRU_SUCCESS) { 863 return (err); 864 } 865 866 if (lock_container(WRITE_LOCK, container) != FRU_SUCCESS) { 867 return (FRU_FAILURE); 868 } 869 fru_strlist_t seg_list; 870 871 /* get a list of all segments */ 872 /* here we do not want to leave out the encrypted segments. */ 873 RETRY(err = data_source->get_seg_list(NODEHDL_TO_TREEHDL(container), 874 &seg_list)) 875 if (err != FRU_SUCCESS) { 876 CHK_UNLOCK_CONTAINER(container); 877 return (err); 878 } 879 880 for (i = 0; i < seg_list.num; i++) { 881 if (strncmp(seg_list.strs[i], def->name, FRU_SEGNAMELEN) 882 == 0) { 883 fru_destroy_strlist(&seg_list); 884 CHK_UNLOCK_CONTAINER(container); 885 return (FRU_DUPSEG); 886 } 887 } 888 fru_destroy_strlist(&seg_list); 889 890 RETRY(err = data_source->add_seg(NODEHDL_TO_TREEHDL(container), def)) 891 892 CHK_UNLOCK_CONTAINER(container); 893 return (err); 894 } 895 896 /* ========================================================================= */ 897 fru_errno_t 898 fru_remove_segment(fru_nodehdl_t container, const char *seg_name) 899 { 900 fru_errno_t err = FRU_SUCCESS; 901 if ((seg_name == NULL) || (strlen(seg_name) > FRU_SEGNAMELEN)) { 902 return (FRU_INVALSEG); 903 } 904 905 if (data_source == NULL) { 906 return (FRU_FAILURE); 907 } 908 909 if ((err = is_container(container)) != FRU_SUCCESS) { 910 return (err); 911 } 912 913 if (lock_container(WRITE_LOCK, container) != FRU_SUCCESS) { 914 return (FRU_FAILURE); 915 } 916 917 /* do not allow encrypted segments to be removed */ 918 /* unless encryption is supported */ 919 if ((segment_is_encrypted(container, seg_name)) && 920 (fru_encryption_supported() == FRU_NOTSUP)) { 921 err = FRU_INVALSEG; 922 } else { 923 RETRY(err = 924 data_source->delete_seg(NODEHDL_TO_TREEHDL(container), 925 seg_name)) 926 } 927 928 CHK_UNLOCK_CONTAINER(container); 929 return (err); 930 } 931 932 /* ========================================================================= */ 933 fru_errno_t 934 fru_get_segment_def(fru_nodehdl_t container, const char *seg_name, 935 fru_segdef_t *definition) 936 { 937 fru_errno_t err = FRU_SUCCESS; 938 if ((seg_name == NULL) || (strlen(seg_name) > 2)) { 939 return (FRU_INVALSEG); 940 } 941 942 if (data_source == NULL) { 943 return (FRU_FAILURE); 944 } 945 946 if ((err = is_container(container)) != FRU_SUCCESS) { 947 return (err); 948 } 949 950 if (lock_container(READ_LOCK, container) != FRU_SUCCESS) { 951 return (FRU_FAILURE); 952 } 953 954 // NOTE: not passing "definition" to this function such that I may 955 // check for encryption before allowing the user to get the data. 956 fru_segdef_t segdef; 957 958 RETRY(err = data_source->get_seg_def(NODEHDL_TO_TREEHDL(container), 959 seg_name, &segdef)) 960 961 if (err != FRU_SUCCESS) { 962 CHK_UNLOCK_CONTAINER(container); 963 return (err); 964 } 965 966 if ((segdef.desc.field.encrypted == 1) && 967 (fru_encryption_supported() == FRU_NOTSUP)) { 968 CHK_UNLOCK_CONTAINER(container); 969 return (FRU_INVALSEG); 970 } 971 972 // After encryption check, copy from my def to users. 973 definition->version = segdef.version; 974 strlcpy(definition->name, segdef.name, FRU_SEGNAMELEN+1); 975 definition->desc = segdef.desc; 976 definition->size = segdef.size; 977 definition->address = segdef.address; 978 definition->hw_desc = segdef.hw_desc; 979 980 CHK_UNLOCK_CONTAINER(container); 981 return (FRU_SUCCESS); 982 } 983 984 /* ========================================================================= */ 985 fru_errno_t 986 fru_list_elems_in(fru_nodehdl_t container, const char *seg_name, 987 fru_strlist_t *list) 988 { 989 fru_errno_t err = FRU_SUCCESS; 990 fru_tag_t *tags = NULL; 991 int i = 0; 992 int num_tags = 0; 993 fru_strlist_t rc_list; 994 995 if ((seg_name == NULL) || (strlen(seg_name) > 2)) { 996 return (FRU_INVALSEG); 997 } 998 999 if (data_source == NULL) { 1000 return (FRU_FAILURE); 1001 } 1002 1003 if ((err = is_container(container)) != FRU_SUCCESS) { 1004 return (err); 1005 } 1006 1007 if (lock_container(READ_LOCK, container) != FRU_SUCCESS) { 1008 return (FRU_FAILURE); 1009 } 1010 1011 if ((segment_is_encrypted(container, seg_name)) && 1012 (fru_encryption_supported() == FRU_NOTSUP)) { 1013 CHK_UNLOCK_CONTAINER(container); 1014 return (FRU_INVALSEG); 1015 } 1016 1017 RETRY(err = data_source->get_tag_list(NODEHDL_TO_TREEHDL(container), 1018 seg_name, &tags, &num_tags)) 1019 if (err != FRU_SUCCESS) { 1020 CHK_UNLOCK_CONTAINER(container); 1021 return (err); 1022 } 1023 if (num_tags == 0) { 1024 CHK_UNLOCK_CONTAINER(container); 1025 list->num = 0; 1026 list->strs = NULL; 1027 return (FRU_SUCCESS); 1028 } 1029 1030 // allocate the memory for the names. 1031 rc_list.num = 0; 1032 rc_list.strs = (char **)malloc(num_tags * sizeof (char *)); 1033 if (rc_list.strs == NULL) { 1034 CHK_UNLOCK_CONTAINER(container); 1035 free(tags); 1036 return (FRU_FAILURE); 1037 } 1038 1039 // for each tag fill in it's name. 1040 for (i = 0; i < num_tags; i++) { 1041 const fru_regdef_t *def = fru_reg_lookup_def_by_tag(tags[i]); 1042 if (def != NULL) { 1043 rc_list.strs[i] = strdup(def->name); 1044 if (rc_list.strs[i] == NULL) { 1045 CHK_UNLOCK_CONTAINER(container); 1046 fru_destroy_strlist(&rc_list); 1047 free(tags); 1048 return (FRU_FAILURE); 1049 } 1050 } else { 1051 // instead of failing return "UNKNOWN" 1052 rc_list.strs[i] = strdup(UNKNOWN_PATH); 1053 if (rc_list.strs[i] == NULL) { 1054 CHK_UNLOCK_CONTAINER(container); 1055 fru_destroy_strlist(&rc_list); 1056 free(tags); 1057 return (FRU_FAILURE); 1058 } 1059 } 1060 rc_list.num++; 1061 } 1062 1063 CHK_UNLOCK_CONTAINER(container); 1064 list->num = rc_list.num; 1065 list->strs = rc_list.strs; 1066 free(tags); 1067 return (FRU_SUCCESS); 1068 } 1069 1070 /* ========================================================================= */ 1071 /* Project-private interface */ 1072 extern "C" fru_errno_t 1073 fru_for_each_segment(fru_nodehdl_t container, 1074 int (*function)(fru_seghdl_t segment, void *args), 1075 void *args) 1076 { 1077 fru_errno_t status; 1078 1079 1080 if (data_source == NULL) { 1081 return (FRU_FAILURE); 1082 } 1083 1084 if (lock_container(READ_LOCK, container) != FRU_SUCCESS) { 1085 return (FRU_FAILURE); 1086 } 1087 RETRY(status = 1088 data_source->for_each_segment(NODEHDL_TO_TREEHDL(container), 1089 function, args)) 1090 CHK_UNLOCK_CONTAINER(container); 1091 return (status); 1092 } 1093 1094 /* ========================================================================= */ 1095 /* 1096 * Project-private interface 1097 * 1098 * This routine is only safe when called from within fru_for_each_segment() 1099 * (which is currently the only way to get a segment handle) so that the 1100 * segment's container will be locked 1101 */ 1102 fru_errno_t 1103 fru_get_segment_name(fru_seghdl_t segment, char **name) 1104 { 1105 fru_errno_t err = FRU_SUCCESS; 1106 1107 assert(data_source != NULL); 1108 1109 RETRY(err = data_source->get_segment_name(NODEHDL_TO_TREEHDL(segment), 1110 name)) 1111 return (err); 1112 } 1113 1114 /* ========================================================================= */ 1115 /* 1116 * Project-private interface 1117 * 1118 * This routine is only safe when called from within fru_for_each_segment() 1119 * (which is currently the only way to get a segment handle) so that the 1120 * segment's container will be locked 1121 */ 1122 extern "C" fru_errno_t 1123 fru_for_each_packet(fru_seghdl_t segment, 1124 int (*function)(fru_tag_t *tag, uint8_t *payload, 1125 size_t length, void *args), 1126 void *args) 1127 { 1128 fru_errno_t err = FRU_SUCCESS; 1129 1130 assert(data_source != NULL); 1131 1132 RETRY(err = data_source->for_each_packet(NODEHDL_TO_TREEHDL(segment), 1133 function, args)) 1134 return (err); 1135 } 1136 1137 1138 /* ========================================================================= */ 1139 // To keep track of the number of instances for each type of tag which 1140 // might occur. 1141 struct TagInstPair 1142 { 1143 int inst; 1144 fru_tag_t tag; 1145 }; 1146 1147 struct tag_inst_hist_t 1148 { 1149 TagInstPair *pairs; 1150 unsigned size; 1151 unsigned numStored; 1152 }; 1153 1154 static fru_errno_t 1155 update_tag_inst_hist(tag_inst_hist_t *hist, fru_tag_t tag) 1156 { 1157 // find if this tag has occured before. 1158 int found = 0; 1159 for (int s = 0; s < (hist->numStored); s++) { 1160 if (tags_equal((hist->pairs)[s].tag, tag)) { 1161 // if so just add to the instance. 1162 hist->pairs[s].inst++; 1163 found = 1; 1164 break; 1165 } 1166 } 1167 // if not add to the end of the array of instance 0. 1168 if (!found) { 1169 if (hist->numStored > hist->size) { 1170 return (FRU_FAILURE); 1171 } 1172 (hist->pairs)[(hist->numStored)].tag.raw_data = tag.raw_data; 1173 (hist->pairs)[(hist->numStored)].inst = 0; 1174 (hist->numStored)++; 1175 } 1176 return (FRU_SUCCESS); 1177 } 1178 1179 static fru_errno_t 1180 get_tag_inst_from_hist(tag_inst_hist_t *hist, fru_tag_t tag, int *instance) 1181 { 1182 int j = 0; 1183 for (j = 0; j < hist->numStored; j++) { 1184 if (tags_equal((hist->pairs)[j].tag, tag)) { 1185 *instance = (hist->pairs)[j].inst; 1186 return (FRU_SUCCESS); 1187 } 1188 } 1189 return (FRU_FAILURE); 1190 } 1191 1192 /* ========================================================================= */ 1193 // Input: 1194 // a list of tags and number of them 1195 // and an instance of the unknown payload you are looking for. 1196 // Returns: 1197 // on FRU_SUCCESS 1198 // instance == the instance of the tag "tag" to read from the list 1199 // else 1200 // instance == the number of instances remaining. 1201 // 1202 static fru_errno_t 1203 find_unknown_element(fru_tag_t *tags, int num_tags, 1204 int *instance, fru_tag_t *tag) 1205 { 1206 fru_errno_t err = FRU_SUCCESS; 1207 1208 tag_inst_hist_t hist; 1209 hist.pairs = (TagInstPair *)alloca(sizeof (TagInstPair) * num_tags); 1210 if (hist.pairs == NULL) { 1211 return (FRU_FAILURE); 1212 } 1213 hist.numStored = 0; 1214 hist.size = num_tags; 1215 1216 // search all the tags untill they are exhausted or we find 1217 // the instance we want. 1218 int found = 0; 1219 int instFound = 0; 1220 // NOTE: instancesFound is a running total of the instances in the tags 1221 // WE SKIPED! 1222 // (ie instances left over == instance - instancesFound) 1223 1224 int i = 0; 1225 for (i = 0; i < num_tags; i++) { 1226 1227 const fru_regdef_t *def = fru_reg_lookup_def_by_tag(tags[i]); 1228 // unknown tag encountered. 1229 if (def == NULL) { 1230 if (update_tag_inst_hist(&hist, tags[i]) 1231 != FRU_SUCCESS) { 1232 return (FRU_FAILURE); 1233 } 1234 // do this check because everything is 0 based. 1235 // if we do the add before the check we will go 1236 // to far. 1237 if ((instFound + 1) > (*instance)) { 1238 found = 1; 1239 break; 1240 } else { 1241 instFound++; 1242 } 1243 } 1244 } 1245 1246 *instance -= instFound; 1247 if (!found) { 1248 return (FRU_DATANOTFOUND); 1249 } 1250 1251 (*tag).raw_data = tags[i].raw_data; 1252 if (get_tag_inst_from_hist(&hist, tags[i], instance) != FRU_SUCCESS) { 1253 return (FRU_FAILURE); 1254 } 1255 1256 return (FRU_SUCCESS); 1257 } 1258 1259 // Input: 1260 // a list of tags and number of them 1261 // a list of Ancestors 1262 // the instance we are looking for 1263 // Returns: 1264 // on FRU_SUCCESS 1265 // instance == the instance of the field within the payload to read. 1266 // correct == pointer into ants which is correct. 1267 // tagInstance == instance of the tag 1268 // else 1269 // instance == the number of instances remaining. 1270 // correct == NULL 1271 // tagInstance == UNDEFINED 1272 // 1273 static fru_errno_t 1274 find_known_element(fru_tag_t *tags, int num_tags, Ancestor *ants, 1275 int *instance, Ancestor **correct, 1276 int *tagInstance) 1277 { 1278 int j = 0; 1279 Ancestor *cur = ants; 1280 int num_posible = 0; 1281 while (cur != NULL) { 1282 num_posible++; 1283 cur = cur->next; 1284 } 1285 1286 tag_inst_hist_t hist; 1287 hist.pairs = (TagInstPair *)alloca(sizeof (TagInstPair) * num_posible); 1288 hist.size = num_posible; 1289 if (hist.pairs == NULL) { 1290 return (FRU_FAILURE); 1291 } 1292 hist.numStored = 0; 1293 1294 *correct = NULL; 1295 int i = 0; 1296 int found = 0; 1297 int instancesFound = 0; 1298 // NOTE: instancesFound is a running total of the instances in the tags 1299 // WE SKIPED! 1300 // (ie instances left over == instance - instancesFound) 1301 for (i = 0; i < num_tags; i++) { 1302 cur = ants; 1303 while (cur != NULL) { 1304 if (tags_equal(cur->getTag(), tags[i])) { 1305 if (update_tag_inst_hist(&hist, tags[i]) 1306 != FRU_SUCCESS) { 1307 return (FRU_FAILURE); 1308 } 1309 1310 // do this check because everything is 0 based. 1311 // if we do the add before the check we will go 1312 // to far. 1313 if ((instancesFound + cur->getNumInstances()) 1314 > (*instance)) { 1315 *correct = cur; 1316 found = 1; 1317 break; /* while loop */ 1318 } 1319 instancesFound += cur->getNumInstances(); 1320 } 1321 cur = cur->next; 1322 } 1323 /* when found break out of both "for" and "while" loops */ 1324 if (found == 1) { 1325 break; /* for loop */ 1326 } 1327 } 1328 1329 *instance -= instancesFound; 1330 if (!found) { 1331 return (FRU_DATANOTFOUND); 1332 } 1333 1334 if (get_tag_inst_from_hist(&hist, tags[i], tagInstance) 1335 != FRU_SUCCESS) { 1336 return (FRU_FAILURE); 1337 } 1338 1339 return (FRU_SUCCESS); 1340 } 1341 1342 /* 1343 * Same as find_known_element but ONLY searches for absolute paths 1344 * (ie PathDef->head == tag) 1345 */ 1346 static fru_errno_t 1347 find_known_element_abs(fru_tag_t *tags, int num_tags, int *instance, 1348 PathDef *head, Ancestor *ants, Ancestor **correct, 1349 int *tagInstance) 1350 { 1351 *correct = NULL; 1352 // find the exact ancestor we want. 1353 Ancestor *cur = ants; 1354 while (cur != NULL) { 1355 if (strcmp(cur->getDef()->name, head->def->name) == 0) { 1356 *correct = cur; 1357 break; 1358 } 1359 cur = cur->next; 1360 } 1361 if (cur == NULL) { 1362 // serious parser bug might cause this, double check. 1363 return (FRU_FAILURE); 1364 } 1365 1366 int found = 0; 1367 (*tagInstance) = 0; 1368 for (int i = 0; i < num_tags; i++) { 1369 if (tags_equal(cur->getTag(), tags[i])) { 1370 // do this check because everything is 0 based. 1371 // if we do the add before the check we will go 1372 // to far. 1373 if (((*tagInstance) +1) > (*instance)) { 1374 *correct = cur; 1375 found = 1; 1376 break; 1377 } 1378 (*tagInstance)++; 1379 } 1380 } 1381 1382 *instance -= (*tagInstance); 1383 if (!found) { 1384 return (FRU_DATANOTFOUND); 1385 } 1386 1387 return (FRU_SUCCESS); 1388 } 1389 1390 1391 /* ========================================================================= */ 1392 // From the container, seg_name, instance, and field_path get me... 1393 // pathDef: A linked list of Path Def objects which represent the 1394 // field_path 1395 // ancestors: A linked list of Tagged Ancestors which represent the 1396 // possible payloads this data MAY reside in. 1397 // correct: A pointer into the above list which indicates the Ancestor 1398 // in which this instance actually resides. 1399 // tagInstance: The instance of this ancestor in the segment. (ie Tag 1400 // instance) 1401 // instWICur: The instance of this element within the tag itself. 1402 // Or in other words "the instances left" 1403 // payload: The payload data 1404 // 1405 // For an "UNKNOWN" payload this will return NULL for the pathDef, ancestors, 1406 // cur pointers. This will indicate to read that this payload should be 1407 // returned with a special definition for it (UNKNOWN)... What a HACK I 1408 // know... 1409 #define READ_MODE 0 1410 #define UPDATE_MODE 1 1411 static fru_errno_t get_payload(fru_nodehdl_t container, 1412 const char *seg_name, 1413 int instance, 1414 const char *field_path, 1415 // returns the following... 1416 PathDef **pathDef, 1417 Ancestor **ancestors, 1418 Ancestor **correct, 1419 int *tagInstance, // instance of the tag within the seg 1420 int *instLeft, // within this payload 1421 uint8_t **payload, 1422 size_t *payloadLen, 1423 int mode) 1424 { 1425 int abs_path_flg = 0; 1426 fru_errno_t err = FRU_SUCCESS; 1427 int num_tags = 0; 1428 fru_tag_t *tags = NULL; 1429 1430 if (data_source == NULL) { 1431 return (FRU_FAILURE); 1432 } 1433 RETRY(err = data_source->get_tag_list(NODEHDL_TO_TREEHDL(container), 1434 seg_name, &tags, &num_tags)) 1435 if (err != FRU_SUCCESS) { 1436 return (err); 1437 } 1438 1439 if (num_tags == 0) { 1440 *instLeft = instance; 1441 return (FRU_DATANOTFOUND); 1442 } 1443 1444 if (IS_UNKNOWN_PATH(field_path)) { 1445 fru_tag_t tagToRead; 1446 1447 *pathDef = NULL; 1448 *correct = *ancestors = NULL; 1449 *tagInstance = 0; 1450 1451 int unknown_inst = instance; 1452 if ((err = find_unknown_element(tags, num_tags, &unknown_inst, 1453 &tagToRead)) != FRU_SUCCESS) { 1454 *instLeft = unknown_inst; 1455 free(tags); 1456 return (err); 1457 } 1458 RETRY(err = 1459 data_source->get_tag_data(NODEHDL_TO_TREEHDL(container), 1460 seg_name, tagToRead, unknown_inst, payload, 1461 payloadLen)) 1462 free(tags); 1463 return (err); 1464 } 1465 1466 err = fru_field_parser(field_path, ancestors, 1467 &abs_path_flg, pathDef); 1468 1469 if (err != FRU_SUCCESS) { 1470 free(tags); 1471 return (err); 1472 } else if (ancestors == NULL) { 1473 /* without valid ancestors we can't find payloads for this */ 1474 free(tags); 1475 delete pathDef; 1476 return (FRU_INVALELEMENT); 1477 } 1478 1479 if ((mode == UPDATE_MODE) && (abs_path_flg != 1)) { 1480 free(tags); 1481 delete *ancestors; // linked list 1482 delete *pathDef; 1483 return (FRU_INVALPATH); 1484 } 1485 1486 if (abs_path_flg == 1) { 1487 if ((err = find_known_element_abs(tags, num_tags, &instance, 1488 *pathDef, *ancestors, correct, tagInstance)) 1489 != FRU_SUCCESS) { 1490 // set up to search next segment for instances left 1491 // over 1492 *instLeft = instance; 1493 free(tags); 1494 delete *ancestors; // linked list 1495 delete *pathDef; 1496 return (err); 1497 } 1498 } else { 1499 if ((err = find_known_element(tags, num_tags, *ancestors, 1500 &instance, correct, tagInstance)) 1501 != FRU_SUCCESS) { 1502 // set up to search next segment for instances left 1503 // over 1504 *instLeft = instance; 1505 free(tags); 1506 delete *ancestors; // linked list 1507 delete *pathDef; 1508 return (err); 1509 } 1510 } 1511 1512 // if we get here this means the instance number within the payload. 1513 *instLeft = instance; 1514 RETRY(err = data_source->get_tag_data(NODEHDL_TO_TREEHDL(container), 1515 seg_name, (*correct)->getTag(), (*tagInstance), payload, 1516 payloadLen)) 1517 free(tags); 1518 if (err != FRU_SUCCESS) { 1519 delete *ancestors; // linked list 1520 delete *pathDef; 1521 } 1522 return (err); 1523 } 1524 1525 /* ========================================================================= */ 1526 /* 1527 * Handle decryption if necessary 1528 */ 1529 static fru_errno_t 1530 do_decryption(fru_nodehdl_t container, const char *seg_name, 1531 uint8_t *payload, size_t payloadLen) 1532 { 1533 fru_errno_t err = FRU_SUCCESS; 1534 if (segment_is_encrypted(container, seg_name)) { 1535 if (fru_encryption_supported() == FRU_SUCCESS) { 1536 if ((err = encrypt_func(FRU_DECRYPT, 1537 payload, payloadLen)) != FRU_SUCCESS) { 1538 return (err); 1539 } 1540 } else { 1541 return (FRU_FAILURE); 1542 } 1543 } 1544 return (FRU_SUCCESS); 1545 } 1546 1547 /* ========================================================================= */ 1548 // Same as get_payload except if seg_name is NULL and it will find the one 1549 // used and return it. 1550 // 1551 static fru_errno_t 1552 get_seg_and_payload(fru_nodehdl_t container, 1553 char **seg_name, 1554 int instance, 1555 const char *field_path, 1556 // returns the following... 1557 PathDef **pathDef, 1558 Ancestor **ancestors, 1559 Ancestor **correct, 1560 int *tagInstance, // within the segment. 1561 int *instLeft, // within this payload 1562 uint8_t **payload, 1563 size_t *payloadLen) 1564 { 1565 fru_errno_t err = FRU_SUCCESS; 1566 if ((err = is_container(container)) != FRU_SUCCESS) { 1567 return (err); 1568 } 1569 1570 if (field_path == NULL) 1571 return (FRU_INVALPATH); 1572 1573 if ((*seg_name) != NULL) { 1574 1575 // always check for valid segment names. 1576 if (strlen((const char *)(*seg_name)) > FRU_SEGNAMELEN) { 1577 return (FRU_INVALSEG); 1578 } 1579 1580 if ((err = get_payload(container, (const char *)(*seg_name), 1581 instance, field_path, pathDef, ancestors, correct, 1582 tagInstance, instLeft, payload, payloadLen, READ_MODE)) 1583 != FRU_SUCCESS) { 1584 return (err); 1585 } 1586 return (do_decryption(container, (const char *)(*seg_name), 1587 *payload, *payloadLen)); 1588 1589 } else { 1590 fru_strlist_t seg_list; 1591 1592 if ((err = get_seg_list_from_ds(container, &seg_list)) 1593 != FRU_SUCCESS) { 1594 return (err); 1595 } 1596 1597 int found = 0; 1598 for (int i = 0; i < seg_list.num; i++) { 1599 err = get_payload(container, 1600 seg_list.strs[i], 1601 instance, field_path, 1602 pathDef, ancestors, correct, 1603 tagInstance, instLeft, 1604 payload, payloadLen, READ_MODE); 1605 if (err == FRU_SUCCESS) { 1606 (*seg_name) = strdup(seg_list.strs[i]); 1607 fru_destroy_strlist(&seg_list); 1608 return (do_decryption(container, 1609 (const char *)(*seg_name), 1610 *payload, *payloadLen)); 1611 } else if (err == FRU_DATANOTFOUND) { 1612 // we may have found some instances or none at 1613 // all but not enough all together. search 1614 // again with the # of instances left. 1615 instance = *instLeft; 1616 } else { 1617 fru_destroy_strlist(&seg_list); 1618 return (err); 1619 } 1620 } 1621 fru_destroy_strlist(&seg_list); 1622 return (FRU_DATANOTFOUND); 1623 } 1624 } 1625 1626 /* ========================================================================= */ 1627 fru_errno_t 1628 fru_read_field(fru_nodehdl_t container, 1629 char **seg_name, unsigned int instance, 1630 const char *field_path, 1631 void **data, size_t *data_len, 1632 char **found_path) 1633 { 1634 fru_errno_t err = FRU_SUCCESS; 1635 // just init this value for the user 1636 *data = NULL; 1637 *data_len = 0; 1638 1639 if (lock_container(READ_LOCK, container) != FRU_SUCCESS) { 1640 return (FRU_FAILURE); 1641 } 1642 PathDef *pathDef; 1643 Ancestor *ancestors; 1644 Ancestor *correctAnt; 1645 int tagInstance = 0; 1646 int instWIPayload = 0; 1647 uint8_t *payload; 1648 size_t payloadLen = 0; 1649 err = get_seg_and_payload(container, seg_name, instance, field_path, 1650 &pathDef, &ancestors, &correctAnt, &tagInstance, 1651 &instWIPayload, &payload, &payloadLen); 1652 1653 CHK_UNLOCK_CONTAINER(container); 1654 1655 if (err != FRU_SUCCESS) { 1656 return (err); 1657 } 1658 1659 if (pathDef == NULL) { // SPECIAL CASE of UNKNOW payload. 1660 delete ancestors; 1661 delete pathDef; 1662 free(payload); 1663 1664 *data = (void *)malloc(payloadLen); 1665 if ((*data) == NULL) { 1666 return (FRU_FAILURE); 1667 } 1668 memcpy(*data, payload, payloadLen); 1669 *data_len = payloadLen; 1670 if (found_path != NULL) { 1671 *found_path = strdup(UNKNOWN_PATH); 1672 } 1673 return (FRU_SUCCESS); 1674 } 1675 1676 // get the specific data 1677 err = PayloadReader::readData(pathDef, correctAnt, 1678 instWIPayload, 1679 payload, payloadLen, 1680 data, data_len); 1681 delete pathDef; 1682 free(payload); 1683 1684 if (err == FRU_SUCCESS) { 1685 if (found_path != NULL) { 1686 *found_path = (char *)malloc( 1687 strlen(correctAnt->getPath(instWIPayload)) 1688 + strlen(field_path) + 2); 1689 if ((*found_path) == NULL) { 1690 delete ancestors; 1691 return (FRU_FAILURE); 1692 } 1693 sprintf(*found_path, "%s%s", 1694 correctAnt->getPath(instWIPayload), 1695 field_path); 1696 } 1697 } 1698 1699 delete ancestors; 1700 return (err); 1701 } 1702 1703 /* ========================================================================= */ 1704 fru_errno_t 1705 fru_update_field(fru_nodehdl_t container, 1706 char *seg_name, unsigned int instance, 1707 const char *field_path, 1708 void *data, size_t length) 1709 { 1710 fru_errno_t err = FRU_SUCCESS; 1711 1712 if ((field_path == NULL) || IS_UNKNOWN_PATH(field_path)) { 1713 return (FRU_INVALPATH); 1714 } else if (seg_name == NULL) { 1715 return (FRU_INVALSEG); 1716 } 1717 1718 if (data_source == NULL) { 1719 return (FRU_FAILURE); 1720 } 1721 1722 if (lock_container(WRITE_LOCK, container) != FRU_SUCCESS) { 1723 return (FRU_FAILURE); 1724 } 1725 PathDef *pathDef; 1726 Ancestor *ancestors; 1727 Ancestor *correctAnt; 1728 int tagInstance = 0; 1729 int instWIPayload = 0; 1730 uint8_t *payload; 1731 size_t payloadLen = 0; 1732 err = get_payload(container, seg_name, instance, field_path, 1733 &pathDef, &ancestors, &correctAnt, &tagInstance, 1734 &instWIPayload, &payload, &payloadLen, UPDATE_MODE); 1735 1736 if (err != FRU_SUCCESS) { 1737 CHK_UNLOCK_CONTAINER(container); 1738 return (err); 1739 } 1740 1741 if ((err = do_decryption(container, (const char *)seg_name, 1742 payload, payloadLen)) != FRU_SUCCESS) { 1743 free(payload); 1744 return (err); 1745 } 1746 1747 // fill in the new data in the payload 1748 err = PayloadReader::updateData(pathDef, correctAnt, instWIPayload, 1749 payload, payloadLen, 1750 data, length); 1751 1752 if (err != FRU_SUCCESS) { 1753 CHK_UNLOCK_CONTAINER(container); 1754 delete ancestors; // linked list. 1755 delete pathDef; 1756 free(payload); 1757 return (err); 1758 } 1759 1760 if ((segment_is_encrypted(container, seg_name)) && 1761 (fru_encryption_supported() == FRU_SUCCESS)) { 1762 if ((err = encrypt_func(FRU_ENCRYPT, payload, payloadLen)) 1763 != FRU_SUCCESS) { 1764 CHK_UNLOCK_CONTAINER(container); 1765 delete ancestors; // linked list. 1766 delete pathDef; 1767 free(payload); 1768 return (err); 1769 } 1770 } 1771 1772 RETRY(err = data_source->set_tag_data(NODEHDL_TO_TREEHDL(container), 1773 seg_name, correctAnt->getTag(), 1774 tagInstance, payload, payloadLen)) 1775 CHK_UNLOCK_CONTAINER(container); 1776 delete ancestors; // linked list. 1777 free(payload); 1778 delete pathDef; 1779 return (err); 1780 } 1781 1782 /* ========================================================================= */ 1783 fru_errno_t 1784 fru_get_num_iterations(fru_nodehdl_t container, 1785 char **seg_name, 1786 unsigned int instance, 1787 const char *iter_path, 1788 int *num_there, 1789 char **found_path) 1790 { 1791 // this ensures a more descriptive error message. 1792 fru_errno_t err = FRU_SUCCESS; 1793 1794 if (lock_container(READ_LOCK, container) != FRU_SUCCESS) { 1795 return (FRU_FAILURE); 1796 } 1797 PathDef *pathDef; 1798 Ancestor *ancestors; 1799 Ancestor *correctAnt; 1800 int tagInstance = 0; 1801 int instWIPayload = 0; 1802 uint8_t *payload; 1803 size_t payloadLen = 0; 1804 err = get_seg_and_payload(container, seg_name, instance, iter_path, 1805 &pathDef, &ancestors, &correctAnt, &tagInstance, 1806 &instWIPayload, &payload, &payloadLen); 1807 1808 CHK_UNLOCK_CONTAINER(container); 1809 1810 if (err != FRU_SUCCESS) { 1811 return (err); 1812 } 1813 1814 if (pathDef == NULL) { // SPECIAL CASE of UNKNOW payload. 1815 // clean up memory from called functions. 1816 err = FRU_INVALPATH; 1817 } else { 1818 // get the specific data 1819 err = PayloadReader::findIterThere(pathDef, correctAnt, 1820 instWIPayload, 1821 payload, payloadLen, 1822 num_there); 1823 } 1824 1825 delete pathDef; 1826 free(payload); 1827 1828 if (err == FRU_SUCCESS) { 1829 if (found_path != NULL) { 1830 *found_path = (char *)malloc( 1831 strlen(correctAnt->getPath(instWIPayload)) 1832 + strlen(iter_path) + 2); 1833 if ((*found_path) == NULL) { 1834 delete ancestors; 1835 return (FRU_FAILURE); 1836 } 1837 sprintf(*found_path, "%s%s", 1838 correctAnt->getPath(instWIPayload), 1839 iter_path); 1840 } 1841 } 1842 1843 delete ancestors; 1844 return (err); 1845 } 1846 1847 /* ========================================================================= */ 1848 // When adding a new payload with 0 data the iteration control bytes must be 1849 // filled in with the number possible. 1850 fru_errno_t 1851 fill_in_iteration_control_bytes(uint8_t *data, 1852 const fru_regdef_t *def, 1853 int inIteration) 1854 { 1855 fru_errno_t rc = FRU_SUCCESS; 1856 1857 if ((def->iterationType == FRU_NOT_ITERATED) || 1858 (inIteration)) { 1859 1860 if (def->dataType == FDTYPE_Record) { 1861 1862 int offset = 0; 1863 for (int i = 0; i < def->enumCount; i++) { 1864 const fru_regdef_t *newDef 1865 = fru_reg_lookup_def_by_name((char *)def->enumTable[i].text); 1866 fru_errno_t rc2 1867 = fill_in_iteration_control_bytes(&(data[offset]), newDef, 0); 1868 if (rc2 != FRU_SUCCESS) 1869 return (rc2); 1870 offset += newDef->payloadLen; 1871 } 1872 1873 } // else field, no sub elements; do nothing... ;-) 1874 1875 } else { 1876 data[3] = (char)def->iterationCount; 1877 1878 int offset = 3; 1879 for (int i = 0; i < def->iterationCount; i++) { 1880 fru_errno_t rc3 1881 = fill_in_iteration_control_bytes(&(data[offset]), def, 1); 1882 if (rc3 != FRU_SUCCESS) 1883 return (rc3); 1884 offset += ((def->payloadLen - 4)/(def->iterationCount)); 1885 } 1886 } 1887 1888 return (rc); 1889 } 1890 1891 /* ========================================================================= */ 1892 fru_errno_t 1893 fru_add_element(fru_nodehdl_t container, 1894 const char *seg_name, 1895 const char *element) 1896 { 1897 fru_errno_t err = FRU_SUCCESS; 1898 1899 if ((seg_name == NULL) || (strlen(seg_name) > FRU_SEGNAMELEN)) { 1900 return (FRU_INVALSEG); 1901 } 1902 1903 const fru_regdef_t *def 1904 = fru_reg_lookup_def_by_name((char *)element); 1905 if (def == NULL) { 1906 return (FRU_NOREGDEF); 1907 } 1908 if (def->tagType == FRU_X) { 1909 return (FRU_ELEMNOTTAGGED); 1910 } 1911 1912 if (data_source == NULL) { 1913 return (FRU_FAILURE); 1914 } 1915 1916 if ((err = is_container(container)) != FRU_SUCCESS) { 1917 return (err); 1918 } 1919 1920 if (lock_container(WRITE_LOCK, container) != FRU_SUCCESS) { 1921 return (FRU_FAILURE); 1922 } 1923 1924 fru_tag_t tag; 1925 mk_tag(def->tagType, def->tagDense, def->payloadLen, &tag); 1926 uint8_t *data = new uint8_t[def->payloadLen]; 1927 memset(data, 0x00, def->payloadLen); 1928 1929 err = fill_in_iteration_control_bytes(data, def, 0); 1930 if (err != FRU_SUCCESS) { 1931 CHK_UNLOCK_CONTAINER(container); 1932 delete[] data; 1933 return (err); 1934 } 1935 1936 if (segment_is_encrypted(container, seg_name)) { 1937 if (fru_encryption_supported() == FRU_NOTSUP) { 1938 CHK_UNLOCK_CONTAINER(container); 1939 delete[] data; 1940 return (FRU_INVALSEG); 1941 } 1942 if ((err = encrypt_func(FRU_ENCRYPT, data, 1943 def->payloadLen)) != FRU_SUCCESS) { 1944 CHK_UNLOCK_CONTAINER(container); 1945 delete[] data; 1946 return (err); 1947 } 1948 } 1949 1950 RETRY(err = data_source->add_tag_to_seg(NODEHDL_TO_TREEHDL(container), 1951 seg_name, tag, data, def->payloadLen)) 1952 CHK_UNLOCK_CONTAINER(container); 1953 delete[] data; 1954 return (err); 1955 } 1956 1957 /* ========================================================================= */ 1958 fru_errno_t 1959 fru_delete_element(fru_nodehdl_t container, 1960 const char *seg_name, 1961 unsigned int instance, 1962 const char *element) 1963 { 1964 fru_errno_t err = FRU_SUCCESS; 1965 1966 if ((seg_name == NULL) || (strlen(seg_name) > FRU_SEGNAMELEN)) { 1967 return (FRU_INVALSEG); 1968 } 1969 1970 if (data_source == NULL) { 1971 return (FRU_FAILURE); 1972 } 1973 1974 if ((err = is_container(container)) != FRU_SUCCESS) { 1975 return (err); 1976 } 1977 1978 if (lock_container(WRITE_LOCK, container) != FRU_SUCCESS) { 1979 return (FRU_FAILURE); 1980 } 1981 if ((segment_is_encrypted(container, seg_name)) && 1982 (fru_encryption_supported() == FRU_NOTSUP)) { 1983 CHK_UNLOCK_CONTAINER(container); 1984 return (FRU_INVALSEG); 1985 } 1986 1987 fru_tag_t tag; 1988 int localInst = instance; 1989 // again the special case of UNKNOWN. This allows us to delete these 1990 // elements if they are somehow not wanted. 1991 // NOTE: "/UNKNOWN" is not supported just as "/ManR" would not be valid 1992 // either. Both of these will result in returning FRU_NOREGDEF 1993 if (strcmp(element, "UNKNOWN") == 0) { 1994 fru_tag_t *tags = NULL; 1995 int num_tags = 0; 1996 1997 RETRY(err = 1998 data_source->get_tag_list(NODEHDL_TO_TREEHDL(container), 1999 seg_name, &tags, &num_tags)) 2000 2001 if (err != FRU_SUCCESS) { 2002 CHK_UNLOCK_CONTAINER(container); 2003 return (err); 2004 } 2005 if ((err = find_unknown_element(tags, num_tags, 2006 &localInst, &tag)) != FRU_SUCCESS) { 2007 free(tags); 2008 CHK_UNLOCK_CONTAINER(container); 2009 return (err); 2010 } 2011 free(tags); 2012 } else { 2013 const fru_regdef_t *def 2014 = fru_reg_lookup_def_by_name((char *)element); 2015 if (def == NULL) { 2016 CHK_UNLOCK_CONTAINER(container); 2017 return (FRU_NOREGDEF); 2018 } 2019 if (def->tagType == FRU_X) { 2020 CHK_UNLOCK_CONTAINER(container); 2021 return (FRU_ELEMNOTTAGGED); 2022 } 2023 mk_tag(def->tagType, def->tagDense, def->payloadLen, &tag); 2024 } 2025 2026 RETRY(err = data_source->delete_tag(NODEHDL_TO_TREEHDL(container), 2027 seg_name, tag, instance)) 2028 CHK_UNLOCK_CONTAINER(container); 2029 return (err); 2030 } 2031 2032 /* General library support */ 2033 /* ========================================================================= */ 2034 static fru_errno_t 2035 make_definition(const fru_regdef_t *def, fru_elemdef_t *definition) 2036 { 2037 definition->version = FRU_ELEMDEF_REV; 2038 definition->data_type = def->dataType; 2039 if (def->tagType != FRU_X) 2040 definition->tagged = FRU_Yes; 2041 else 2042 definition->tagged = FRU_No; 2043 2044 // zzz 2045 // This should be the following statement. 2046 // (*definition)->data_length = def->dataLength; 2047 // instead of. 2048 if (def->iterationType != FRU_NOT_ITERATED) { 2049 int elemLen = ((def->dataLength-4)/def->iterationCount); 2050 definition->data_length = elemLen; 2051 } else { 2052 definition->data_length = def->dataLength; 2053 } 2054 // END zzz 2055 2056 definition->disp_type = def->dispType; 2057 definition->purgeable = def->purgeable; 2058 definition->relocatable = def->relocatable; 2059 2060 definition->enum_count = 0; 2061 definition->enum_table = NULL; 2062 2063 unsigned int count = def->enumCount; 2064 if (count != 0) { 2065 definition->enum_table = (fru_enum_t *)malloc( 2066 (sizeof (fru_enum_t)) * count); 2067 if ((definition->enum_table) == NULL) { 2068 return (FRU_FAILURE); 2069 } 2070 memset(definition->enum_table, 0x00, 2071 ((sizeof (fru_enum_t)) * count)); 2072 } 2073 2074 for (int i = 0; i < count; i++) { 2075 definition->enum_table[i].value = def->enumTable[i].value; 2076 definition->enum_table[i].text = strdup(def->enumTable[i].text); 2077 if ((definition->enum_table[i].text) == NULL) { 2078 fru_destroy_elemdef(definition); 2079 return (FRU_FAILURE); 2080 } 2081 (definition->enum_count)++; 2082 } 2083 2084 definition->iteration_count = def->iterationCount; 2085 definition->iteration_type = def->iterationType; 2086 2087 definition->example_string = strdup(def->exampleString); 2088 if ((definition->example_string) == NULL) { 2089 fru_destroy_elemdef(definition); 2090 return (FRU_FAILURE); 2091 } 2092 2093 return (FRU_SUCCESS); 2094 } 2095 2096 /* ========================================================================= */ 2097 fru_errno_t 2098 fru_get_definition(const char *element_name, 2099 fru_elemdef_t *definition) 2100 { 2101 // find the last one in the string... 2102 int abs_path_flg = 0; 2103 Ancestor *ancestors = NULL; 2104 PathDef *pathDef = NULL; 2105 fru_errno_t err = FRU_SUCCESS; 2106 2107 err = fru_field_parser(element_name, &ancestors, 2108 &abs_path_flg, &pathDef); 2109 if (err != FRU_SUCCESS) { 2110 return (err); 2111 } 2112 2113 PathDef *last = pathDef; 2114 while (last->next != NULL) 2115 last = last->next; 2116 2117 err = make_definition(last->def, definition); 2118 2119 delete ancestors; 2120 delete pathDef; 2121 return (err); 2122 } 2123 2124 /* ========================================================================= */ 2125 fru_errno_t 2126 fru_get_registry(fru_strlist_t *list) 2127 { 2128 fru_errno_t err = FRU_SUCCESS; 2129 unsigned int number = 0; 2130 char **entries = fru_reg_list_entries(&number); 2131 if (entries == NULL) { 2132 return (FRU_FAILURE); 2133 } 2134 list->strs = entries; 2135 list->num = number; 2136 return (FRU_SUCCESS); 2137 } 2138 2139 /* ========================================================================= */ 2140 fru_errno_t 2141 fru_get_tagged_parents(const char *element, fru_strlist_t *parents) 2142 { 2143 Ancestor *ancestors 2144 = Ancestor::listTaggedAncestors((char *)element); 2145 2146 Ancestor *cur = ancestors; 2147 /* count them */ 2148 int number = 0; 2149 while (cur != NULL) { 2150 number++; 2151 cur = cur->next; 2152 } 2153 2154 parents->num = 0; 2155 parents->strs = NULL; 2156 if (number == 0) { 2157 return (FRU_SUCCESS); 2158 } 2159 parents->strs = (char **)malloc(number * sizeof (char *)); 2160 if (parents->strs == NULL) { 2161 return (FRU_FAILURE); 2162 } 2163 memset(parents->strs, 0x00, (number * sizeof (char *))); 2164 2165 cur = ancestors; 2166 for (int i = 0; i < number; i++) { 2167 if (cur == NULL) { 2168 fru_destroy_strlist(parents); 2169 return (FRU_FAILURE); 2170 } 2171 parents->strs[i] = strdup(cur->getDef()->name); 2172 if (parents->strs[i] == NULL) { 2173 fru_destroy_strlist(parents); 2174 return (FRU_FAILURE); 2175 } 2176 parents->num++; 2177 cur = cur->next; 2178 } 2179 2180 return (FRU_SUCCESS); 2181 } 2182 2183 /* 2184 * Enum string converters. 2185 */ 2186 /* ========================================================================= */ 2187 const char * 2188 get_displaytype_str(fru_displaytype_t e) 2189 { 2190 switch (e) { 2191 case FDISP_Binary: 2192 return (gettext("Binary")); 2193 case FDISP_Hex: 2194 return (gettext("Hex")); 2195 case FDISP_Decimal: 2196 return (gettext("Decimal")); 2197 case FDISP_Octal: 2198 return (gettext("Octal")); 2199 case FDISP_String: 2200 return (gettext("String")); 2201 case FDISP_Time: 2202 return (gettext("Time")); 2203 case FDISP_UNDEFINED: 2204 return (gettext("UNDEFINED")); 2205 } 2206 return (gettext("UNDEFINED")); 2207 } 2208 2209 /* ========================================================================= */ 2210 const char * 2211 get_datatype_str(fru_datatype_t e) 2212 { 2213 switch (e) { 2214 case FDTYPE_Binary: 2215 return (gettext("Binary")); 2216 case FDTYPE_ByteArray: 2217 return (gettext("Byte Array")); 2218 case FDTYPE_ASCII: 2219 return (gettext("ASCII")); 2220 case FDTYPE_Unicode: 2221 return (gettext("Unicode")); 2222 case FDTYPE_Record: 2223 return (gettext("Record")); 2224 case FDTYPE_Enumeration: 2225 return (gettext("Enumeration")); 2226 case FDTYPE_UNDEFINED: 2227 return (gettext("UNDEFINED")); 2228 } 2229 return (gettext("UNDEFINED")); 2230 } 2231 /* ========================================================================= */ 2232 const char * 2233 get_which_str(fru_which_t e) 2234 { 2235 switch (e) { 2236 case FRU_No: 2237 return (gettext("No")); 2238 case FRU_Yes: 2239 return (gettext("Yes")); 2240 case FRU_WHICH_UNDEFINED: 2241 return (gettext("WHICH UNDEFINED")); 2242 } 2243 return (gettext("WHICH UNDEFINED")); 2244 } 2245 /* ========================================================================= */ 2246 const char * 2247 get_itertype_str(fru_itertype_t e) 2248 { 2249 switch (e) { 2250 case FRU_FIFO: 2251 return (gettext("FIFO")); 2252 case FRU_Circular: 2253 return (gettext("Circular")); 2254 case FRU_Linear: 2255 return (gettext("Linear")); 2256 case FRU_LIFO: 2257 return (gettext("LIFO")); 2258 case FRU_NOT_ITERATED: 2259 return (gettext("NOT ITERATED")); 2260 } 2261 return (gettext("NOT ITERATED")); 2262 } 2263