1/*	$OpenBSD: sensors.c,v 1.32 2020/07/15 07:13:56 kettenis Exp $	*/
2
3/*
4 * Copyright (c) 2007 Deanna Phillips <deanna@openbsd.org>
5 * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
6 * Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 *
20 */
21
22#include <sys/types.h>
23#include <sys/signal.h>
24#include <sys/sysctl.h>
25#include <sys/sensors.h>
26
27#include <err.h>
28#include <errno.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include "systat.h"
33
34struct sensor sensor;
35struct sensordev sensordev;
36
37struct sensinfo {
38	int sn_dev;
39	struct sensor sn_sensor;
40};
41#define sn_type sn_sensor.type
42#define sn_numt sn_sensor.numt
43#define sn_desc sn_sensor.desc
44#define sn_status sn_sensor.status
45#define sn_value sn_sensor.value
46
47#define SYSTAT_MAXSENSORDEVICES 1024
48char *devnames[SYSTAT_MAXSENSORDEVICES];
49
50#define ADD_ALLOC 100
51static size_t sensor_cnt = 0;
52static size_t num_alloc = 0;
53static struct sensinfo *sensors = NULL;
54
55static char *fmttime(double);
56static void showsensor(struct sensinfo *s);
57
58void print_sn(void);
59int read_sn(void);
60int select_sn(void);
61
62const char *drvstat[] = {
63	NULL,
64	"empty", "ready", "powering up", "online", "idle", "active",
65	"rebuilding", "powering down", "failed", "degraded"
66};
67
68
69field_def fields_sn[] = {
70	{"SENSOR", 16, 32, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
71	{"VALUE", 16, 20, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
72	{"STATUS", 5, 8, 1, FLD_ALIGN_CENTER, -1, 0, 0, 0},
73	{"DESCRIPTION", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0}
74};
75
76#define FLD_SN_SENSOR	FIELD_ADDR(fields_sn,0)
77#define FLD_SN_VALUE	FIELD_ADDR(fields_sn,1)
78#define FLD_SN_STATUS	FIELD_ADDR(fields_sn,2)
79#define FLD_SN_DESCR	FIELD_ADDR(fields_sn,3)
80
81/* Define views */
82field_def *view_sn_0[] = {
83	FLD_SN_SENSOR, FLD_SN_VALUE, FLD_SN_STATUS, FLD_SN_DESCR, NULL
84};
85
86
87/* Define view managers */
88struct view_manager sensors_mgr = {
89	"Sensors", select_sn, read_sn, NULL, print_header,
90	print_sn, keyboard_callback, NULL, NULL
91};
92
93field_view views_sn[] = {
94	{view_sn_0, "sensors", '3', &sensors_mgr},
95	{NULL, NULL, 0, NULL}
96};
97
98struct sensinfo *
99next_sn(void)
100{
101	if (num_alloc <= sensor_cnt) {
102		struct sensinfo *s;
103		size_t a = num_alloc + ADD_ALLOC;
104		if (a < num_alloc)
105			return NULL;
106		s = reallocarray(sensors, a, sizeof(struct sensinfo));
107		if (s == NULL)
108			return NULL;
109		sensors = s;
110		num_alloc = a;
111	}
112
113	return &sensors[sensor_cnt++];
114}
115
116
117int
118select_sn(void)
119{
120	num_disp = sensor_cnt;
121	return (0);
122}
123
124int
125read_sn(void)
126{
127	enum sensor_type type;
128	size_t		 slen, sdlen;
129	int		 mib[5], dev, numt;
130	struct sensinfo	*s;
131
132	mib[0] = CTL_HW;
133	mib[1] = HW_SENSORS;
134
135	sensor_cnt = 0;
136
137	for (dev = 0; dev < SYSTAT_MAXSENSORDEVICES; dev++) {
138		mib[2] = dev;
139		sdlen = sizeof(struct sensordev);
140		if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
141			if (errno == ENOENT)
142				break;
143			if (errno == ENXIO)
144				continue;
145			error("sysctl: %s", strerror(errno));
146		}
147
148		if (devnames[dev] && strcmp(devnames[dev], sensordev.xname)) {
149			free(devnames[dev]);
150			devnames[dev] = NULL;
151		}
152		if (devnames[dev] == NULL)
153			devnames[dev] = strdup(sensordev.xname);
154
155		for (type = 0; type < SENSOR_MAX_TYPES; type++) {
156			mib[3] = type;
157			for (numt = 0; numt < sensordev.maxnumt[type]; numt++) {
158				mib[4] = numt;
159				slen = sizeof(struct sensor);
160				if (sysctl(mib, 5, &sensor, &slen, NULL, 0)
161				    == -1) {
162					if (errno != ENOENT)
163						error("sysctl: %s", strerror(errno));
164					continue;
165				}
166				if (sensor.flags & SENSOR_FINVALID)
167					continue;
168
169				s = next_sn();
170				s->sn_sensor = sensor;
171				s->sn_dev = dev;
172			}
173		}
174	}
175
176	num_disp = sensor_cnt;
177	return 0;
178}
179
180
181void
182print_sn(void)
183{
184	int n, count = 0;
185
186	for (n = dispstart; n < num_disp; n++) {
187		showsensor(sensors + n);
188		count++;
189		if (maxprint > 0 && count >= maxprint)
190			break;
191	}
192}
193
194int
195initsensors(void)
196{
197	field_view *v;
198
199	memset(devnames, 0, sizeof(devnames));
200
201	for (v = views_sn; v->name != NULL; v++)
202		add_view(v);
203
204	return(1);
205}
206
207static void
208showsensor(struct sensinfo *s)
209{
210	tb_start();
211	tbprintf("%s.%s%d", devnames[s->sn_dev],
212		 sensor_type_s[s->sn_type], s->sn_numt);
213	print_fld_tb(FLD_SN_SENSOR);
214
215	if (s->sn_desc[0] != '\0')
216		print_fld_str(FLD_SN_DESCR, s->sn_desc);
217
218	tb_start();
219
220	switch (s->sn_type) {
221	case SENSOR_TEMP:
222		tbprintf("%10.2f degC",
223		    (s->sn_value - 273150000) / 1000000.0);
224		break;
225	case SENSOR_FANRPM:
226		tbprintf("%11lld RPM", s->sn_value);
227		break;
228	case SENSOR_VOLTS_DC:
229		tbprintf("%10.2f V DC",
230		    s->sn_value / 1000000.0);
231		break;
232	case SENSOR_VOLTS_AC:
233		tbprintf("%10.2f V AC",
234		    s->sn_value / 1000000.0);
235		break;
236	case SENSOR_OHMS:
237		tbprintf("%11lld ohm", s->sn_value);
238		break;
239	case SENSOR_WATTS:
240		tbprintf("%10.2f W", s->sn_value / 1000000.0);
241		break;
242	case SENSOR_AMPS:
243		tbprintf("%10.2f A", s->sn_value / 1000000.0);
244		break;
245	case SENSOR_WATTHOUR:
246		tbprintf("%12.2f Wh", s->sn_value / 1000000.0);
247		break;
248	case SENSOR_AMPHOUR:
249		tbprintf("%10.2f Ah", s->sn_value / 1000000.0);
250		break;
251	case SENSOR_INDICATOR:
252		tbprintf("%15s", s->sn_value ? "On" : "Off");
253		break;
254	case SENSOR_INTEGER:
255		tbprintf("%11lld raw", s->sn_value);
256		break;
257	case SENSOR_PERCENT:
258		tbprintf("%14.2f%%", s->sn_value / 1000.0);
259		break;
260	case SENSOR_LUX:
261		tbprintf("%15.2f lx", s->sn_value / 1000000.0);
262		break;
263	case SENSOR_DRIVE:
264		if (0 < s->sn_value &&
265		    s->sn_value < sizeof(drvstat)/sizeof(drvstat[0])) {
266			tbprintf("%15s", drvstat[s->sn_value]);
267			break;
268		}
269		break;
270	case SENSOR_TIMEDELTA:
271		tbprintf("%15s", fmttime(s->sn_value / 1000000000.0));
272		break;
273	case SENSOR_HUMIDITY:
274		tbprintf("%3.2f%%", s->sn_value / 1000.0);
275		break;
276	case SENSOR_FREQ:
277		tbprintf("%11.2f Hz", s->sn_value / 1000000.0);
278		break;
279	case SENSOR_ANGLE:
280		tbprintf("%3.4f degrees", s->sn_value / 1000000.0);
281		break;
282	case SENSOR_DISTANCE:
283		tbprintf("%.3f m", s->sn_value / 1000000.0);
284		break;
285	case SENSOR_PRESSURE:
286		tbprintf("%.2f Pa", s->sn_value / 1000.0);
287		break;
288	case SENSOR_ACCEL:
289		tbprintf("%2.4f m/s^2", s->sn_value / 1000000.0);
290		break;
291	case SENSOR_VELOCITY:
292		tbprintf("%4.3f m/s", s->sn_value / 1000000.0);
293		break;
294	case SENSOR_ENERGY:
295		tbprintf("%.2f J", s->sn_value / 1000000.0);
296		break;
297	default:
298		tbprintf("%10lld", s->sn_value);
299		break;
300	}
301
302	print_fld_tb(FLD_SN_VALUE);
303
304	switch (s->sn_status) {
305	case SENSOR_S_UNSPEC:
306		break;
307	case SENSOR_S_UNKNOWN:
308		print_fld_str(FLD_SN_STATUS, "unknown");
309		break;
310	case SENSOR_S_WARN:
311		print_fld_str(FLD_SN_STATUS, "WARNING");
312		break;
313	case SENSOR_S_CRIT:
314		print_fld_str(FLD_SN_STATUS, "CRITICAL");
315		break;
316	case SENSOR_S_OK:
317		print_fld_str(FLD_SN_STATUS, "OK");
318		break;
319	}
320	end_line();
321}
322
323#define SECS_PER_DAY 86400
324#define SECS_PER_HOUR 3600
325#define SECS_PER_MIN 60
326
327static char *
328fmttime(double in)
329{
330	int signbit = 1;
331	int tiny = 0;
332	char *unit;
333#define LEN 32
334	static char outbuf[LEN];
335
336	if (in < 0){
337		signbit = -1;
338		in *= -1;
339	}
340
341	if (in >= SECS_PER_DAY ){
342		unit = "days";
343		in /= SECS_PER_DAY;
344	} else if (in >= SECS_PER_HOUR ){
345		unit = "hr";
346		in /= SECS_PER_HOUR;
347	} else if (in >= SECS_PER_MIN ){
348		unit = "min";
349		in /= SECS_PER_MIN;
350	} else if (in >= 1 ){
351		unit = "s";
352		/* in *= 1; */ /* no op */
353	} else if (in == 0 ){ /* direct comparisons to floats are scary */
354		unit = "s";
355	} else if (in >= 1e-3 ){
356		unit = "ms";
357		in *= 1e3;
358	} else if (in >= 1e-6 ){
359		unit = "us";
360		in *= 1e6;
361	} else if (in >= 1e-9 ){
362		unit = "ns";
363		in *= 1e9;
364	} else {
365		unit = "ps";
366		if (in < 1e-13)
367			tiny = 1;
368		in *= 1e12;
369	}
370
371	snprintf(outbuf, LEN,
372	    tiny ? "%s%f %s" : "%s%.3f %s",
373	    signbit == -1 ? "-" : "", in, unit);
374
375	return outbuf;
376}
377