1 /* $NetBSD: envstat.c,v 1.23 2006/08/31 10:25:04 gson 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.23 2006/08/31 10:25:04 gson 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 #define ENVSYSUNITNAMES 53 #include <sys/envsys.h> 54 #include <sys/ioctl.h> 55 56 int main(int, char **); 57 void listsensors(envsys_basic_info_t *, size_t); 58 size_t numsensors(int); 59 void fillsensors(int, envsys_tre_data_t *, envsys_basic_info_t *, size_t); 60 size_t longestname(envsys_basic_info_t *, size_t); 61 int marksensors(envsys_basic_info_t *, int *, char *, size_t); 62 int strtosnum(envsys_basic_info_t *, const char *, size_t); 63 void header(size_t, int, envsys_basic_info_t *, const int * const, size_t); 64 void values(size_t, int, envsys_tre_data_t *, const int * const, size_t); 65 void usage(void); 66 void printrow(int *, envsys_tre_data_t *, envsys_basic_info_t *, int, int, 67 size_t); 68 69 int rflag = 0; 70 71 int 72 main(int argc, char **argv) 73 { 74 int c, fd, ls, celsius; 75 size_t ns; 76 size_t interval, width, headrep, headcnt; 77 envsys_tre_data_t *etds; 78 envsys_basic_info_t *ebis; 79 int *cetds; 80 char *sensors; 81 const char *dev; 82 83 fd = -1; 84 ls = 0; 85 celsius = 1; 86 interval = 0; 87 width = 0; 88 sensors = NULL; 89 headrep = 22; 90 91 while ((c = getopt(argc, argv, "fi:ln:rs:w:")) != -1) { 92 switch(c) { 93 case 'r': 94 rflag = 1; 95 break; 96 case 'i': /* wait time between displays */ 97 interval = atoi(optarg); 98 break; 99 case 'w': /* minimum column width */ 100 width = atoi(optarg); 101 break; 102 case 'l': /* list sensor names */ 103 ls = 1; 104 break; 105 case 'n': /* repeat header every headrep lines */ 106 headrep = atoi(optarg); 107 break; 108 case 's': /* restrict display to named sensors */ 109 sensors = (char *)malloc(strlen(optarg) + 1); 110 if (sensors == NULL) 111 exit(1); 112 strcpy(sensors, optarg); 113 break; 114 case 'f': /* display temp in degF */ 115 celsius = 0; 116 break; 117 case '?': 118 default: 119 usage(); 120 /* NOTREACHED */ 121 } 122 } 123 124 if (optind < argc) 125 dev = argv[optind]; 126 else 127 dev = _PATH_SYSMON; 128 129 if ((fd = open(dev, O_RDONLY)) == -1) 130 err(1, "unable to open %s", dev); 131 132 /* 133 * Determine number of sensors, allocate and fill arrays with 134 * initial information. Determine column width 135 */ 136 if ((ns = numsensors(fd)) == 0) 137 errx(1, "No sensors found"); 138 139 cetds = (int *)malloc(ns * sizeof(int)); 140 etds = (envsys_tre_data_t *)malloc(ns * sizeof(envsys_tre_data_t)); 141 ebis = (envsys_basic_info_t *)malloc(ns * sizeof(envsys_basic_info_t)); 142 143 if ((cetds == NULL) || (etds == NULL) || (ebis == NULL)) 144 err(1, "Out of memory"); 145 146 fillsensors(fd, etds, ebis, ns); 147 148 if (ls) { 149 listsensors(ebis, ns); 150 exit(0); 151 } 152 153 154 #define MAX(x, y) (((x) > (y)) ? (x) : (y)) 155 if (!width) { 156 width = longestname(ebis, ns); 157 width = MAX(((79 - ns) / ns), width); 158 } 159 160 /* Mark which sensor numbers are to be displayed */ 161 if (marksensors(ebis, cetds, sensors, ns) == -1) 162 exit(1); 163 164 if (rflag) { 165 if (interval == 0) { 166 printrow(cetds, etds, ebis, ns, celsius, width); 167 exit(0); 168 } 169 170 while (1) { 171 printrow(cetds, etds, ebis, ns, celsius, width); 172 sleep(interval); 173 fillsensors(fd, etds, ebis, ns); 174 printf("\n"); 175 } 176 } 177 178 179 180 /* If we didn't specify an interval value, print the sensors once */ 181 if (!interval) { 182 if (headrep) 183 header(width, celsius, ebis, cetds, ns); 184 values(width, celsius, etds, cetds, ns); 185 186 exit (0); 187 } 188 189 headcnt = 0; 190 if (headrep) 191 header(width, celsius, ebis, cetds, ns); 192 193 for (;;) { 194 values(width, celsius, etds, cetds, ns); 195 if (headrep && (++headcnt == headrep)) { 196 headcnt = 0; 197 header(width, celsius, ebis, cetds, ns); 198 } 199 fflush(stdout); 200 201 sleep(interval); 202 203 fillsensors(fd, etds, ebis, ns); 204 } 205 206 /* NOTREACHED */ 207 return (0); 208 } 209 210 /* 211 * row wise processing 212 */ 213 void 214 printrow(int *cetds, envsys_tre_data_t *etds, envsys_basic_info_t *ebis, 215 int ns, int celsius, size_t width) 216 { 217 int i; 218 219 for (i = 0 ; i < ns ; i++) { 220 if (cetds[i] == 0) 221 continue; 222 223 if ((etds[i].validflags & ENVSYS_FCURVALID) == 0) 224 continue; 225 226 if (ebis[i].units == ENVSYS_INDICATOR && 227 etds[i].cur.data_s == 0) 228 continue; 229 230 printf("%*.*s", (int)width, (int)width, ebis[i].desc); 231 /* different units need some magic */ 232 switch (ebis[i].units) 233 { 234 case ENVSYS_INDICATOR: 235 break; 236 case ENVSYS_INTEGER: 237 printf(": %10d", etds[i].cur.data_s); 238 break; 239 case ENVSYS_STEMP: { 240 double temp = (etds[i].cur.data_s / 1000000.0) 241 - 273.15; 242 if (celsius) 243 printf(": %10.3f degC", temp); 244 else { 245 temp = (9.0 / 5.0) * temp + 32.0; 246 printf(": %10.3f degF", temp); 247 } 248 break; 249 } 250 case ENVSYS_SFANRPM: 251 printf(": %10u RPM", etds[i].cur.data_us); 252 break; 253 default: 254 printf(": %10.3f %s", 255 etds[i].cur.data_s / 1000000.0, 256 envsysunitnames[ebis[i].units]); 257 break; 258 } 259 260 if (etds[i].validflags & ENVSYS_FFRACVALID) { 261 printf(" (%5.2f%%)", 262 (etds[i].cur.data_s * 100.0) / 263 etds[i].max.data_s); 264 } 265 266 printf("\n"); 267 } 268 269 } 270 271 /* 272 * pre: cetds[i] != 0 iff sensor i should appear in the output 273 * post: a column header line is displayed on stdout 274 */ 275 void 276 header(size_t width, int celsius, envsys_basic_info_t *ebis, 277 const int * const cetds, size_t ns) 278 { 279 int i; 280 const char *s; 281 282 /* sensor names */ 283 for (i = 0; i < ns; ++i) 284 if (cetds[i]) 285 printf(" %*.*s", (int)width, (int)width, ebis[i].desc); 286 287 printf("\n"); 288 289 /* units */ 290 for (i = 0; i < ns; ++i) 291 if (cetds[i]) { 292 if ((ebis[i].units == ENVSYS_STEMP) && 293 !celsius) 294 s = "degF"; 295 else if (ebis[i].units >= ENVSYS_NSENSORS) 296 s = envsysunitnames[ENVSYS_NSENSORS]; 297 else 298 s = envsysunitnames[ebis[i].units]; 299 300 printf(" %*.*s", (int)width, (int)width, s); 301 } 302 printf("\n"); 303 } 304 305 void 306 values(size_t width, int celsius, envsys_tre_data_t *etds, 307 const int * const cetds, size_t ns) 308 { 309 int i; 310 double temp; 311 312 for (i = 0; i < ns; ++i) 313 if (cetds[i]) { 314 315 /* * sensors without valid data */ 316 if ((etds[i].validflags & ENVSYS_FCURVALID) == 0) { 317 printf(" %*.*s", (int)width, (int)width, "*"); 318 continue; 319 } 320 321 switch(etds[i].units) { 322 case ENVSYS_INDICATOR: 323 printf(" %*.*s", (int)width, (int)width, 324 etds[i].cur.data_us ? "ON" : "OFF"); 325 break; 326 case ENVSYS_STEMP: 327 temp = (etds[i].cur.data_us / 1000000.0) - 328 273.15; 329 if (!celsius) 330 temp = (9.0 / 5.0) * temp + 32.0; 331 printf(" %*.2f", (int)width, temp); 332 break; 333 case ENVSYS_SFANRPM: 334 printf(" %*u", (int)width, etds[i].cur.data_us); 335 break; 336 default: 337 printf(" %*.2f", (int)width, etds[i].cur.data_s / 338 1000000.0); 339 break; 340 } 341 } 342 printf("\n"); 343 } 344 345 346 /* 347 * post: displays usage on stderr 348 */ 349 void 350 usage(void) 351 { 352 353 (void)fprintf(stderr, "usage: %s [-fr] [-s s1,s2,...]", getprogname()); 354 (void)fprintf(stderr, " [-i interval] [-n headrep] [-w width]"); 355 (void)fprintf(stderr, " [device]\n"); 356 (void)fprintf(stderr, " %s -l [device]\n", getprogname()); 357 exit(1); 358 } 359 360 361 /* 362 * post: a list of sensor names supported by the device is displayed on stdout 363 */ 364 void 365 listsensors(envsys_basic_info_t *ebis, size_t ns) 366 { 367 int i; 368 369 for (i = 0; i < ns; ++i) 370 if (ebis[i].validflags & ENVSYS_FVALID) 371 printf("%s\n", ebis[i].desc); 372 } 373 374 375 /* 376 * pre: fd contains a valid file descriptor of an envsys(4) supporting device 377 * post: returns the number of valid sensors provided by the device 378 * or -1 on error 379 */ 380 size_t 381 numsensors(int fd) 382 { 383 int count = 0, valid = 1; 384 envsys_tre_data_t etd; 385 etd.sensor = 0; 386 387 while (valid) { 388 if (ioctl(fd, ENVSYS_GTREDATA, &etd) == -1) 389 err(1, "Can't get sensor data"); 390 391 valid = etd.validflags & ENVSYS_FVALID; 392 if (valid) 393 ++count; 394 395 ++etd.sensor; 396 } 397 398 return count; 399 } 400 401 /* 402 * pre: fd contains a valid file descriptor of an envsys(4) supporting device 403 * && ns is the number of sensors 404 * && etds and ebis are arrays of sufficient size 405 * post: returns 0 and etds and ebis arrays are filled with sensor info 406 * or returns -1 on failure 407 */ 408 void 409 fillsensors(int fd, envsys_tre_data_t *etds, envsys_basic_info_t *ebis, 410 size_t ns) 411 { 412 int i; 413 414 for (i = 0; i < ns; ++i) { 415 ebis[i].sensor = i; 416 if (ioctl(fd, ENVSYS_GTREINFO, &ebis[i]) == -1) 417 err(1, "Can't get sensor info for sensor %d", i); 418 419 etds[i].sensor = i; 420 if (ioctl(fd, ENVSYS_GTREDATA, &etds[i]) == -1) 421 err(1, "Can't get sensor data for sensor %d", i); 422 } 423 } 424 425 426 /* 427 * post: returns the strlen() of the longest sensor name 428 */ 429 size_t 430 longestname(envsys_basic_info_t *ebis, size_t ns) 431 { 432 size_t i, maxlen, cur; 433 434 maxlen = 0; 435 436 for (i = 0; i < ns; ++i) { 437 cur = strlen(ebis[i].desc); 438 if (cur > maxlen) 439 maxlen = cur; 440 } 441 442 return maxlen; 443 } 444 445 /* 446 * post: returns 0 and cetds[i] != 0 iff sensor i should appear in the output 447 * or returns -1 448 */ 449 int 450 marksensors(envsys_basic_info_t *ebis, int *cetds, char *sensors, size_t ns) 451 { 452 size_t i; 453 char *s; 454 455 if (sensors == NULL) { 456 /* No sensors specified, include them all */ 457 for (i = 0; i < ns; ++i) 458 cetds[i] = 1; 459 460 return 0; 461 } 462 463 /* Assume no sensors in display */ 464 memset(cetds, 0, ns * sizeof(int)); 465 466 s = strtok(sensors, ","); 467 while (s != NULL) { 468 int snum; 469 if ((snum = strtosnum(ebis, s, ns)) != -1) 470 cetds[snum] = 1; 471 else { 472 warnx("Unknown sensor %s", s); 473 return (-1); 474 } 475 476 s = strtok(NULL, ","); 477 } 478 479 /* Check if we have at least one sensor selected for output */ 480 for (i = 0; i < ns; ++i) 481 if (cetds[i] == 1) 482 return (0); 483 484 warnx("No sensors selected for display"); 485 return (-1); 486 } 487 488 489 /* 490 * returns -1 if s is not a valid sensor name for the device 491 * or the sensor number of a sensor which has that name 492 */ 493 int 494 strtosnum(envsys_basic_info_t *ebis, const char *s, size_t ns) 495 { 496 size_t i; 497 498 for (i = 0; i < ns; ++i) { 499 if((ebis[i].validflags & ENVSYS_FVALID) && 500 !strcmp(s, ebis[i].desc)) 501 return ebis[i].sensor; 502 } 503 504 return -1; 505 } 506