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