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) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25/*
26 * Copyright (c) 2019, Joyent, Inc.
27 */
28
29#include <alloca.h>
30#include <ctype.h>
31#include <limits.h>
32#include <syslog.h>
33#include <strings.h>
34#include <unistd.h>
35#include <sys/fm/protocol.h>
36#include <sys/systeminfo.h>
37#include <sys/utsname.h>
38
39#include <topo_error.h>
40#include <topo_subr.h>
41
42void
43topo_hdl_lock(topo_hdl_t *thp)
44{
45	(void) pthread_mutex_lock(&thp->th_lock);
46}
47
48void
49topo_hdl_unlock(topo_hdl_t *thp)
50{
51	(void) pthread_mutex_unlock(&thp->th_lock);
52}
53
54const char *
55topo_stability2name(topo_stability_t s)
56{
57	switch (s) {
58	case TOPO_STABILITY_INTERNAL:	return (TOPO_STABSTR_INTERNAL);
59	case TOPO_STABILITY_PRIVATE:	return (TOPO_STABSTR_PRIVATE);
60	case TOPO_STABILITY_OBSOLETE:	return (TOPO_STABSTR_OBSOLETE);
61	case TOPO_STABILITY_EXTERNAL:	return (TOPO_STABSTR_EXTERNAL);
62	case TOPO_STABILITY_UNSTABLE:	return (TOPO_STABSTR_UNSTABLE);
63	case TOPO_STABILITY_EVOLVING:	return (TOPO_STABSTR_EVOLVING);
64	case TOPO_STABILITY_STABLE:	return (TOPO_STABSTR_STABLE);
65	case TOPO_STABILITY_STANDARD:	return (TOPO_STABSTR_STANDARD);
66	default:			return (TOPO_STABSTR_UNKNOWN);
67	}
68}
69
70topo_stability_t
71topo_name2stability(const char *name)
72{
73	if (strcmp(name, TOPO_STABSTR_INTERNAL) == 0)
74		return (TOPO_STABILITY_INTERNAL);
75	else if (strcmp(name, TOPO_STABSTR_PRIVATE) == 0)
76		return (TOPO_STABILITY_PRIVATE);
77	else if (strcmp(name, TOPO_STABSTR_OBSOLETE) == 0)
78		return (TOPO_STABILITY_OBSOLETE);
79	else if (strcmp(name, TOPO_STABSTR_EXTERNAL) == 0)
80		return (TOPO_STABILITY_EXTERNAL);
81	else if (strcmp(name, TOPO_STABSTR_UNSTABLE) == 0)
82		return (TOPO_STABILITY_UNSTABLE);
83	else if (strcmp(name, TOPO_STABSTR_EVOLVING) == 0)
84		return (TOPO_STABILITY_EVOLVING);
85	else if (strcmp(name, TOPO_STABSTR_STABLE) == 0)
86		return (TOPO_STABILITY_STABLE);
87	else if (strcmp(name, TOPO_STABSTR_STANDARD) == 0)
88		return (TOPO_STABILITY_STANDARD);
89
90	return (TOPO_STABILITY_UNKNOWN);
91}
92
93static const topo_debug_mode_t _topo_dbout_modes[] = {
94	{ "stderr", "send debug messages to stderr", TOPO_DBOUT_STDERR },
95	{ "syslog", "send debug messages to syslog", TOPO_DBOUT_SYSLOG },
96	{ NULL, NULL, 0 }
97};
98
99static const topo_debug_mode_t _topo_dbflag_modes[] = {
100	{ "error", "error handling debug messages enabled", TOPO_DBG_ERR },
101	{ "module", "module debug messages enabled", TOPO_DBG_MOD },
102	{ "modulesvc", "module services debug messages enabled",
103	    TOPO_DBG_MODSVC },
104	{ "walk", "walker subsystem debug messages enabled", TOPO_DBG_WALK },
105	{ "xml", "xml file parsing messages enabled", TOPO_DBG_XML },
106	{ "devinfoforce", "devinfo DINFOFORCE snapshot used", TOPO_DBG_FORCE },
107	{ "all", "all debug modes enabled", TOPO_DBG_ALL},
108	{ NULL, NULL, 0 }
109};
110
111void
112env_process_value(topo_hdl_t *thp, const char *begin, const char *end)
113{
114	char buf[MAXNAMELEN];
115	size_t count;
116	topo_debug_mode_t *dbp;
117
118	while (begin < end && isspace(*begin))
119		begin++;
120
121	while (begin < end && isspace(*(end - 1)))
122		end--;
123
124	if (begin >= end)
125		return;
126
127	count = end - begin;
128	count += 1;
129
130	if (count > sizeof (buf))
131		return;
132
133	(void) snprintf(buf, count, "%s", begin);
134
135	for (dbp = (topo_debug_mode_t *)_topo_dbflag_modes;
136	    dbp->tdm_name != NULL; ++dbp) {
137		if (strcmp(buf, dbp->tdm_name) == 0)
138			thp->th_debug |= dbp->tdm_mode;
139	}
140}
141
142void
143topo_debug_set(topo_hdl_t *thp, const char *dbmode, const char *dout)
144{
145	char *end, *value, *next;
146	topo_debug_mode_t *dbp;
147
148	topo_hdl_lock(thp);
149	value = (char *)dbmode;
150
151	for (end = (char *)dbmode; *end != '\0'; value = next) {
152		end = strchr(value, ',');
153		if (end != NULL)
154			next = end + 1;	/* skip the comma */
155		else
156			next = end = value + strlen(value);
157
158		env_process_value(thp, value, end);
159	}
160
161	if (dout == NULL) {
162		topo_hdl_unlock(thp);
163		return;
164	}
165
166	for (dbp = (topo_debug_mode_t *)_topo_dbout_modes;
167	    dbp->tdm_name != NULL; ++dbp) {
168		if (strcmp(dout, dbp->tdm_name) == 0)
169			thp->th_dbout = dbp->tdm_mode;
170	}
171	topo_hdl_unlock(thp);
172}
173
174void
175topo_vdprintf(topo_hdl_t *thp, const char *mod, const char *format, va_list ap)
176{
177	char *msg;
178	size_t len;
179	char c;
180
181	len = vsnprintf(&c, 1, format, ap);
182	msg = alloca(len + 2);
183	(void) vsnprintf(msg, len + 1, format, ap);
184
185	if (msg[len - 1] != '\n')
186		(void) strcpy(&msg[len], "\n");
187
188	if (thp->th_dbout == TOPO_DBOUT_SYSLOG) {
189		if (mod == NULL) {
190			syslog(LOG_DEBUG | LOG_USER, "libtopo DEBUG: %s", msg);
191		} else {
192			syslog(LOG_DEBUG | LOG_USER, "libtopo DEBUG: %s: %s",
193			    mod, msg);
194		}
195	} else {
196		if (mod == NULL) {
197			(void) fprintf(stderr, "libtopo DEBUG: %s", msg);
198		} else {
199			(void) fprintf(stderr, "libtopo DEBUG: %s: %s", mod,
200			    msg);
201		}
202	}
203}
204
205/*PRINTFLIKE3*/
206void
207topo_dprintf(topo_hdl_t *thp, int mask, const char *format, ...)
208{
209	va_list ap;
210
211	if (!(thp->th_debug & mask))
212		return;
213
214	va_start(ap, format);
215	topo_vdprintf(thp, NULL, format, ap);
216	va_end(ap);
217}
218
219tnode_t *
220topo_hdl_root(topo_hdl_t *thp, const char *scheme)
221{
222	ttree_t *tp;
223
224	for (tp = topo_list_next(&thp->th_trees); tp != NULL;
225	    tp = topo_list_next(tp)) {
226		if (strcmp(scheme, tp->tt_scheme) == 0)
227			return (tp->tt_root);
228	}
229
230	return (NULL);
231}
232
233/*
234 * buf_append -- Append str to buf (if it's non-NULL).  Place prepend
235 * in buf in front of str and append behind it (if they're non-NULL).
236 * Continue to update size even if we run out of space to actually
237 * stuff characters in the buffer.
238 */
239void
240topo_fmristr_build(ssize_t *sz, char *buf, size_t buflen, char *str,
241    char *prepend, char *append)
242{
243	ssize_t left;
244
245	if (str == NULL)
246		return;
247
248	if (buflen == 0 || (left = buflen - *sz) < 0)
249		left = 0;
250
251	if (buf != NULL && left != 0)
252		buf += *sz;
253
254	if (prepend == NULL && append == NULL)
255		*sz += snprintf(buf, left, "%s", str);
256	else if (append == NULL)
257		*sz += snprintf(buf, left, "%s%s", prepend, str);
258	else if (prepend == NULL)
259		*sz += snprintf(buf, left, "%s%s", str, append);
260	else
261		*sz += snprintf(buf, left, "%s%s%s", prepend, str, append);
262}
263
264#define	TOPO_PLATFORM_PATH	"%s/usr/platform/%s/lib/fm/topo/%s"
265#define	TOPO_COMMON_PATH	"%s/usr/lib/fm/topo/%s"
266
267char *
268topo_search_path(topo_mod_t *mod, const char *rootdir, const char *file)
269{
270	char *pp, sp[PATH_MAX];
271	topo_hdl_t *thp = mod->tm_hdl;
272
273	/*
274	 * Search for file name in order of platform, machine and common
275	 * topo directories
276	 */
277	(void) snprintf(sp, PATH_MAX, TOPO_PLATFORM_PATH, rootdir,
278	    thp->th_platform, file);
279	if (access(sp, F_OK) != 0) {
280		(void) snprintf(sp, PATH_MAX, TOPO_PLATFORM_PATH,
281		    thp->th_rootdir, thp->th_machine, file);
282		if (access(sp, F_OK) != 0) {
283			(void) snprintf(sp, PATH_MAX, TOPO_COMMON_PATH,
284			    thp->th_rootdir, file);
285			if (access(sp, F_OK) != 0) {
286				return (NULL);
287			}
288		}
289	}
290
291	pp = topo_mod_strdup(mod, sp);
292
293	return (pp);
294}
295
296/*
297 * SMBIOS serial numbers can contain characters (particularly ':' and ' ')
298 * that are invalid for the authority and can break FMRI parsing.  We translate
299 * any invalid characters to a safe '-', as well as trimming any leading or
300 * trailing whitespace.  Similarly, '/' can be found in some product names
301 * so we translate that to '-'.
302 */
303char *
304topo_cleanup_auth_str(topo_hdl_t *thp, const char *begin)
305{
306	char buf[MAXNAMELEN];
307	const char *end, *cp;
308	char *pp;
309	char c;
310	int i;
311
312	end = begin + strlen(begin);
313
314	while (begin < end && isspace(*begin))
315		begin++;
316	while (begin < end && isspace(*(end - 1)))
317		end--;
318
319	if (begin >= end)
320		return (NULL);
321
322	cp = begin;
323	for (i = 0; i < MAXNAMELEN - 1; i++) {
324		if (cp >= end)
325			break;
326		c = *cp;
327		if (c == ':' || c == '=' || c == '/' || isspace(c) ||
328		    !isprint(c))
329			buf[i] = '-';
330		else
331			buf[i] = c;
332		cp++;
333	}
334	buf[i] = 0;
335
336	pp = topo_hdl_strdup(thp, buf);
337	return (pp);
338}
339
340void
341topo_sensor_type_name(uint32_t type, char *buf, size_t len)
342{
343	topo_name_trans_t *ntp;
344
345	for (ntp = &topo_sensor_type_table[0]; ntp->int_name != NULL; ntp++) {
346		if (ntp->int_value == type) {
347			(void) strlcpy(buf, ntp->int_name, len);
348			return;
349		}
350	}
351
352	(void) snprintf(buf, len, "0x%02x", type);
353}
354
355void
356topo_sensor_units_name(uint8_t type, char *buf, size_t len)
357{
358	topo_name_trans_t *ntp;
359
360	for (ntp = &topo_units_type_table[0]; ntp->int_name != NULL; ntp++) {
361		if (ntp->int_value == type) {
362			(void) strlcpy(buf, ntp->int_name, len);
363			return;
364		}
365	}
366
367	(void) snprintf(buf, len, "0x%02x", type);
368}
369
370void
371topo_led_type_name(uint8_t type, char *buf, size_t len)
372{
373	topo_name_trans_t *ntp;
374
375	for (ntp = &topo_led_type_table[0]; ntp->int_name != NULL; ntp++) {
376		if (ntp->int_value == type) {
377			(void) strlcpy(buf, ntp->int_name, len);
378			return;
379		}
380	}
381
382	(void) snprintf(buf, len, "0x%02x", type);
383}
384
385void
386topo_led_state_name(uint8_t type, char *buf, size_t len)
387{
388	topo_name_trans_t *ntp;
389
390	for (ntp = &topo_led_states_table[0]; ntp->int_name != NULL; ntp++) {
391		if (ntp->int_value == type) {
392			(void) strlcpy(buf, ntp->int_name, len);
393			return;
394		}
395	}
396
397	(void) snprintf(buf, len, "0x%02x", type);
398}
399
400void
401topo_sensor_state_name(uint32_t sensor_type, uint8_t state, char *buf,
402    size_t len)
403{
404	topo_name_trans_t *ntp;
405
406	switch (sensor_type) {
407		case TOPO_SENSOR_TYPE_PHYSICAL:
408			ntp = &topo_sensor_states_physical_table[0];
409			break;
410		case TOPO_SENSOR_TYPE_PLATFORM:
411			ntp = &topo_sensor_states_platform_table[0];
412			break;
413		case TOPO_SENSOR_TYPE_PROCESSOR:
414			ntp = &topo_sensor_states_processor_table[0];
415			break;
416		case TOPO_SENSOR_TYPE_POWER_SUPPLY:
417			ntp = &topo_sensor_states_power_supply_table[0];
418			break;
419		case TOPO_SENSOR_TYPE_POWER_UNIT:
420			ntp = &topo_sensor_states_power_unit_table[0];
421			break;
422		case TOPO_SENSOR_TYPE_MEMORY:
423			ntp = &topo_sensor_states_memory_table[0];
424			break;
425		case TOPO_SENSOR_TYPE_BAY:
426			ntp = &topo_sensor_states_bay_table[0];
427			break;
428		case TOPO_SENSOR_TYPE_FIRMWARE:
429			ntp = &topo_sensor_states_firmware_table[0];
430			break;
431		case TOPO_SENSOR_TYPE_EVENT_LOG:
432			ntp = &topo_sensor_states_event_log_table[0];
433			break;
434		case TOPO_SENSOR_TYPE_WATCHDOG1:
435			ntp = &topo_sensor_states_watchdog1_table[0];
436			break;
437		case TOPO_SENSOR_TYPE_SYSTEM:
438			ntp = &topo_sensor_states_system_table[0];
439			break;
440		case TOPO_SENSOR_TYPE_CRITICAL:
441			ntp = &topo_sensor_states_critical_table[0];
442			break;
443		case TOPO_SENSOR_TYPE_BUTTON:
444			ntp = &topo_sensor_states_button_table[0];
445			break;
446		case TOPO_SENSOR_TYPE_CABLE:
447			ntp = &topo_sensor_states_cable_table[0];
448			break;
449		case TOPO_SENSOR_TYPE_BOOT_STATE:
450			ntp = &topo_sensor_states_boot_state_table[0];
451			break;
452		case TOPO_SENSOR_TYPE_BOOT_ERROR:
453			ntp = &topo_sensor_states_boot_error_table[0];
454			break;
455		case TOPO_SENSOR_TYPE_BOOT_OS:
456			ntp = &topo_sensor_states_boot_os_table[0];
457			break;
458		case TOPO_SENSOR_TYPE_OS_SHUTDOWN:
459			ntp = &topo_sensor_states_os_table[0];
460			break;
461		case TOPO_SENSOR_TYPE_SLOT:
462			ntp = &topo_sensor_states_slot_table[0];
463			break;
464		case TOPO_SENSOR_TYPE_ACPI:
465			ntp = &topo_sensor_states_acpi_table[0];
466			break;
467		case TOPO_SENSOR_TYPE_WATCHDOG2:
468			ntp = &topo_sensor_states_watchdog2_table[0];
469			break;
470		case TOPO_SENSOR_TYPE_ALERT:
471			ntp = &topo_sensor_states_alert_table[0];
472			break;
473		case TOPO_SENSOR_TYPE_PRESENCE:
474			ntp = &topo_sensor_states_presence_table[0];
475			break;
476		case TOPO_SENSOR_TYPE_LAN:
477			ntp = &topo_sensor_states_lan_table[0];
478			break;
479		case TOPO_SENSOR_TYPE_HEALTH:
480			ntp = &topo_sensor_states_health_table[0];
481			break;
482		case TOPO_SENSOR_TYPE_BATTERY:
483			ntp = &topo_sensor_states_battery_table[0];
484			break;
485		case TOPO_SENSOR_TYPE_AUDIT:
486			ntp = &topo_sensor_states_audit_table[0];
487			break;
488		case TOPO_SENSOR_TYPE_VERSION:
489			ntp = &topo_sensor_states_version_table[0];
490			break;
491		case TOPO_SENSOR_TYPE_FRU_STATE:
492			ntp = &topo_sensor_states_fru_state_table[0];
493			break;
494		case TOPO_SENSOR_TYPE_THRESHOLD_STATE:
495			ntp = &topo_sensor_states_thresh_table[0];
496			break;
497		case TOPO_SENSOR_TYPE_GENERIC_USAGE:
498			ntp = &topo_sensor_states_generic_usage_table[0];
499			break;
500		case TOPO_SENSOR_TYPE_GENERIC_STATE:
501			ntp = &topo_sensor_states_generic_state_table[0];
502			break;
503		case TOPO_SENSOR_TYPE_GENERIC_PREDFAIL:
504			ntp = &topo_sensor_states_generic_predfail_table[0];
505			break;
506		case TOPO_SENSOR_TYPE_GENERIC_LIMIT:
507			ntp = &topo_sensor_states_generic_limit_table[0];
508			break;
509		case TOPO_SENSOR_TYPE_GENERIC_PERFORMANCE:
510			ntp = &topo_sensor_states_generic_perf_table[0];
511			break;
512		case TOPO_SENSOR_TYPE_SEVERITY:
513			ntp = &topo_sensor_states_severity_table[0];
514			break;
515		case TOPO_SENSOR_TYPE_GENERIC_PRESENCE:
516			ntp = &topo_sensor_states_generic_presence_table[0];
517			break;
518		case TOPO_SENSOR_TYPE_GENERIC_AVAILABILITY:
519			ntp = &topo_sensor_states_generic_avail_table[0];
520			break;
521		case TOPO_SENSOR_TYPE_GENERIC_STATUS:
522			ntp = &topo_sensor_states_generic_status_table[0];
523			break;
524		case TOPO_SENSOR_TYPE_GENERIC_ACPI:
525			ntp = &topo_sensor_states_generic_acpi_pwr_table[0];
526			break;
527		case TOPO_SENSOR_TYPE_GENERIC_FAILURE:
528			ntp = &topo_sensor_states_generic_failure_table[0];
529			break;
530		case TOPO_SENSOR_TYPE_GENERIC_OK:
531			ntp = &topo_sensor_states_generic_ok_table[0];
532			break;
533		default:
534			(void) snprintf(buf, len, "0x%02x", state);
535			return;
536	}
537	if (state == 0) {
538		(void) snprintf(buf, len, "NO_STATES_ASSERTED");
539		return;
540	}
541	buf[0] = '\0';
542	for (; ntp->int_name != NULL; ntp++) {
543		if (state & ntp->int_value) {
544			if (buf[0] != '\0')
545				(void) strlcat(buf, "|", len);
546			(void) strlcat(buf, ntp->int_name, len);
547		}
548	}
549
550	if (buf[0] == '\0')
551		(void) snprintf(buf, len, "0x%02x", state);
552}
553
554static const topo_pgroup_info_t sys_pgroup = {
555	TOPO_PGROUP_SYSTEM,
556	TOPO_STABILITY_PRIVATE,
557	TOPO_STABILITY_PRIVATE,
558	1
559};
560static const topo_pgroup_info_t auth_pgroup = {
561	FM_FMRI_AUTHORITY,
562	TOPO_STABILITY_PRIVATE,
563	TOPO_STABILITY_PRIVATE,
564	1
565};
566
567void
568topo_pgroup_hcset(tnode_t *node, nvlist_t *auth)
569{
570	int err;
571	char isa[MAXNAMELEN];
572	struct utsname uts;
573	char *prod, *psn, *csn, *server;
574
575	if (auth == NULL)
576		return;
577
578	if (topo_pgroup_create(node, &auth_pgroup, &err) != 0) {
579		if (err != ETOPO_PROP_DEFD)
580			return;
581	}
582
583	/*
584	 * Inherit if we can, it saves memory
585	 */
586	if ((topo_prop_inherit(node, FM_FMRI_AUTHORITY, FM_FMRI_AUTH_PRODUCT,
587	    &err) != 0) && (err != ETOPO_PROP_DEFD)) {
588		if (nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT, &prod) ==
589		    0)
590			(void) topo_prop_set_string(node, FM_FMRI_AUTHORITY,
591			    FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, prod,
592			    &err);
593	}
594	if ((topo_prop_inherit(node, FM_FMRI_AUTHORITY, FM_FMRI_AUTH_PRODUCT_SN,
595	    &err) != 0) && (err != ETOPO_PROP_DEFD)) {
596		if (nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT_SN, &psn) ==
597		    0)
598			(void) topo_prop_set_string(node, FM_FMRI_AUTHORITY,
599			    FM_FMRI_AUTH_PRODUCT_SN, TOPO_PROP_IMMUTABLE, psn,
600			    &err);
601	}
602	if ((topo_prop_inherit(node, FM_FMRI_AUTHORITY, FM_FMRI_AUTH_CHASSIS,
603	    &err) != 0) && (err != ETOPO_PROP_DEFD)) {
604		if (nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS, &csn) == 0)
605			(void) topo_prop_set_string(node, FM_FMRI_AUTHORITY,
606			    FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, csn,
607			    &err);
608	}
609	if ((topo_prop_inherit(node, FM_FMRI_AUTHORITY, FM_FMRI_AUTH_SERVER,
610	    &err) != 0) && (err != ETOPO_PROP_DEFD)) {
611		if (nvlist_lookup_string(auth, FM_FMRI_AUTH_SERVER, &server) ==
612		    0)
613			(void) topo_prop_set_string(node, FM_FMRI_AUTHORITY,
614			    FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, server,
615			    &err);
616	}
617
618	if (topo_pgroup_create(node, &sys_pgroup, &err) != 0)
619		return;
620
621	if (sysinfo(SI_ARCHITECTURE, isa, sizeof (isa)) != -1)
622		(void) topo_prop_set_string(node, TOPO_PGROUP_SYSTEM,
623		    TOPO_PROP_ISA, TOPO_PROP_IMMUTABLE, isa, &err);
624
625	if (uname(&uts) != -1)
626		(void) topo_prop_set_string(node, TOPO_PGROUP_SYSTEM,
627		    TOPO_PROP_MACHINE, TOPO_PROP_IMMUTABLE, uts.machine, &err);
628}
629