/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2014 Gary Mills * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright (c) 2018, Joyent, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #define NUM_OF_SEGMENT 1 #define SEGMENT_NAME_SIZE 2 #define FD_SEGMENT_SIZE 2949 static char *command, *customer_data = NULL, *frupath = NULL, **svcargv; /* DataElement supported in the customer operation */ static char *cust_data_list[] = {"Customer_DataR"}; /* DataElement supported in the service operation */ static char *serv_data_list[] = {"InstallationR", "ECO_CurrentR"}; /* currently supported segment name */ static char *segment_name[] = {"FD"}; static int found_frupath = 0, list_only = 0, recursive = 0, service_mode = 0, svcargc, update = 0; static void usage(void) { (void) fprintf(stderr, gettext("Usage: %s [ -l ] | [ [ -r ] frupath [ text ] ]\n"), command); } static int validate_fieldnames(int argc, char *argv[]) { static int num = sizeof (serv_data_list)/sizeof (*serv_data_list); char *fieldname; int i, j, match, status; fru_elemdef_t definition; for (i = 0; i < argc; i += 2) { if (argv[i][0] == '/') { fieldname = &argv[i][1]; } else { fieldname = &argv[i][0]; } match = 0; for (j = 0; j < num; j++) { if (strncmp(fieldname, serv_data_list[j], strlen(serv_data_list[j])) == 0) { match = 1; } } if (!match) { (void) fprintf(stderr, gettext("\"%s\" is not a supported field\n"), argv[i]); return (1); } if ((status = fru_get_definition(argv[i], &definition)) != FRU_SUCCESS) { (void) fprintf(stderr, gettext("\"%s\": %s\n"), argv[i], fru_strerror(status)); return (1); } else if ((definition.data_type == FDTYPE_Record) || (definition.data_type == FDTYPE_UNDEFINED)) { (void) fprintf(stderr, gettext("\"%s\" is not a field\n"), argv[i]); return (1); } } return (0); } static int pathmatch(const char *path) { char *match; if ((frupath != NULL) && ((match = strstr(path, frupath)) != NULL) && ((match + strlen(frupath)) == (path + strlen(path))) && ((match == path) || (*(match - 1) == '/'))) { found_frupath = 1; return (1); } return (0); } static void displayBinary(unsigned char *data, size_t length, fru_elemdef_t *def) { int i = 0; uint64_t lldata; uint64_t mask; if (def->disp_type == FDISP_Hex) { for (i = 0; i < length; i++) { (void) printf("%02X", data[i]); } return; } (void) memcpy(&lldata, data, sizeof (lldata)); switch (def->disp_type) { case FDISP_Binary: { mask = 0x8000000000000000ULL; for (i = 0; i < (sizeof (uint64_t) *8); i++) { if (lldata & (mask >> i)) { (void) printf("1"); } else { (void) printf("0"); } } return; } case FDISP_Octal: { (void) printf("%llo", lldata); return; } case FDISP_Decimal: { (void) printf("%lld", lldata); return; } case FDISP_Time: { char buffer[PATH_MAX]; time_t time; time = (time_t)lldata; (void) strftime(buffer, PATH_MAX, "%+", localtime(&time)); (void) printf("%s", buffer); return; } } } static void displayBAasBinary(unsigned char *data, size_t length) { int i; unsigned char mask; for (i = 0; i < length; i++) { /* * make a mask for the high order bit and adjust down through * all the bits. */ for (mask = 0x80; mask > 0; mask /= 2) { if ((data[i] & mask) != 0) /* bit must be on */ (void) printf("1"); else /* bit is off... */ (void) printf("0"); } } (void) printf("\n"); } static void display_data(unsigned char *data, size_t length, fru_elemdef_t *def) { int i = 0; uint64_t lldata; if (data == 0x00) { (void) printf("\n"); return; } switch (def->data_type) { case FDTYPE_Binary: { displayBinary(data, length, def); return; } case FDTYPE_ByteArray: { switch (def->disp_type) { case FDISP_Binary: displayBAasBinary(data, length); return; case FDISP_Hex: for (i = 0; i < length; i++) { (void) printf("%02X", data[i]); } return; } return; } case FDTYPE_Unicode: assert(gettext("Unicode not yet supported") == 0); break; case FDTYPE_ASCII: { char *disp_str = (char *)alloca(length+1); for (i = 0; i < length; i++) disp_str[i] = data[i]; disp_str[i] = '\0'; (void) printf("%s", disp_str); return; } case FDTYPE_Enumeration: { lldata = strtoull((const char *)data, NULL, 0); for (i = 0; i < def->enum_count; i++) { if (def->enum_table[i].value == lldata) { /* strdup such that map_... can realloc if necessary. */ char *tmp = strdup(def->enum_table[i].text); (void) printf("%s", tmp); free(tmp); return; } } (void) printf(gettext("Unrecognized Value: 0x")); for (i = 0; i < sizeof (uint64_t); i++) (void) printf("%02X", data[i]); break; } default: break; } } static void print_node_data(fru_nodehdl_t cont_hdl) { int iter_cnt = 0; int iter; int numseg; int list_cnt; unsigned char *data; size_t dataLen; int total_cnt; char *found_path = NULL; fru_elemdef_t def, def1; int instance = 0; char **ptr; char **tmp_ptr; int count = 0; char elem_name[PATH_MAX]; if (service_mode) { total_cnt = sizeof (serv_data_list)/sizeof (*serv_data_list); ptr = serv_data_list; } else { total_cnt = sizeof (cust_data_list)/sizeof (*cust_data_list); ptr = cust_data_list; } tmp_ptr = ptr; for (numseg = 0; numseg < NUM_OF_SEGMENT; numseg++) { ptr = tmp_ptr; for (list_cnt = 0; list_cnt < total_cnt; list_cnt++) { if ((fru_get_definition(*ptr, &def)) != FRU_SUCCESS) { continue; } if ((fru_get_num_iterations(cont_hdl, &segment_name[numseg], 0, *ptr, &iter_cnt, NULL)) != FRU_SUCCESS) { iter_cnt = 0; } iter = 0; do { for (count = 0; count < def.enum_count; count++) { if (def.iteration_type != FRU_NOT_ITERATED) { (void) snprintf(elem_name, sizeof (elem_name), "/%s[%d]/%s", *ptr, iter, def.enum_table[count].text); } else { (void) snprintf(elem_name, sizeof (elem_name), "/%s/%s", *ptr, def.enum_table[count].text); } if ((fru_read_field(cont_hdl, &segment_name[numseg], instance, elem_name, (void**)&data, &dataLen, &found_path)) != FRU_SUCCESS) { break; } if ((fru_get_definition( def.enum_table[count].text, &def1)) != FRU_SUCCESS) { break; } (void) printf(" %s: ",\ elem_name); display_data(data, dataLen, &def1); (void) printf("\n"); } iter ++; } while (iter < iter_cnt); ptr++; } } } static char * convertBinaryToDecimal(char *ptr) { int cnt = 0; char *data; int str_len; char *ret = NULL; uint64_t result = 0; str_len = strlen(ptr); data = ptr; while (str_len >= 1) { str_len -= 1; if (data[str_len] == '0') { result += (0 * pow(2, cnt)); } if (data[str_len] == '1') { result += (1 * pow(2, cnt)); } cnt++; } ret = (char *)lltostr(result, "\n"); return (ret); } /* * called update_field() to update the field with specific field value. * nodehdl represents the fru, segment represents the segment name in the fru. * field_name represents the field to be updated with the value field_value. */ static int convert_update(fru_nodehdl_t nodehdl, char *segment, char *field_name, char *field_value) { uint64_t num = 0; fru_elemdef_t def; fru_errno_t err; void *data = NULL; size_t dataLen = 0; int i; if ((err = fru_get_definition(field_name, &def)) != FRU_SUCCESS) { (void) fprintf(stderr, gettext("Failed to get definition %s: %s\n"), field_name, fru_strerror(err)); return (1); } if (field_value == NULL) { return (1); } switch (def.data_type) { case FDTYPE_Binary: if (def.disp_type != FDISP_Time) { if (field_value[0] == 'b') { field_value = convertBinaryToDecimal((field_value +1)); } num = strtoll(field_value, (char **)NULL, 0); if ((num == 0) && (errno == 0)) { return (1); } data = (void*)# dataLen = sizeof (uint64_t); } break; case FDTYPE_ByteArray: return (1); case FDTYPE_Unicode: return (1); case FDTYPE_ASCII: data = (void *) field_value; dataLen = strlen(field_value); if (dataLen < def.data_length) { dataLen++; } break; case FDTYPE_Enumeration: for (i = 0; i < def.enum_count; i++) { if (strcmp(def.enum_table[i].text, field_value) == 0) { data = (void *)(uintptr_t) def.enum_table[i].value; dataLen = sizeof (uint64_t); break; } } return (1); case FDTYPE_Record: if (def.iteration_count == 0) { return (1); } data = NULL; dataLen = 0; break; case FDTYPE_UNDEFINED: return (1); } if ((err = fru_update_field(nodehdl, segment, 0, field_name, data, dataLen)) != FRU_SUCCESS) { (void) fprintf(stderr, gettext("fru_update_field(): %s\n"), fru_strerror(err)); return (1); } return (0); } /* * called by update_field() when a new data element is created. * it updates the UNIX_Timestamp32 field with the current system time. */ static int update_unixtimestamp(fru_nodehdl_t nodehdl, char *segment, char **ptr) { char *field_name; time_t clock; struct tm *sp_tm; fru_errno_t err = FRU_SUCCESS; uint64_t time_data; size_t len; len = strlen(*ptr) + strlen("UNIX_Timestamp32") + 3; field_name = alloca(len); (void) snprintf(field_name, len, "/%s/UNIX_Timestamp32", *ptr); clock = time(NULL); sp_tm = localtime(&clock); time_data = (uint64_t)mktime(sp_tm); if ((err = fru_update_field(nodehdl, segment, 0, field_name, (void *)&time_data, sizeof (time_data))) != FRU_SUCCESS) { (void) fprintf(stderr, gettext("fru_update_field(): %s\n"), fru_strerror(err)); return (1); } return (0); } /* * create segment on the specified fru represented by nodehdl. */ static int create_segment(fru_nodehdl_t nodehdl) { fru_segdesc_t seg_desc; fru_segdef_t def; int cnt; int status; (void) memset(&seg_desc, 0, sizeof (seg_desc)); seg_desc.field.field_perm = 0x6; seg_desc.field.operations_perm = 0x6; seg_desc.field.engineering_perm = 0x6; seg_desc.field.repair_perm = 0x6; (void) memset(&def, 0, sizeof (def)); def.address = 0; def.desc.raw_data = seg_desc.raw_data; def.hw_desc.all_bits = 0; for (cnt = 0; cnt < NUM_OF_SEGMENT; cnt++) { (void) strncpy(def.name, segment_name[cnt], SEGMENT_NAME_SIZE); if (cnt == 0) { def.size = FD_SEGMENT_SIZE; } if ((status = fru_create_segment(nodehdl, &def)) != FRU_SUCCESS) { continue; } return (cnt); } if (status != FRU_SUCCESS) (void) fprintf(stderr, gettext("fru_create_segment(): %s\n"), fru_strerror(status)); return (1); } /* * called from update_field() when service flag is ON. currently * supported iterated record is InstallationR and fields supported for * update are Geo_North, Geo_East, Geo_Alt, Geo_Location. */ static int updateiter_record(fru_nodehdl_t nodehdl, int cnt, char **ptr, char *field_name, char *field_value) { int iter_cnt = 0; char rec_name[512]; void *data = NULL; char *tmpptr = NULL; size_t dataLen = 0; char **elem_ptr; int found = 0; int index; int total_cnt; fru_errno_t err; static char *elem_list[] = {"/Geo_North", "/Geo_East",\ "/Geo_Alt", "/Geo_Location"}; elem_ptr = elem_list; total_cnt = sizeof (elem_list)/sizeof (*elem_list); for (index = 0; index < total_cnt; index++) { tmpptr = strrchr(field_name, '/'); if (tmpptr == NULL) { (void) fprintf(stderr, gettext("Error: Data Element not known\n")); return (1); } if ((strncmp(*elem_ptr, tmpptr, strlen(*elem_ptr)) != 0)) { elem_ptr++; continue; } found = 1; break; } if (found == 0) { (void) fprintf(stderr, gettext("Error: Update not allowed for field: %s\n"), field_name); return (1); } if ((fru_get_num_iterations(nodehdl, &segment_name[cnt], 0, *ptr, &iter_cnt, NULL)) != FRU_SUCCESS) { return (1); } /* add a new Iterated Record if complete path is not given */ if (iter_cnt == 0) { (void) snprintf(rec_name, sizeof (rec_name), "/%s[+]", *ptr); if ((err = fru_update_field(nodehdl, segment_name[cnt], 0, rec_name, data, dataLen)) != FRU_SUCCESS) { (void) fprintf(stderr, gettext("fru_update_field(): %s\n"), fru_strerror(err)); return (1); } iter_cnt = 1; } (void) snprintf(rec_name, sizeof (rec_name), "/%s[%d]%s", *ptr, iter_cnt-1, strrchr(field_name, '/')); if ((convert_update(nodehdl, segment_name[cnt], rec_name, field_value)) != 0) { return (1); } /* update success now update the unix timestamp */ (void) snprintf(rec_name, sizeof (rec_name), "/%s[%d]", *ptr, iter_cnt-1); tmpptr = rec_name; /* update UNIX_Timestamp32 with creation time */ if ((update_unixtimestamp(nodehdl, segment_name[cnt], &tmpptr)) != 0) { return (1); } return (0); } static int update_field(fru_nodehdl_t nodehdl, char *field_name, char *field_value) { fru_elemdef_t def; unsigned char *data; size_t dataLen; char *found_path = NULL; int cnt; char **ptr; fru_strlist_t elem; int elem_cnt; int add_flag = 1; int total_cnt; int status; if (service_mode) { ptr = serv_data_list; total_cnt = sizeof (serv_data_list)/sizeof (*serv_data_list); for (cnt = 0; cnt < total_cnt; cnt++) { if ((strncmp(*ptr, &field_name[1], strlen(*ptr)) \ != 0) && (strncmp(*ptr, &field_name[0], strlen(*ptr)) != 0)) { ptr++; add_flag = 0; continue; } add_flag = 1; break; } } else { ptr = cust_data_list; } /* look for the field in either of the segment if found update it */ for (cnt = 0; cnt < NUM_OF_SEGMENT; cnt++) { if ((fru_read_field(nodehdl, &segment_name[cnt], 0, field_name, (void **) &data, &dataLen, &found_path)) != FRU_SUCCESS) { continue; } if ((fru_get_definition(*ptr, &def)) == FRU_SUCCESS) { if (def.iteration_count != 0) { if ((updateiter_record(nodehdl, cnt, ptr, field_name, field_value)) != 0) { return (1); } return (0); } } if ((convert_update(nodehdl, segment_name[cnt], field_name, field_value)) != 0) { return (1); } /* update UNIX_Timestamp32 with update time */ if ((update_unixtimestamp(nodehdl, segment_name[cnt], ptr)) != 0) { return (1); } return (0); } elem.num = 0; /* field not found add the the record in one of the segment */ for (cnt = 0; cnt < NUM_OF_SEGMENT; cnt++) { (void) fru_list_elems_in(nodehdl, segment_name[cnt], &elem); for (elem_cnt = 0; elem_cnt < elem.num; elem_cnt++) { if ((strcmp(*ptr, elem.strs[elem_cnt])) == 0) { add_flag = 0; } } if (add_flag) { if ((fru_add_element(nodehdl, segment_name[cnt], *ptr)) != FRU_SUCCESS) { continue; } } if ((fru_get_definition(*ptr, &def)) == FRU_SUCCESS) { if (def.iteration_count != 0) { if ((updateiter_record(nodehdl, cnt, ptr, field_name, field_value)) != 0) { return (1); } return (0); } } /* update UNIX_Timestamp32 with creation time */ if ((update_unixtimestamp(nodehdl, segment_name[cnt], ptr)) != 0) { return (1); } /* record added update the field with the value */ if ((convert_update(nodehdl, segment_name[cnt], field_name, field_value)) != 0) { return (1); } return (0); } /* segment not present, create one and add the record */ cnt = create_segment(nodehdl); if (cnt == 1) { return (1); } if ((status = fru_add_element(nodehdl, segment_name[cnt], *ptr)) != FRU_SUCCESS) { (void) fprintf(stderr, gettext("fru_add_element(): %s\n"), fru_strerror(status)); return (1); } if ((fru_get_definition(*ptr, &def)) == FRU_SUCCESS) { if (def.iteration_count != 0) { if ((updateiter_record(nodehdl, cnt, ptr, field_name, field_value)) != 0) { return (1); } return (0); } } /* update UNIX_Timestamp32 with creation time */ if ((update_unixtimestamp(nodehdl, segment_name[cnt], ptr)) != 0) { return (1); } if ((convert_update(nodehdl, segment_name[cnt], field_name, field_value)) != 0) { return (1); } return (0); } static int update_node_data(fru_nodehdl_t node) { int i; int status = 0; if (service_mode) { for (i = 0; i < svcargc; i += 2) if (update_field(node, svcargv[i], svcargv[i + 1])) { status = 1; } } else { status = update_field(node, "/Customer_DataR/Cust_Data", customer_data); } return (status); } static void walk_tree(fru_nodehdl_t node, const char *prior_path, int process_tree) { char *name, path[PATH_MAX]; int process_self = process_tree, status, update_status = 0; fru_nodehdl_t next_node; fru_node_t type; if ((status = fru_get_node_type(node, &type)) != FRU_SUCCESS) { (void) fprintf(stderr, gettext("Error getting FRU tree node type: %s\n"), fru_strerror(status)); exit(1); } if ((status = fru_get_name_from_hdl(node, &name)) != FRU_SUCCESS) { (void) fprintf(stderr, gettext("Error getting name of FRU tree node: %s\n"), fru_strerror(status)); exit(1); } /* * Build the current path */ if (snprintf(path, sizeof (path), "%s/%s", prior_path, name) >= sizeof (path)) { (void) fprintf(stderr, gettext("FRU tree path would overflow buffer\n")); exit(1); } free(name); /* * Process the node */ if (list_only) { (void) printf("%s%s\n", path, ((type == FRU_NODE_FRU) ? " (fru)" : ((type == FRU_NODE_CONTAINER) ? " (container)" : ""))); } else if ((process_tree || (process_self = pathmatch(path))) && (type == FRU_NODE_CONTAINER)) { (void) printf("%s\n", path); if (update) { status = update_node_data(node); update_status = status; } print_node_data(node); if (!recursive) { exit(status); } } else if (process_self && !recursive) { (void) fprintf(stderr, gettext("\"%s\" is not a container\n"), path); exit(1); } /* * Recurse */ if (fru_get_child(node, &next_node) == FRU_SUCCESS) walk_tree(next_node, path, process_self); if (fru_get_peer(node, &next_node) == FRU_SUCCESS) walk_tree(next_node, prior_path, process_tree); /* * when update_node_data failed, need to exit with return value 1 */ if (update_status) exit(1); } int main(int argc, char *argv[]) { int process_tree = 0, option, status; fru_nodehdl_t root; command = argv[0]; opterr = 0; /* "getopt" should not print to "stderr" */ while ((option = getopt(argc, argv, "lrs")) != EOF) { switch (option) { case 'l': list_only = 1; break; case 'r': recursive = 1; break; case 's': service_mode = 1; break; default: usage(); return (1); } } argc -= optind; argv += optind; if (argc == 0) { process_tree = 1; recursive = 1; } else { if (list_only) { usage(); return (1); } frupath = argv[0]; if (*frupath == 0) { usage(); (void) fprintf(stderr, gettext("\"frupath\" should not be empty\n")); return (1); } argc--; argv++; if (argc > 0) { update = 1; if (service_mode) { if ((argc % 2) != 0) { (void) fprintf(stderr, gettext("Must specify " "field-value pairs " "for update\n")); return (1); } if (validate_fieldnames(argc, argv) != 0) { return (1); } svcargc = argc; svcargv = argv; } else if (argc == 1) customer_data = argv[0]; else { usage(); return (1); } } } if ((status = fru_open_data_source("picl", NULL)) != FRU_SUCCESS) { (void) fprintf(stderr, gettext("Unable to access FRU data source: %s\n"), fru_strerror(status)); return (1); } if ((status = fru_get_root(&root)) == FRU_NODENOTFOUND) { (void) fprintf(stderr, gettext("This system does not support PICL " "infrastructure to provide FRUID data\n" "Please use the platform SP to access the FRUID " "information\n")); return (1); } else if (status != FRU_SUCCESS) { (void) fprintf(stderr, gettext("Unable to access FRU ID data " "due to data source error\n")); return (1); } walk_tree(root, "", process_tree); if ((frupath != NULL) && (!found_frupath)) { (void) fprintf(stderr, gettext("\"%s\" not found\n"), frupath); return (1); } return (0); }