1 /* $NetBSD: envstat.c,v 1.11 2003/01/11 23:44:47 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2000 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Bill Squier. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 #ifndef lint 41 __RCSID("$NetBSD: envstat.c,v 1.11 2003/01/11 23:44:47 christos Exp $"); 42 #endif 43 44 #include <fcntl.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 #include <err.h> 50 #include <paths.h> 51 52 #include <sys/envsys.h> 53 #include <sys/ioctl.h> 54 55 const char E_CANTENUM[] = "cannot enumerate sensors"; 56 57 int main(int, char **); 58 void listsensors(envsys_basic_info_t *, int); 59 int numsensors(int); 60 int fillsensors(int, envsys_tre_data_t *, envsys_basic_info_t *, int); 61 int longestname(envsys_basic_info_t *, int); 62 int marksensors(envsys_basic_info_t *, int *, char *, int); 63 int strtosnum(envsys_basic_info_t *, const char *, int); 64 void header(unsigned, int, envsys_basic_info_t *, const int * const, int); 65 void values(unsigned, int, envsys_tre_data_t *, const int * const, int); 66 void usage(void); 67 68 int rflag = 0; 69 70 static const char *unit_str[] = {"degC", "RPM", "VAC", "V", "Ohms", "W", 71 "A", "Wh", "Ah", "bool", "Unk"}; 72 73 int 74 main(int argc, char **argv) 75 { 76 int c, fd, ns, ls, celsius; 77 unsigned int interval, width, headrep, headcnt; 78 envsys_tre_data_t *etds; 79 envsys_basic_info_t *ebis; 80 int *cetds; 81 char *sensors; 82 const char *dev; 83 84 fd = -1; 85 ls = 0; 86 celsius = 1; 87 interval = 0; 88 width = 0; 89 sensors = NULL; 90 headrep = 22; 91 92 while ((c = getopt(argc, argv, "cfi:ln:rs:w:r")) != -1) { 93 switch(c) { 94 case 'r': 95 rflag= 1; 96 break; 97 case 'i': /* wait time between displays */ 98 interval = atoi(optarg); 99 break; 100 case 'w': /* minimum column width */ 101 width = atoi(optarg); 102 break; 103 case 'l': /* list sensor names */ 104 ls = 1; 105 break; 106 case 'n': /* repeat header every headrep lines */ 107 headrep = atoi(optarg); 108 break; 109 case 's': /* restrict display to named sensors */ 110 sensors = (char *)malloc(strlen(optarg) + 1); 111 if (sensors == NULL) 112 exit(1); 113 strcpy(sensors, optarg); 114 break; 115 case 'f': /* display temp in degF */ 116 celsius = 0; 117 break; 118 case '?': 119 default: 120 usage(); 121 /* NOTREACHED */ 122 } 123 } 124 125 if (optind < argc) 126 dev = argv[optind]; 127 else 128 dev = _PATH_SYSMON; 129 130 if ((fd = open(dev, O_RDONLY)) == -1) 131 err(1, "unable to open %s", dev); 132 133 /* 134 * Determine number of sensors, allocate and fill arrays with 135 * initial information. Determine column width 136 */ 137 if ((ns = numsensors(fd)) <= 0) 138 errx(1, E_CANTENUM); 139 140 cetds = (int *)malloc(ns * sizeof(int)); 141 etds = (envsys_tre_data_t *)malloc(ns * sizeof(envsys_tre_data_t)); 142 ebis = (envsys_basic_info_t *)malloc(ns * sizeof(envsys_basic_info_t)); 143 144 if ((cetds == NULL) || (etds == NULL) || (ebis == NULL)) 145 errx(1, "cannot allocate memory"); 146 147 if (fillsensors(fd, etds, ebis, ns) == -1) 148 errx(1, E_CANTENUM); 149 150 if (ls) { 151 listsensors(ebis, ns); 152 exit(0); 153 } 154 155 156 #define MAX(x, y) (((x) > (y)) ? (x) : (y)) 157 if (!width) { 158 width = longestname(ebis, ns); 159 width = MAX(((79 - ns) / ns), width); 160 } 161 162 /* Mark which sensor numbers are to be displayed */ 163 if (marksensors(ebis, cetds, sensors, ns) == -1) 164 exit(1); 165 166 if (rflag) { 167 int i; 168 169 for (i = 0 ; i < ns ; i++) { 170 if (ebis[i].units == ENVSYS_INDICATOR) { 171 if (etds[i].cur.data_s) { 172 printf("%*.*s\n", 173 (int)width, 174 (int)width, 175 ebis[i].desc); 176 } 177 continue; 178 } 179 180 if (ebis[i].units == ENVSYS_INTEGER) { 181 printf("%*.*s:", (int)width, (int)width, 182 ebis[i].desc); 183 printf(" %10d\n", etds[i].cur.data_s); 184 continue; 185 } 186 187 printf("%*.*s:", (int)width, (int)width, ebis[i].desc); 188 /* different units need some magic */ 189 switch (ebis[i].units) 190 { 191 case ENVSYS_INDICATOR: 192 printf(" %10s", etds[i].cur.data_us ? 193 "ON" : "OFF"); 194 break; 195 case ENVSYS_STEMP: 196 { 197 double temp = 198 (etds[i].cur.data_s / 1000000.0) 199 - 273.15; 200 if (celsius) 201 printf(" %10.3f degC", 202 temp); 203 else 204 { 205 temp = (9.0 / 5.0) * temp + 32.0; 206 printf(" %10.3f degF", 207 temp); 208 } 209 } 210 break; 211 case ENVSYS_SFANRPM: 212 printf(" %10u RPM", 213 etds[i].cur.data_us); 214 break; 215 default: 216 printf(" %10.3f %s", 217 etds[i].cur.data_s / 1000000.0, 218 unit_str[ebis[i].units]); 219 break; 220 } 221 if (etds[i].validflags & ENVSYS_FFRACVALID) { 222 printf(" (%5.2f%%)", 223 (etds[i].cur.data_s * 100.0) / 224 etds[i].max.data_s); 225 } 226 227 printf("\n"); 228 } 229 exit(0); 230 } 231 232 233 234 /* If we didn't specify an interval value, print the sensors once */ 235 if (!interval) { 236 if (headrep) 237 header(width, celsius, ebis, cetds, ns); 238 values(width, celsius, etds, cetds, ns); 239 240 exit (0); 241 } 242 243 headcnt = 0; 244 if (headrep) 245 header(width, celsius, ebis, cetds, ns); 246 247 for (;;) { 248 values(width, celsius, etds, cetds, ns); 249 if (headrep && (++headcnt == headrep)) { 250 headcnt = 0; 251 header(width, celsius, ebis, cetds, ns); 252 } 253 254 sleep(interval); 255 256 if (fillsensors(fd, etds, ebis, ns) == -1) 257 errx(1, E_CANTENUM); 258 } 259 260 /* NOTREACHED */ 261 return (0); 262 } 263 264 265 /* 266 * pre: cetds[i] != 0 iff sensor i should appear in the output 267 * post: a column header line is displayed on stdout 268 */ 269 void 270 header(unsigned width, int celsius, envsys_basic_info_t *ebis, 271 const int * const cetds, int ns) 272 { 273 int i; 274 const char *s; 275 276 /* sensor names */ 277 for (i = 0; i < ns; ++i) 278 if (cetds[i]) 279 printf(" %*.*s", (int)width, (int)width, ebis[i].desc); 280 281 printf("\n"); 282 283 /* units */ 284 for (i = 0; i < ns; ++i) 285 if (cetds[i]) { 286 if ((ebis[i].units == ENVSYS_STEMP) && 287 !celsius) 288 s = "degF"; 289 else if (ebis[i].units >= ENVSYS_NSENSORS) 290 s = unit_str[ENVSYS_NSENSORS]; 291 else 292 s = unit_str[ebis[i].units]; 293 294 printf(" %*.*s", (int)width, (int)width, s); 295 } 296 printf("\n"); 297 } 298 299 void 300 values(unsigned width, int celsius, envsys_tre_data_t *etds, 301 const int * const cetds, int ns) 302 { 303 int i; 304 double temp; 305 306 for (i = 0; i < ns; ++i) 307 if (cetds[i]) { 308 309 /* * sensors without valid data */ 310 if ((etds[i].validflags & ENVSYS_FCURVALID) == 0) { 311 printf(" %*.*s", (int)width, (int)width, "*"); 312 continue; 313 } 314 315 switch(etds[i].units) { 316 case ENVSYS_INDICATOR: 317 printf(" %*.*s", (int)width, (int)width, 318 etds[i].cur.data_us ? "ON" : "OFF"); 319 break; 320 case ENVSYS_STEMP: 321 temp = (etds[i].cur.data_us / 1000000.0) - 322 273.15; 323 if (!celsius) 324 temp = (9.0 / 5.0) * temp + 32.0; 325 printf(" %*.2f", width, temp); 326 break; 327 case ENVSYS_SFANRPM: 328 printf(" %*u", width, etds[i].cur.data_us); 329 break; 330 default: 331 printf(" %*.2f", width, etds[i].cur.data_s / 332 1000000.0); 333 break; 334 } 335 } 336 printf("\n"); 337 } 338 339 340 /* 341 * post: displays usage on stderr 342 */ 343 void 344 usage(void) 345 { 346 347 fprintf(stderr, "Usage: %s [-cr] [-s s1,s2,...]", getprogname()); 348 fprintf(stderr, " [-i interval] [-n headrep] [-w width] [device]\n"); 349 fprintf(stderr, " envstat -l [device]\n"); 350 exit(1); 351 } 352 353 354 /* 355 * post: a list of sensor names supported by the device is displayed on stdout 356 */ 357 void 358 listsensors(envsys_basic_info_t *ebis, int ns) 359 { 360 int i; 361 362 for (i = 0; i < ns; ++i) 363 if (ebis[i].validflags & ENVSYS_FVALID) 364 printf("%s\n", ebis[i].desc); 365 } 366 367 368 /* 369 * pre: fd contains a valid file descriptor of an envsys(4) supporting device 370 * post: returns the number of valid sensors provided by the device 371 * or -1 on error 372 */ 373 int 374 numsensors(int fd) 375 { 376 int count = 0, valid = 1; 377 envsys_tre_data_t etd; 378 etd.sensor = 0; 379 380 while (valid) { 381 if (ioctl(fd, ENVSYS_GTREDATA, &etd) == -1) { 382 fprintf(stderr, E_CANTENUM); 383 exit(1); 384 } 385 386 valid = etd.validflags & ENVSYS_FVALID; 387 if (valid) 388 ++count; 389 390 ++etd.sensor; 391 } 392 393 return count; 394 } 395 396 /* 397 * pre: fd contains a valid file descriptor of an envsys(4) supporting device 398 * && ns is the number of sensors 399 * && etds and ebis are arrays of sufficient size 400 * post: returns 0 and etds and ebis arrays are filled with sensor info 401 * or returns -1 on failure 402 */ 403 int 404 fillsensors(int fd, envsys_tre_data_t *etds, envsys_basic_info_t *ebis, int ns) 405 { 406 int i; 407 408 for (i = 0; i < ns; ++i) { 409 ebis[i].sensor = i; 410 if (ioctl(fd, ENVSYS_GTREINFO, &ebis[i]) == -1) 411 return -1; 412 413 etds[i].sensor = i; 414 if (ioctl(fd, ENVSYS_GTREDATA, &etds[i]) == -1) 415 return -1; 416 } 417 418 return 0; 419 } 420 421 422 /* 423 * post: returns the strlen() of the longest sensor name 424 */ 425 int 426 longestname(envsys_basic_info_t *ebis, int ns) 427 { 428 int i, maxlen, cur; 429 430 maxlen = 0; 431 432 for (i = 0; i < ns; ++i) { 433 cur = strlen(ebis[i].desc); 434 if (cur > maxlen) 435 maxlen = cur; 436 } 437 438 return maxlen; 439 } 440 441 /* 442 * post: returns 0 and cetds[i] != 0 iff sensor i should appear in the output 443 * or returns -1 444 */ 445 int 446 marksensors(envsys_basic_info_t *ebis, int *cetds, char *sensors, int ns) 447 { 448 int i; 449 char *s; 450 451 if (sensors == NULL) { 452 /* No sensors specified, include them all */ 453 for (i = 0; i < ns; ++i) 454 cetds[i] = 1; 455 456 return 0; 457 } 458 459 /* Assume no sensors in display */ 460 memset(cetds, 0, ns * sizeof(int)); 461 462 s = strtok(sensors, ","); 463 while (s != NULL) { 464 if ((i = strtosnum(ebis, s, ns)) != -1) 465 cetds[i] = 1; 466 else { 467 fprintf(stderr, "envstat: unknown sensor %s\n", s); 468 return (-1); 469 } 470 471 s = strtok(NULL, ","); 472 } 473 474 /* Check if we have at least one sensor selected for output */ 475 for (i = 0; i < ns; ++i) 476 if (cetds[i] == 1) 477 return (0); 478 479 fprintf(stderr, "envstat: no sensors selected for display\n"); 480 return (-1); 481 } 482 483 484 /* 485 * returns -1 if s is not a valid sensor name for the device 486 * or the sensor number of a sensor which has that name 487 */ 488 int 489 strtosnum(envsys_basic_info_t *ebis, const char *s, int ns) 490 { 491 int i; 492 493 for (i = 0; i < ns; ++i) { 494 if((ebis[i].validflags & ENVSYS_FVALID) && 495 !strcmp(s, ebis[i].desc)) 496 return ebis[i].sensor; 497 } 498 499 return -1; 500 } 501