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