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