xref: /openbsd-src/usr.bin/systat/sensors.c (revision d651af2f0d36423a1e0fb41c78b959d61bcc4319)
1*d651af2fSmatthieu /*	$OpenBSD: sensors.c,v 1.33 2024/11/08 08:45:47 matthieu Exp $	*/
2ae112e8dSdeanna 
3d1eaec8dSdeanna /*
4d1eaec8dSdeanna  * Copyright (c) 2007 Deanna Phillips <deanna@openbsd.org>
5d1eaec8dSdeanna  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
6fd5655b8Sderaadt  * Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
7d1eaec8dSdeanna  *
8d1eaec8dSdeanna  * Permission to use, copy, modify, and distribute this software for any
9d1eaec8dSdeanna  * purpose with or without fee is hereby granted, provided that the above
10d1eaec8dSdeanna  * copyright notice and this permission notice appear in all copies.
11d1eaec8dSdeanna  *
12d1eaec8dSdeanna  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13d1eaec8dSdeanna  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14d1eaec8dSdeanna  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15d1eaec8dSdeanna  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16d1eaec8dSdeanna  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17d1eaec8dSdeanna  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18d1eaec8dSdeanna  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19d1eaec8dSdeanna  *
20d1eaec8dSdeanna  */
21d1eaec8dSdeanna 
228f6b3bafSderaadt #include <sys/types.h>
238f6b3bafSderaadt #include <sys/signal.h>
24d1eaec8dSdeanna #include <sys/sysctl.h>
25d1eaec8dSdeanna #include <sys/sensors.h>
26d1eaec8dSdeanna 
27d1eaec8dSdeanna #include <err.h>
28d1eaec8dSdeanna #include <errno.h>
29d1eaec8dSdeanna #include <stdio.h>
30d1eaec8dSdeanna #include <stdlib.h>
3173baed14Scanacar #include <string.h>
32*d651af2fSmatthieu #include <util.h>
33d1eaec8dSdeanna #include "systat.h"
34d1eaec8dSdeanna 
35d1eaec8dSdeanna struct sensor sensor;
36d1eaec8dSdeanna struct sensordev sensordev;
3773baed14Scanacar 
3873baed14Scanacar struct sensinfo {
3973baed14Scanacar 	int sn_dev;
4073baed14Scanacar 	struct sensor sn_sensor;
4173baed14Scanacar };
4273baed14Scanacar #define sn_type sn_sensor.type
4373baed14Scanacar #define sn_numt sn_sensor.numt
4473baed14Scanacar #define sn_desc sn_sensor.desc
4573baed14Scanacar #define sn_status sn_sensor.status
4673baed14Scanacar #define sn_value sn_sensor.value
4773baed14Scanacar 
48f7811f45Sderaadt #define SYSTAT_MAXSENSORDEVICES 1024
49f7811f45Sderaadt char *devnames[SYSTAT_MAXSENSORDEVICES];
5073baed14Scanacar 
5173baed14Scanacar #define ADD_ALLOC 100
5273baed14Scanacar static size_t sensor_cnt = 0;
5373baed14Scanacar static size_t num_alloc = 0;
5473baed14Scanacar static struct sensinfo *sensors = NULL;
5573baed14Scanacar 
56fee947f3Sckuethe static char *fmttime(double);
5773baed14Scanacar static void showsensor(struct sensinfo *s);
58d1eaec8dSdeanna 
5973baed14Scanacar void print_sn(void);
6073baed14Scanacar int read_sn(void);
6173baed14Scanacar int select_sn(void);
62d1eaec8dSdeanna 
63d1eaec8dSdeanna const char *drvstat[] = {
642ce90493Sderaadt 	NULL,
6527d0e24aSokan 	"empty", "ready", "powering up", "online", "idle", "active",
6627d0e24aSokan 	"rebuilding", "powering down", "failed", "degraded"
67d1eaec8dSdeanna };
68d1eaec8dSdeanna 
6973baed14Scanacar 
7073baed14Scanacar field_def fields_sn[] = {
7173baed14Scanacar 	{"SENSOR", 16, 32, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
7273baed14Scanacar 	{"VALUE", 16, 20, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
7373baed14Scanacar 	{"STATUS", 5, 8, 1, FLD_ALIGN_CENTER, -1, 0, 0, 0},
7473baed14Scanacar 	{"DESCRIPTION", 20, 45, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0}
7573baed14Scanacar };
7673baed14Scanacar 
77596a8091Sjasper #define FLD_SN_SENSOR	FIELD_ADDR(fields_sn,0)
78596a8091Sjasper #define FLD_SN_VALUE	FIELD_ADDR(fields_sn,1)
79596a8091Sjasper #define FLD_SN_STATUS	FIELD_ADDR(fields_sn,2)
80596a8091Sjasper #define FLD_SN_DESCR	FIELD_ADDR(fields_sn,3)
8173baed14Scanacar 
8273baed14Scanacar /* Define views */
8373baed14Scanacar field_def *view_sn_0[] = {
8473baed14Scanacar 	FLD_SN_SENSOR, FLD_SN_VALUE, FLD_SN_STATUS, FLD_SN_DESCR, NULL
8573baed14Scanacar };
8673baed14Scanacar 
8773baed14Scanacar 
8873baed14Scanacar /* Define view managers */
8973baed14Scanacar struct view_manager sensors_mgr = {
9073baed14Scanacar 	"Sensors", select_sn, read_sn, NULL, print_header,
9173baed14Scanacar 	print_sn, keyboard_callback, NULL, NULL
9273baed14Scanacar };
9373baed14Scanacar 
9473baed14Scanacar field_view views_sn[] = {
9573baed14Scanacar 	{view_sn_0, "sensors", '3', &sensors_mgr},
9673baed14Scanacar 	{NULL, NULL, 0, NULL}
9773baed14Scanacar };
9873baed14Scanacar 
9973baed14Scanacar struct sensinfo *
10073baed14Scanacar next_sn(void)
101d1eaec8dSdeanna {
10273baed14Scanacar 	if (num_alloc <= sensor_cnt) {
10373baed14Scanacar 		struct sensinfo *s;
10473baed14Scanacar 		size_t a = num_alloc + ADD_ALLOC;
10573baed14Scanacar 		if (a < num_alloc)
10673baed14Scanacar 			return NULL;
1077e81aea1Sdoug 		s = reallocarray(sensors, a, sizeof(struct sensinfo));
10873baed14Scanacar 		if (s == NULL)
10973baed14Scanacar 			return NULL;
11073baed14Scanacar 		sensors = s;
11173baed14Scanacar 		num_alloc = a;
11273baed14Scanacar 	}
11373baed14Scanacar 
11473baed14Scanacar 	return &sensors[sensor_cnt++];
11573baed14Scanacar }
11673baed14Scanacar 
11773baed14Scanacar 
11873baed14Scanacar int
11973baed14Scanacar select_sn(void)
12073baed14Scanacar {
12173baed14Scanacar 	num_disp = sensor_cnt;
12273baed14Scanacar 	return (0);
12373baed14Scanacar }
12473baed14Scanacar 
12573baed14Scanacar int
12673baed14Scanacar read_sn(void)
12773baed14Scanacar {
12873baed14Scanacar 	enum sensor_type type;
12973baed14Scanacar 	size_t		 slen, sdlen;
13073baed14Scanacar 	int		 mib[5], dev, numt;
13173baed14Scanacar 	struct sensinfo	*s;
13273baed14Scanacar 
13373baed14Scanacar 	mib[0] = CTL_HW;
13473baed14Scanacar 	mib[1] = HW_SENSORS;
13573baed14Scanacar 
13673baed14Scanacar 	sensor_cnt = 0;
13773baed14Scanacar 
138f7811f45Sderaadt 	for (dev = 0; dev < SYSTAT_MAXSENSORDEVICES; dev++) {
13973baed14Scanacar 		mib[2] = dev;
14073baed14Scanacar 		sdlen = sizeof(struct sensordev);
14173baed14Scanacar 		if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
142f7811f45Sderaadt 			if (errno == ENOENT)
143f7811f45Sderaadt 				break;
144f7811f45Sderaadt 			if (errno == ENXIO)
14573baed14Scanacar 				continue;
146f7811f45Sderaadt 			error("sysctl: %s", strerror(errno));
14773baed14Scanacar 		}
14873baed14Scanacar 
14973baed14Scanacar 		if (devnames[dev] && strcmp(devnames[dev], sensordev.xname)) {
15073baed14Scanacar 			free(devnames[dev]);
15173baed14Scanacar 			devnames[dev] = NULL;
15273baed14Scanacar 		}
15373baed14Scanacar 		if (devnames[dev] == NULL)
15473baed14Scanacar 			devnames[dev] = strdup(sensordev.xname);
15573baed14Scanacar 
15673baed14Scanacar 		for (type = 0; type < SENSOR_MAX_TYPES; type++) {
15773baed14Scanacar 			mib[3] = type;
15873baed14Scanacar 			for (numt = 0; numt < sensordev.maxnumt[type]; numt++) {
15973baed14Scanacar 				mib[4] = numt;
16073baed14Scanacar 				slen = sizeof(struct sensor);
16173baed14Scanacar 				if (sysctl(mib, 5, &sensor, &slen, NULL, 0)
16273baed14Scanacar 				    == -1) {
16373baed14Scanacar 					if (errno != ENOENT)
16473baed14Scanacar 						error("sysctl: %s", strerror(errno));
16573baed14Scanacar 					continue;
16673baed14Scanacar 				}
16773baed14Scanacar 				if (sensor.flags & SENSOR_FINVALID)
16873baed14Scanacar 					continue;
16973baed14Scanacar 
17073baed14Scanacar 				s = next_sn();
17173baed14Scanacar 				s->sn_sensor = sensor;
17273baed14Scanacar 				s->sn_dev = dev;
17373baed14Scanacar 			}
17473baed14Scanacar 		}
17573baed14Scanacar 	}
17673baed14Scanacar 
17773baed14Scanacar 	num_disp = sensor_cnt;
17873baed14Scanacar 	return 0;
17973baed14Scanacar }
18073baed14Scanacar 
18173baed14Scanacar 
18273baed14Scanacar void
18373baed14Scanacar print_sn(void)
18473baed14Scanacar {
18573baed14Scanacar 	int n, count = 0;
18673baed14Scanacar 
18773baed14Scanacar 	for (n = dispstart; n < num_disp; n++) {
18873baed14Scanacar 		showsensor(sensors + n);
18973baed14Scanacar 		count++;
19073baed14Scanacar 		if (maxprint > 0 && count >= maxprint)
19173baed14Scanacar 			break;
19273baed14Scanacar 	}
193d1eaec8dSdeanna }
194d1eaec8dSdeanna 
195d1eaec8dSdeanna int
196d1eaec8dSdeanna initsensors(void)
197d1eaec8dSdeanna {
19873baed14Scanacar 	field_view *v;
19973baed14Scanacar 
20073baed14Scanacar 	memset(devnames, 0, sizeof(devnames));
20173baed14Scanacar 
20273baed14Scanacar 	for (v = views_sn; v->name != NULL; v++)
20373baed14Scanacar 		add_view(v);
20473baed14Scanacar 
205f4842697Ssteven 	return(1);
206d1eaec8dSdeanna }
207d1eaec8dSdeanna 
20873baed14Scanacar static void
20973baed14Scanacar showsensor(struct sensinfo *s)
210d1eaec8dSdeanna {
21173baed14Scanacar 	tb_start();
21273baed14Scanacar 	tbprintf("%s.%s%d", devnames[s->sn_dev],
21373baed14Scanacar 		 sensor_type_s[s->sn_type], s->sn_numt);
21473baed14Scanacar 	print_fld_tb(FLD_SN_SENSOR);
21573baed14Scanacar 
21673baed14Scanacar 	if (s->sn_desc[0] != '\0')
21773baed14Scanacar 		print_fld_str(FLD_SN_DESCR, s->sn_desc);
21873baed14Scanacar 
21973baed14Scanacar 	tb_start();
22073baed14Scanacar 
22173baed14Scanacar 	switch (s->sn_type) {
222d1eaec8dSdeanna 	case SENSOR_TEMP:
22373baed14Scanacar 		tbprintf("%10.2f degC",
22473baed14Scanacar 		    (s->sn_value - 273150000) / 1000000.0);
225d1eaec8dSdeanna 		break;
226d1eaec8dSdeanna 	case SENSOR_FANRPM:
22773baed14Scanacar 		tbprintf("%11lld RPM", s->sn_value);
228d1eaec8dSdeanna 		break;
2291c5be208Syuo 	case SENSOR_VOLTS_DC:
2301c5be208Syuo 		tbprintf("%10.2f V DC",
2311c5be208Syuo 		    s->sn_value / 1000000.0);
2321c5be208Syuo 		break;
233ea99e918Syuo 	case SENSOR_VOLTS_AC:
234ea99e918Syuo 		tbprintf("%10.2f V AC",
235ea99e918Syuo 		    s->sn_value / 1000000.0);
236ea99e918Syuo 		break;
2371c5be208Syuo 	case SENSOR_OHMS:
2381c5be208Syuo 		tbprintf("%11lld ohm", s->sn_value);
239d1eaec8dSdeanna 		break;
2400ae663d1Scnst 	case SENSOR_WATTS:
2410ae663d1Scnst 		tbprintf("%10.2f W", s->sn_value / 1000000.0);
2420ae663d1Scnst 		break;
243d1eaec8dSdeanna 	case SENSOR_AMPS:
24473baed14Scanacar 		tbprintf("%10.2f A", s->sn_value / 1000000.0);
245d1eaec8dSdeanna 		break;
2461c5be208Syuo 	case SENSOR_WATTHOUR:
2471c5be208Syuo 		tbprintf("%12.2f Wh", s->sn_value / 1000000.0);
2481c5be208Syuo 		break;
2491c5be208Syuo 	case SENSOR_AMPHOUR:
2501c5be208Syuo 		tbprintf("%10.2f Ah", s->sn_value / 1000000.0);
2511c5be208Syuo 		break;
252d1eaec8dSdeanna 	case SENSOR_INDICATOR:
25373baed14Scanacar 		tbprintf("%15s", s->sn_value ? "On" : "Off");
254d1eaec8dSdeanna 		break;
255d1eaec8dSdeanna 	case SENSOR_INTEGER:
25673baed14Scanacar 		tbprintf("%11lld raw", s->sn_value);
257d1eaec8dSdeanna 		break;
258d1eaec8dSdeanna 	case SENSOR_PERCENT:
25973baed14Scanacar 		tbprintf("%14.2f%%", s->sn_value / 1000.0);
260d1eaec8dSdeanna 		break;
261d1eaec8dSdeanna 	case SENSOR_LUX:
26273baed14Scanacar 		tbprintf("%15.2f lx", s->sn_value / 1000000.0);
263d1eaec8dSdeanna 		break;
264d1eaec8dSdeanna 	case SENSOR_DRIVE:
26573baed14Scanacar 		if (0 < s->sn_value &&
26649065c7dSderaadt 		    s->sn_value < sizeof(drvstat)/sizeof(drvstat[0])) {
26773baed14Scanacar 			tbprintf("%15s", drvstat[s->sn_value]);
268d1eaec8dSdeanna 			break;
269d1eaec8dSdeanna 		}
27002023423Sderaadt 		break;
271d1eaec8dSdeanna 	case SENSOR_TIMEDELTA:
27273baed14Scanacar 		tbprintf("%15s", fmttime(s->sn_value / 1000000000.0));
273d1eaec8dSdeanna 		break;
2749a3cef4fSyuo 	case SENSOR_HUMIDITY:
2759a3cef4fSyuo 		tbprintf("%3.2f%%", s->sn_value / 1000.0);
2769a3cef4fSyuo 		break;
2770bb7dfbaSoga 	case SENSOR_FREQ:
278*d651af2fSmatthieu 		if (humanreadable) {
279*d651af2fSmatthieu 			char buf[FMT_SCALED_STRSIZE];
280*d651af2fSmatthieu 			fmt_scaled(s->sn_value / 1000000.0, buf);
281*d651af2fSmatthieu 			tbprintf("%sHz", buf);
282*d651af2fSmatthieu 		} else
283ea99e918Syuo 			tbprintf("%11.2f Hz", s->sn_value / 1000000.0);
2840bb7dfbaSoga 		break;
2858e60fd2aSderaadt 	case SENSOR_ANGLE:
286028e7ea2Sderaadt 		tbprintf("%3.4f degrees", s->sn_value / 1000000.0);
2878e60fd2aSderaadt 		break;
288d95200b8Syuo 	case SENSOR_DISTANCE:
28931d62911Slandry 		tbprintf("%.3f m", s->sn_value / 1000000.0);
290d95200b8Syuo 		break;
291d95200b8Syuo 	case SENSOR_PRESSURE:
29242db9c7fSjasper 		tbprintf("%.2f Pa", s->sn_value / 1000.0);
293d95200b8Syuo 		break;
294d95200b8Syuo 	case SENSOR_ACCEL:
29542db9c7fSjasper 		tbprintf("%2.4f m/s^2", s->sn_value / 1000000.0);
296d95200b8Syuo 		break;
29731d62911Slandry 	case SENSOR_VELOCITY:
29831d62911Slandry 		tbprintf("%4.3f m/s", s->sn_value / 1000000.0);
29931d62911Slandry 		break;
300e8afce5bSkettenis 	case SENSOR_ENERGY:
301e8afce5bSkettenis 		tbprintf("%.2f J", s->sn_value / 1000000.0);
302e8afce5bSkettenis 		break;
303d1eaec8dSdeanna 	default:
30473baed14Scanacar 		tbprintf("%10lld", s->sn_value);
30502023423Sderaadt 		break;
306d1eaec8dSdeanna 	}
3072ce90493Sderaadt 
30873baed14Scanacar 	print_fld_tb(FLD_SN_VALUE);
30973baed14Scanacar 
31073baed14Scanacar 	switch (s->sn_status) {
311ae112e8dSdeanna 	case SENSOR_S_UNSPEC:
312ae112e8dSdeanna 		break;
313d1eaec8dSdeanna 	case SENSOR_S_UNKNOWN:
31473baed14Scanacar 		print_fld_str(FLD_SN_STATUS, "unknown");
315d1eaec8dSdeanna 		break;
316d1eaec8dSdeanna 	case SENSOR_S_WARN:
31773baed14Scanacar 		print_fld_str(FLD_SN_STATUS, "WARNING");
318d1eaec8dSdeanna 		break;
319d1eaec8dSdeanna 	case SENSOR_S_CRIT:
32073baed14Scanacar 		print_fld_str(FLD_SN_STATUS, "CRITICAL");
321d1eaec8dSdeanna 		break;
32202023423Sderaadt 	case SENSOR_S_OK:
32373baed14Scanacar 		print_fld_str(FLD_SN_STATUS, "OK");
324d1eaec8dSdeanna 		break;
325d1eaec8dSdeanna 	}
32673baed14Scanacar 	end_line();
327d1eaec8dSdeanna }
328fee947f3Sckuethe 
329fee947f3Sckuethe #define SECS_PER_DAY 86400
330fee947f3Sckuethe #define SECS_PER_HOUR 3600
331fee947f3Sckuethe #define SECS_PER_MIN 60
332fee947f3Sckuethe 
333fee947f3Sckuethe static char *
334fee947f3Sckuethe fmttime(double in)
335fee947f3Sckuethe {
336fee947f3Sckuethe 	int signbit = 1;
337fee947f3Sckuethe 	int tiny = 0;
338fee947f3Sckuethe 	char *unit;
339fee947f3Sckuethe #define LEN 32
340fee947f3Sckuethe 	static char outbuf[LEN];
341fee947f3Sckuethe 
342fee947f3Sckuethe 	if (in < 0){
343fee947f3Sckuethe 		signbit = -1;
344fee947f3Sckuethe 		in *= -1;
345fee947f3Sckuethe 	}
346fee947f3Sckuethe 
347fee947f3Sckuethe 	if (in >= SECS_PER_DAY ){
348fee947f3Sckuethe 		unit = "days";
349fee947f3Sckuethe 		in /= SECS_PER_DAY;
350fee947f3Sckuethe 	} else if (in >= SECS_PER_HOUR ){
351fee947f3Sckuethe 		unit = "hr";
352fee947f3Sckuethe 		in /= SECS_PER_HOUR;
353fee947f3Sckuethe 	} else if (in >= SECS_PER_MIN ){
354fee947f3Sckuethe 		unit = "min";
355fee947f3Sckuethe 		in /= SECS_PER_MIN;
356fee947f3Sckuethe 	} else if (in >= 1 ){
357cfc2f976Sckuethe 		unit = "s";
358fee947f3Sckuethe 		/* in *= 1; */ /* no op */
359f7ff0c41Sckuethe 	} else if (in == 0 ){ /* direct comparisons to floats are scary */
360cfc2f976Sckuethe 		unit = "s";
361fee947f3Sckuethe 	} else if (in >= 1e-3 ){
362cfc2f976Sckuethe 		unit = "ms";
363fee947f3Sckuethe 		in *= 1e3;
364fee947f3Sckuethe 	} else if (in >= 1e-6 ){
365cfc2f976Sckuethe 		unit = "us";
366fee947f3Sckuethe 		in *= 1e6;
367fee947f3Sckuethe 	} else if (in >= 1e-9 ){
368cfc2f976Sckuethe 		unit = "ns";
369fee947f3Sckuethe 		in *= 1e9;
370fee947f3Sckuethe 	} else {
371cfc2f976Sckuethe 		unit = "ps";
372fee947f3Sckuethe 		if (in < 1e-13)
373fee947f3Sckuethe 			tiny = 1;
374fee947f3Sckuethe 		in *= 1e12;
375fee947f3Sckuethe 	}
376fee947f3Sckuethe 
377fee947f3Sckuethe 	snprintf(outbuf, LEN,
37807fe0f2dScanacar 	    tiny ? "%s%f %s" : "%s%.3f %s",
379fee947f3Sckuethe 	    signbit == -1 ? "-" : "", in, unit);
380fee947f3Sckuethe 
381fee947f3Sckuethe 	return outbuf;
382fee947f3Sckuethe }
383