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