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