xref: /illumos-gate/usr/src/cmd/fruadm/fruadm.c (revision d8109ce4330e1b8ad6c29f9fccacec969066bb9d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2014 Gary Mills
24  *
25  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
26  * Use is subject to license terms.
27  *
28  * Copyright (c) 2018, Joyent, Inc.
29  */
30 
31 #include <limits.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <libintl.h>
36 #include <libfru.h>
37 #include <errno.h>
38 #include <math.h>
39 #include <alloca.h>
40 #include <assert.h>
41 #include <sys/systeminfo.h>
42 
43 #define	NUM_OF_SEGMENT	1
44 #define	SEGMENT_NAME_SIZE	2
45 
46 #define	FD_SEGMENT_SIZE	2949
47 
48 static char  *command, *customer_data = NULL, *frupath = NULL, **svcargv;
49 
50 /* DataElement supported in the customer operation */
51 static  char    *cust_data_list[] = {"Customer_DataR"};
52 
53 /* DataElement supported in the service operation */
54 static  char    *serv_data_list[] = {"InstallationR", "ECO_CurrentR"};
55 
56 /* currently supported segment name */
57 static  char    *segment_name[] = {"FD"};
58 
59 static int   found_frupath = 0, list_only = 0, recursive = 0,
60     service_mode = 0, svcargc, update = 0;
61 
62 
63 static void
64 usage(void)
65 {
66 	(void) fprintf(stderr,
67 	    gettext("Usage:  %s [ -l ] | [ [ -r ] frupath [ text ] ]\n"),
68 	    command);
69 }
70 
71 static int
72 validate_fieldnames(int argc, char *argv[])
73 {
74 	static int	num = sizeof (serv_data_list)/sizeof (*serv_data_list);
75 
76 	char		*fieldname;
77 
78 	int		i, j, match, status;
79 
80 	fru_elemdef_t	definition;
81 
82 
83 	for (i = 0; i < argc; i += 2) {
84 		if (argv[i][0] == '/') {
85 			fieldname = &argv[i][1];
86 		} else {
87 			fieldname = &argv[i][0];
88 		}
89 
90 		match = 0;
91 		for (j = 0; j < num; j++) {
92 			if (strncmp(fieldname, serv_data_list[j],
93 			    strlen(serv_data_list[j])) == 0) {
94 				match = 1;
95 			}
96 		}
97 		if (!match) {
98 			(void) fprintf(stderr,
99 			    gettext("\"%s\" is not a supported field\n"),
100 			    argv[i]);
101 			return (1);
102 		}
103 
104 		if ((status = fru_get_definition(argv[i], &definition))
105 		    != FRU_SUCCESS) {
106 			(void) fprintf(stderr, gettext("\"%s\":  %s\n"),
107 			    argv[i],
108 			    fru_strerror(status));
109 			return (1);
110 		} else if ((definition.data_type == FDTYPE_Record) ||
111 		    (definition.data_type == FDTYPE_UNDEFINED)) {
112 			(void) fprintf(stderr,
113 			    gettext("\"%s\" is not a field\n"), argv[i]);
114 			return (1);
115 		}
116 	}
117 
118 	return (0);
119 }
120 
121 static int
122 pathmatch(const char *path)
123 {
124 	char  *match;
125 
126 	if ((frupath != NULL) &&
127 	    ((match = strstr(path, frupath)) != NULL) &&
128 	    ((match + strlen(frupath)) == (path + strlen(path))) &&
129 	    ((match == path) || (*(match - 1) == '/'))) {
130 		found_frupath = 1;
131 		return (1);
132 	}
133 	return (0);
134 }
135 
136 static void
137 displayBinary(unsigned char *data, size_t length, fru_elemdef_t *def)
138 {
139 	int	i = 0;
140 	uint64_t	lldata;
141 	uint64_t	mask;
142 
143 	if (def->disp_type == FDISP_Hex) {
144 		for (i = 0; i < length; i++) {
145 			(void) printf("%02X", data[i]);
146 		}
147 		return;
148 	}
149 
150 	(void) memcpy(&lldata, data, sizeof (lldata));
151 	switch (def->disp_type) {
152 		case FDISP_Binary:
153 		{
154 			mask = 0x8000000000000000ULL;
155 			for (i = 0; i < (sizeof (uint64_t) *8); i++) {
156 				if (lldata & (mask >> i)) {
157 					(void) printf("1");
158 				} else {
159 					(void) printf("0");
160 				}
161 			}
162 			return;
163 		}
164 		case FDISP_Octal:
165 		{
166 			(void) printf("%llo", lldata);
167 			return;
168 		}
169 		case FDISP_Decimal:
170 		{
171 			(void) printf("%lld", lldata);
172 			return;
173 		}
174 		case FDISP_Time:
175 		{
176 			char buffer[PATH_MAX];
177 			time_t time;
178 			time = (time_t)lldata;
179 			(void) strftime(buffer, PATH_MAX, "%+",
180 			    localtime(&time));
181 			(void) printf("%s", buffer);
182 			return;
183 		}
184 	}
185 }
186 
187 static void
188 displayBAasBinary(unsigned char *data, size_t length)
189 {
190 	int i;
191 	unsigned char mask;
192 
193 	for (i = 0; i < length; i++) {
194 		/*
195 		 * make a mask for the high order bit and adjust down through
196 		 * all the bits.
197 		 */
198 		for (mask = 0x80; mask > 0; mask /= 2) {
199 			if ((data[i] & mask) != 0) /* bit must be on */
200 				(void) printf("1");
201 			else /* bit is off... */
202 				(void) printf("0");
203 		}
204 	}
205 	(void) printf("\n");
206 }
207 
208 static void
209 display_data(unsigned char *data, size_t length, fru_elemdef_t *def)
210 {
211 	int i = 0;
212 	uint64_t	lldata;
213 
214 	if (data == 0x00) {
215 		(void) printf("\n");
216 		return;
217 	}
218 
219 	switch (def->data_type) {
220 	case FDTYPE_Binary:
221 	{
222 		displayBinary(data, length, def);
223 		return;
224 	}
225 
226 	case FDTYPE_ByteArray:
227 	{
228 		switch (def->disp_type) {
229 		case FDISP_Binary:
230 			displayBAasBinary(data, length);
231 			return;
232 		case FDISP_Hex:
233 			for (i = 0; i < length; i++) {
234 				(void) printf("%02X", data[i]);
235 			}
236 			return;
237 		}
238 		return;
239 	}
240 	case FDTYPE_Unicode:
241 		assert(gettext("Unicode not yet supported") == 0);
242 		break;
243 	case FDTYPE_ASCII:
244 	{
245 		char *disp_str = (char *)alloca(length+1);
246 		for (i = 0; i < length; i++)
247 			disp_str[i] = data[i];
248 		disp_str[i] = '\0';
249 		(void) printf("%s", disp_str);
250 		return;
251 	}
252 
253 	case FDTYPE_Enumeration:
254 	{
255 		lldata = strtoull((const char *)data, NULL, 0);
256 		for (i = 0; i < def->enum_count; i++) {
257 			if (def->enum_table[i].value == lldata) {
258 			/* strdup such that map_... can realloc if necessary. */
259 				char *tmp = strdup(def->enum_table[i].text);
260 				(void) printf("%s", tmp);
261 				free(tmp);
262 				return;
263 			}
264 		}
265 		(void) printf(gettext("Unrecognized Value:  0x"));
266 		for (i = 0; i < sizeof (uint64_t); i++)
267 			(void) printf("%02X", data[i]);
268 		break;
269 	}
270 	default:
271 		break;
272 	}
273 }
274 
275 static void
276 print_node_data(fru_nodehdl_t cont_hdl)
277 {
278 	int	iter_cnt = 0;
279 	int	iter;
280 	int	numseg;
281 	int	list_cnt;
282 	unsigned char	*data;
283 	size_t	dataLen;
284 	int	total_cnt;
285 	char	*found_path = NULL;
286 	fru_elemdef_t	 def, def1;
287 	int	instance = 0;
288 	char	**ptr;
289 	char	**tmp_ptr;
290 	int	count = 0;
291 	char	elem_name[PATH_MAX];
292 
293 	if (service_mode) {
294 		total_cnt = sizeof (serv_data_list)/sizeof (*serv_data_list);
295 		ptr = serv_data_list;
296 	} else {
297 		total_cnt = sizeof (cust_data_list)/sizeof (*cust_data_list);
298 		ptr = cust_data_list;
299 	}
300 	tmp_ptr = ptr;
301 
302 	for (numseg = 0; numseg < NUM_OF_SEGMENT; numseg++) {
303 		ptr = tmp_ptr;
304 		for (list_cnt = 0; list_cnt < total_cnt; list_cnt++) {
305 			if ((fru_get_definition(*ptr, &def)) != FRU_SUCCESS) {
306 				continue;
307 			}
308 			if ((fru_get_num_iterations(cont_hdl,
309 			    &segment_name[numseg], 0, *ptr,
310 			    &iter_cnt, NULL)) != FRU_SUCCESS) {
311 				iter_cnt = 0;
312 			}
313 			iter = 0;
314 			do {
315 				for (count = 0; count < def.enum_count;
316 				    count++) {
317 					if (def.iteration_type !=
318 					    FRU_NOT_ITERATED) {
319 						(void) snprintf(elem_name,
320 						    sizeof (elem_name),
321 			"/%s[%d]/%s", *ptr, iter, def.enum_table[count].text);
322 					} else {
323 						(void) snprintf(elem_name,
324 						    sizeof (elem_name),
325 			"/%s/%s", *ptr, def.enum_table[count].text);
326 					}
327 
328 					if ((fru_read_field(cont_hdl,
329 					    &segment_name[numseg], instance,
330 					    elem_name, (void**)&data, &dataLen,
331 					    &found_path)) != FRU_SUCCESS) {
332 						break;
333 					}
334 
335 					if ((fru_get_definition(
336 			def.enum_table[count].text, &def1)) != FRU_SUCCESS) {
337 						break;
338 					}
339 					(void) printf("	%s:  ",\
340 					    elem_name);
341 					display_data(data, dataLen, &def1);
342 					(void) printf("\n");
343 				}
344 				iter ++;
345 			} while (iter < iter_cnt);
346 			ptr++;
347 		}
348 	}
349 }
350 
351 static char *
352 convertBinaryToDecimal(char *ptr)
353 {
354 	int	cnt = 0;
355 	char	*data;
356 	int	str_len;
357 	char	*ret = NULL;
358 	uint64_t	result = 0;
359 
360 	str_len = strlen(ptr);
361 	data = ptr;
362 
363 	while (str_len >= 1) {
364 		str_len -= 1;
365 		if (data[str_len] == '0') {
366 			result += (0 * pow(2, cnt));
367 		}
368 		if (data[str_len] == '1') {
369 			result += (1 * pow(2, cnt));
370 		}
371 		cnt++;
372 	}
373 	ret = (char *)lltostr(result, "\n");
374 	return (ret);
375 }
376 
377 /*
378  * called update_field() to update the field with specific field value.
379  * nodehdl represents the fru, segment represents the segment name in the fru.
380  * field_name represents the field to be updated with the value field_value.
381  */
382 
383 static int
384 convert_update(fru_nodehdl_t nodehdl, char *segment, char *field_name,
385 							char *field_value)
386 {
387 	uint64_t num = 0;
388 	fru_elemdef_t def;
389 	fru_errno_t	err;
390 	void    *data = NULL;
391 	size_t  dataLen = 0;
392 	int	i;
393 
394 	if ((err = fru_get_definition(field_name, &def)) != FRU_SUCCESS) {
395 		(void) fprintf(stderr,
396 		    gettext("Failed to get definition %s:  %s\n"),
397 		    field_name, fru_strerror(err));
398 		return (1);
399 	}
400 
401 	if (field_value == NULL) {
402 		return (1);
403 	}
404 
405 	switch (def.data_type) {
406 		case    FDTYPE_Binary:
407 			if (def.disp_type != FDISP_Time) {
408 				if (field_value[0] == 'b') {
409 					field_value =
410 					    convertBinaryToDecimal((field_value
411 					    +1));
412 				}
413 				num = strtoll(field_value, (char **)NULL, 0);
414 				if ((num == 0) && (errno == 0)) {
415 					return (1);
416 				}
417 				data = (void*)&num;
418 				dataLen = sizeof (uint64_t);
419 			}
420 			break;
421 		case    FDTYPE_ByteArray:
422 			return (1);
423 		case    FDTYPE_Unicode:
424 			return (1);
425 		case    FDTYPE_ASCII:
426 			data = (void *) field_value;
427 			dataLen = strlen(field_value);
428 			if (dataLen < def.data_length) {
429 				dataLen++;
430 			}
431 			break;
432 		case    FDTYPE_Enumeration:
433 			for (i = 0; i < def.enum_count; i++) {
434 				if (strcmp(def.enum_table[i].text,
435 				    field_value) == 0) {
436 					data = (void *)(uintptr_t)
437 					    def.enum_table[i].value;
438 					dataLen = sizeof (uint64_t);
439 					break;
440 				}
441 			}
442 			return (1);
443 		case    FDTYPE_Record:
444 			if (def.iteration_count == 0) {
445 				return (1);
446 			}
447 			data = NULL;
448 			dataLen = 0;
449 			break;
450 		case    FDTYPE_UNDEFINED:
451 			return (1);
452 	}
453 
454 	if ((err = fru_update_field(nodehdl, segment, 0, field_name, data,
455 	    dataLen)) != FRU_SUCCESS) {
456 		(void) fprintf(stderr, gettext("fru_update_field():  %s\n"),
457 		    fru_strerror(err));
458 		return (1);
459 	}
460 	return (0);
461 }
462 /*
463  * called by update_field() when a new data element is created.
464  * it updates the UNIX_Timestamp32 field with the current system time.
465  */
466 
467 static int
468 update_unixtimestamp(fru_nodehdl_t nodehdl, char *segment, char **ptr)
469 {
470 	char	*field_name;
471 	time_t	clock;
472 	struct	tm *sp_tm;
473 	fru_errno_t	err = FRU_SUCCESS;
474 	uint64_t	time_data;
475 	size_t		len;
476 
477 	len = strlen(*ptr) + strlen("UNIX_Timestamp32") + 3;
478 	field_name = alloca(len);
479 
480 	(void) snprintf(field_name, len, "/%s/UNIX_Timestamp32", *ptr);
481 
482 	clock = time(NULL);
483 	sp_tm = localtime(&clock);
484 	time_data = (uint64_t)mktime(sp_tm);
485 
486 	if ((err = fru_update_field(nodehdl, segment, 0, field_name,
487 	    (void *)&time_data, sizeof (time_data))) != FRU_SUCCESS) {
488 		(void) fprintf(stderr, gettext("fru_update_field():  %s\n"),
489 		    fru_strerror(err));
490 		return (1);
491 	}
492 	return (0);
493 }
494 
495 /*
496  * create segment on the specified fru represented by nodehdl.
497  */
498 
499 static int
500 create_segment(fru_nodehdl_t nodehdl)
501 {
502 	fru_segdesc_t	seg_desc;
503 	fru_segdef_t	def;
504 	int	cnt;
505 	int	status;
506 
507 	(void) memset(&seg_desc, 0, sizeof (seg_desc));
508 	seg_desc.field.field_perm = 0x6;
509 	seg_desc.field.operations_perm = 0x6;
510 	seg_desc.field.engineering_perm = 0x6;
511 	seg_desc.field.repair_perm = 0x6;
512 
513 	(void) memset(&def, 0, sizeof (def));
514 	def.address = 0;
515 	def.desc.raw_data = seg_desc.raw_data;
516 	def.hw_desc.all_bits = 0;
517 
518 	for (cnt = 0; cnt < NUM_OF_SEGMENT; cnt++) {
519 		(void) strncpy(def.name, segment_name[cnt], SEGMENT_NAME_SIZE);
520 		if (cnt == 0) {
521 			def.size = FD_SEGMENT_SIZE;
522 		}
523 		if ((status = fru_create_segment(nodehdl, &def))
524 		    != FRU_SUCCESS) {
525 			continue;
526 		}
527 		return (cnt);
528 	}
529 	if (status != FRU_SUCCESS)
530 		(void) fprintf(stderr, gettext("fru_create_segment():  %s\n"),
531 		    fru_strerror(status));
532 	return (1);
533 }
534 
535 /*
536  * called from update_field() when service flag is ON. currently
537  * supported iterated record is InstallationR and fields supported for
538  * update are Geo_North, Geo_East, Geo_Alt, Geo_Location.
539  */
540 
541 static int
542 updateiter_record(fru_nodehdl_t nodehdl, int cnt, char **ptr,
543 			char *field_name, char  *field_value)
544 {
545 	int	iter_cnt  = 0;
546 	char	rec_name[512];
547 	void	*data = NULL;
548 	char	*tmpptr = NULL;
549 	size_t	dataLen = 0;
550 	char	**elem_ptr;
551 	int	found = 0;
552 	int	index;
553 	int	total_cnt;
554 	fru_errno_t	err;
555 
556 	static  char    *elem_list[] = {"/Geo_North", "/Geo_East",\
557 				"/Geo_Alt", "/Geo_Location"};
558 
559 	elem_ptr = elem_list;
560 	total_cnt = sizeof (elem_list)/sizeof (*elem_list);
561 
562 	for (index = 0; index < total_cnt; index++) {
563 		tmpptr = strrchr(field_name, '/');
564 		if (tmpptr == NULL) {
565 			(void) fprintf(stderr,
566 			    gettext("Error:  Data Element not known\n"));
567 			return (1);
568 		}
569 		if ((strncmp(*elem_ptr, tmpptr, strlen(*elem_ptr)) != 0)) {
570 			elem_ptr++;
571 			continue;
572 		}
573 		found = 1;
574 		break;
575 	}
576 
577 	if (found == 0) {
578 		(void) fprintf(stderr,
579 		    gettext("Error:  Update not allowed for field:  %s\n"),
580 		    field_name);
581 		return (1);
582 	}
583 
584 	if ((fru_get_num_iterations(nodehdl, &segment_name[cnt], 0,
585 	    *ptr, &iter_cnt, NULL)) != FRU_SUCCESS) {
586 		return (1);
587 	}
588 
589 	/* add a new Iterated Record if complete path is not given */
590 	if (iter_cnt == 0) {
591 		(void) snprintf(rec_name, sizeof (rec_name), "/%s[+]", *ptr);
592 		if ((err = fru_update_field(nodehdl, segment_name[cnt], 0,
593 		    rec_name, data, dataLen)) != FRU_SUCCESS) {
594 			(void) fprintf(stderr,
595 			gettext("fru_update_field():  %s\n"),
596 			    fru_strerror(err));
597 			return (1);
598 		}
599 
600 		iter_cnt = 1;
601 	}
602 
603 	(void) snprintf(rec_name, sizeof (rec_name), "/%s[%d]%s",
604 	    *ptr, iter_cnt-1, strrchr(field_name, '/'));
605 
606 	if ((convert_update(nodehdl, segment_name[cnt], rec_name,
607 	    field_value)) != 0) {
608 		return (1);
609 	}
610 
611 	/* update success  now update the unix timestamp */
612 
613 	(void) snprintf(rec_name, sizeof (rec_name), "/%s[%d]",
614 	    *ptr, iter_cnt-1);
615 	tmpptr = rec_name;
616 
617 	/* update UNIX_Timestamp32 with creation time */
618 	if ((update_unixtimestamp(nodehdl, segment_name[cnt],
619 	    &tmpptr)) != 0) {
620 		return (1);
621 	}
622 
623 	return (0);
624 }
625 
626 static int
627 update_field(fru_nodehdl_t nodehdl, char *field_name, char *field_value)
628 {
629 	fru_elemdef_t	def;
630 	unsigned char	*data;
631 	size_t	dataLen;
632 	char	*found_path = NULL;
633 	int	cnt;
634 	char	**ptr;
635 	fru_strlist_t	elem;
636 	int	elem_cnt;
637 	int	add_flag = 1;
638 	int	total_cnt;
639 	int	status;
640 
641 	if (service_mode) {
642 		ptr = serv_data_list;
643 		total_cnt = sizeof (serv_data_list)/sizeof (*serv_data_list);
644 
645 		for (cnt = 0; cnt < total_cnt; cnt++) {
646 			if ((strncmp(*ptr, &field_name[1], strlen(*ptr)) \
647 			    != 0) && (strncmp(*ptr, &field_name[0],
648 			    strlen(*ptr)) != 0)) {
649 				ptr++;
650 				add_flag = 0;
651 				continue;
652 			}
653 			add_flag = 1;
654 			break;
655 		}
656 	} else {
657 		ptr = cust_data_list;
658 	}
659 
660 	/* look for the field in either of the segment if found update it */
661 	for (cnt = 0; cnt < NUM_OF_SEGMENT; cnt++) {
662 		if ((fru_read_field(nodehdl, &segment_name[cnt], 0, field_name,
663 		    (void **) &data, &dataLen, &found_path)) != FRU_SUCCESS) {
664 			continue;
665 		}
666 		if ((fru_get_definition(*ptr, &def)) == FRU_SUCCESS) {
667 			if (def.iteration_count != 0) {
668 				if ((updateiter_record(nodehdl, cnt, ptr,
669 				    field_name, field_value)) != 0) {
670 					return (1);
671 				}
672 				return (0);
673 			}
674 		}
675 
676 		if ((convert_update(nodehdl, segment_name[cnt],
677 		    field_name, field_value)) != 0) {
678 			return (1);
679 		}
680 
681 		/* update UNIX_Timestamp32 with update time */
682 		if ((update_unixtimestamp(nodehdl, segment_name[cnt],
683 		    ptr)) != 0) {
684 			return (1);
685 		}
686 		return (0);
687 	}
688 
689 	elem.num = 0;
690 
691 	/* field not found add the the record in one of the segment */
692 	for (cnt = 0; cnt < NUM_OF_SEGMENT; cnt++) {
693 		(void) fru_list_elems_in(nodehdl, segment_name[cnt], &elem);
694 		for (elem_cnt = 0; elem_cnt < elem.num; elem_cnt++) {
695 			if ((strcmp(*ptr, elem.strs[elem_cnt])) == 0) {
696 				add_flag = 0;
697 			}
698 		}
699 
700 		if (add_flag) {
701 			if ((fru_add_element(nodehdl, segment_name[cnt],
702 			    *ptr)) != FRU_SUCCESS) {
703 				continue;
704 			}
705 		}
706 
707 		if ((fru_get_definition(*ptr, &def)) == FRU_SUCCESS) {
708 			if (def.iteration_count != 0) {
709 				if ((updateiter_record(nodehdl, cnt, ptr,
710 				    field_name, field_value)) != 0) {
711 					return (1);
712 				}
713 				return (0);
714 			}
715 		}
716 
717 		/* update UNIX_Timestamp32 with creation time */
718 		if ((update_unixtimestamp(nodehdl, segment_name[cnt],
719 		    ptr)) != 0) {
720 			return (1);
721 		}
722 
723 		/* record added update the field with the value */
724 		if ((convert_update(nodehdl, segment_name[cnt], field_name,
725 		    field_value)) != 0) {
726 			return (1);
727 		}
728 		return (0);
729 	}
730 
731 	/* segment not present, create one and add the record */
732 	cnt = create_segment(nodehdl);
733 	if (cnt == 1) {
734 		return (1);
735 	}
736 
737 	if ((status = fru_add_element(nodehdl, segment_name[cnt], *ptr))
738 	    != FRU_SUCCESS) {
739 		(void) fprintf(stderr, gettext("fru_add_element():  %s\n"),
740 		    fru_strerror(status));
741 		return (1);
742 	}
743 
744 	if ((fru_get_definition(*ptr, &def)) == FRU_SUCCESS) {
745 		if (def.iteration_count != 0) {
746 			if ((updateiter_record(nodehdl,  cnt, ptr,
747 			    field_name, field_value)) != 0) {
748 				return (1);
749 			}
750 			return (0);
751 		}
752 	}
753 
754 	/* update UNIX_Timestamp32 with creation time */
755 	if ((update_unixtimestamp(nodehdl, segment_name[cnt],
756 	    ptr)) != 0) {
757 		return (1);
758 	}
759 
760 	if ((convert_update(nodehdl, segment_name[cnt], field_name,
761 	    field_value)) != 0) {
762 		return (1);
763 	}
764 	return (0);
765 }
766 
767 static int
768 update_node_data(fru_nodehdl_t node)
769 {
770 	int	i;
771 	int	status = 0;
772 
773 	if (service_mode) {
774 		for (i = 0; i < svcargc; i += 2)
775 			if (update_field(node, svcargv[i], svcargv[i + 1])) {
776 				status = 1;
777 			}
778 	} else {
779 		status = update_field(node, "/Customer_DataR/Cust_Data",
780 		    customer_data);
781 	}
782 	return (status);
783 }
784 
785 static void
786 walk_tree(fru_nodehdl_t node, const char *prior_path, int process_tree)
787 {
788 	char	*name, path[PATH_MAX];
789 	int	process_self = process_tree, status, update_status = 0;
790 	fru_nodehdl_t	 next_node;
791 	fru_node_t	type;
792 
793 	if ((status = fru_get_node_type(node, &type)) != FRU_SUCCESS) {
794 		(void) fprintf(stderr,
795 		    gettext("Error getting FRU tree node type:  %s\n"),
796 		    fru_strerror(status));
797 		exit(1);
798 	}
799 
800 	if ((status = fru_get_name_from_hdl(node, &name)) != FRU_SUCCESS) {
801 		(void) fprintf(stderr,
802 		    gettext("Error getting name of FRU tree node:  %s\n"),
803 		    fru_strerror(status));
804 		exit(1);
805 	}
806 
807 
808 	/*
809 	 * Build the current path
810 	 */
811 	if (snprintf(path, sizeof (path), "%s/%s", prior_path, name)
812 	    >= sizeof (path)) {
813 		(void) fprintf(stderr,
814 		    gettext("FRU tree path would overflow buffer\n"));
815 		exit(1);
816 	}
817 
818 	free(name);
819 
820 	/*
821 	 * Process the node
822 	 */
823 	if (list_only) {
824 		(void) printf("%s%s\n", path, ((type == FRU_NODE_FRU) ?
825 		    " (fru)" : ((type == FRU_NODE_CONTAINER) ?
826 		    " (container)" : "")));
827 	} else if ((process_tree || (process_self = pathmatch(path))) &&
828 	    (type == FRU_NODE_CONTAINER)) {
829 		(void) printf("%s\n", path);
830 		if (update) {
831 			status = update_node_data(node);
832 			update_status = status;
833 		}
834 		print_node_data(node);
835 		if (!recursive) {
836 			exit(status);
837 		}
838 	} else if (process_self && !recursive) {
839 		(void) fprintf(stderr,
840 		    gettext("\"%s\" is not a container\n"), path);
841 		exit(1);
842 	}
843 
844 
845 	/*
846 	 * Recurse
847 	 */
848 	if (fru_get_child(node, &next_node) == FRU_SUCCESS)
849 		walk_tree(next_node, path, process_self);
850 
851 	if (fru_get_peer(node, &next_node) == FRU_SUCCESS)
852 		walk_tree(next_node, prior_path, process_tree);
853 
854 	/*
855 	 * when update_node_data failed, need to exit with return value 1
856 	 */
857 	if (update_status)
858 		exit(1);
859 }
860 
861 int
862 main(int argc, char *argv[])
863 {
864 	int	process_tree = 0, option, status;
865 
866 	fru_nodehdl_t  root;
867 
868 
869 	command = argv[0];
870 
871 	opterr = 0;	/*  "getopt" should not print to "stderr"  */
872 	while ((option = getopt(argc, argv, "lrs")) != EOF) {
873 	switch (option) {
874 		case 'l':
875 			list_only = 1;
876 			break;
877 		case 'r':
878 			recursive = 1;
879 			break;
880 		case 's':
881 			service_mode = 1;
882 			break;
883 		default:
884 			usage();
885 			return (1);
886 		}
887 	}
888 
889 	argc -= optind;
890 	argv += optind;
891 
892 	if (argc == 0) {
893 		process_tree   = 1;
894 		recursive = 1;
895 	} else {
896 		if (list_only) {
897 			usage();
898 			return (1);
899 		}
900 
901 		frupath = argv[0];
902 		if (*frupath == 0) {
903 			usage();
904 			(void) fprintf(stderr,
905 			    gettext("\"frupath\" should not be empty\n"));
906 			return (1);
907 		}
908 
909 		argc--;
910 		argv++;
911 
912 		if (argc > 0) {
913 			update = 1;
914 			if (service_mode) {
915 				if ((argc % 2) != 0) {
916 					(void) fprintf(stderr,
917 					    gettext("Must specify "
918 					    "field-value pairs "
919 					    "for update\n"));
920 					return (1);
921 				}
922 
923 				if (validate_fieldnames(argc, argv) != 0) {
924 					return (1);
925 				}
926 
927 				svcargc = argc;
928 				svcargv = argv;
929 			} else if (argc == 1)
930 				customer_data = argv[0];
931 			else {
932 				usage();
933 				return (1);
934 			}
935 		}
936 	}
937 
938 	if ((status = fru_open_data_source("picl", NULL)) != FRU_SUCCESS) {
939 		(void) fprintf(stderr,
940 		    gettext("Unable to access FRU data source: 	%s\n"),
941 		    fru_strerror(status));
942 		return (1);
943 	}
944 
945 	if ((status = fru_get_root(&root)) == FRU_NODENOTFOUND) {
946 		(void) fprintf(stderr,
947 		    gettext("This system does not support PICL "
948 		    "infrastructure to provide FRUID data\n"
949 		    "Please use the platform SP to access the FRUID "
950 		    "information\n"));
951 		return (1);
952 	} else if (status != FRU_SUCCESS) {
953 		(void) fprintf(stderr,
954 		    gettext("Unable to access FRU ID data "
955 		    "due to data source error\n"));
956 		return (1);
957 	}
958 
959 	walk_tree(root, "", process_tree);
960 
961 	if ((frupath != NULL) && (!found_frupath)) {
962 		(void) fprintf(stderr,
963 		    gettext("\"%s\" not found\n"),
964 		    frupath);
965 		return (1);
966 	}
967 
968 	return (0);
969 }
970