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