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