cpu voltage patch
[monky] / src / linux.c
1 /* linux.c
2  * Contains linux specific code
3  *
4  *  $Id$
5  */
6
7
8 #include "conky.h"
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <dirent.h>
13 #include <ctype.h>
14 #include <errno.h>
15 #include <limits.h>
16 #include <sys/types.h>
17 #include <sys/sysinfo.h>
18 #include <sys/stat.h>
19 #include <fcntl.h>
20 #include <unistd.h>
21 // #include <assert.h>
22 #include <time.h>
23 #include "top.h"
24
25 #include <sys/ioctl.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <linux/sockios.h>
29 #include <net/if.h>
30 #include <math.h>
31
32 #define SHORTSTAT_TEMPL "%*s %llu %llu %llu"
33 #define LONGSTAT_TEMPL "%*s %llu %llu %llu "
34
35
36 static struct sysinfo s_info;
37
38 static int show_nice_processes;
39
40 void prepare_update()
41 {
42 }
43
44 static void update_sysinfo()
45 {
46         sysinfo(&s_info);
47
48         info.uptime = (double) s_info.uptime;
49
50         /* there was some problem with these */
51 #if 0
52 //      info.loadavg[0] = s_info.loads[0] / 100000.0f;
53         info.loadavg[1] = s_info.loads[1] / 100000.0f;
54         info.loadavg[2] = s_info.loads[2] / 100000.0f;
55         gkrelltop_process_find_top_three info.mask |= 1 << INFO_LOADAVG;
56 #endif
57
58         info.procs = s_info.procs;
59
60         /* these aren't nice, no cache and should check kernel version for mem_unit */
61 #if 0
62         info.memmax = s_info.totalram;
63         info.mem = s_info.totalram - s_info.freeram;
64         info.swapmax = s_info.totalswap;
65         info.swap = s_info.totalswap - s_info.swap;
66         info.mask |= 1 << INFO_MEM;
67 #endif
68
69         info.mask |= (1 << INFO_UPTIME) | (1 << INFO_PROCS);
70 }
71
72 void update_uptime()
73 {
74         /* prefers sysinfo() for uptime, I don't really know which one is better
75          * (=faster?) */
76 #ifdef USE_PROC_UPTIME
77         static int rep;
78         FILE *fp = open_file("/proc/uptime", &rep);
79         if (!fp)
80                 return 0;
81         fscanf(fp, "%lf", &info.uptime);
82         fclose(fp);
83
84         info.mask |= (1 << INFO_UPTIME);
85 #else
86         update_sysinfo();
87 #endif
88 }
89
90 /* these things are also in sysinfo except Buffers:, that's why I'm reading
91 * them from proc */
92
93 static FILE *meminfo_fp;
94
95 void update_meminfo()
96 {
97         static int rep;
98         /*  unsigned int a; */
99         char buf[256];
100
101         info.mem = info.memmax = info.swap = info.swapmax = info.bufmem =
102             info.buffers = info.cached = 0;
103
104         if (meminfo_fp == NULL)
105                 meminfo_fp = open_file("/proc/meminfo", &rep);
106         else
107                 fseek(meminfo_fp, 0, SEEK_SET);
108         if (meminfo_fp == NULL)
109                 return;
110
111         while (!feof(meminfo_fp)) {
112                 if (fgets(buf, 255, meminfo_fp) == NULL)
113                         break;
114
115                 if (strncmp(buf, "MemTotal:", 9) == 0) {
116                         sscanf(buf, "%*s %lu", &info.memmax);
117                 } else if (strncmp(buf, "MemFree:", 8) == 0) {
118                         sscanf(buf, "%*s %lu", &info.mem);
119                 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
120                         sscanf(buf, "%*s %lu", &info.swapmax);
121                 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
122                         sscanf(buf, "%*s %lu", &info.swap);
123                 } else if (strncmp(buf, "Buffers:", 8) == 0) {
124                         sscanf(buf, "%*s %lu", &info.buffers);
125                 } else if (strncmp(buf, "Cached:", 7) == 0) {
126                         sscanf(buf, "%*s %lu", &info.cached);
127                 }
128         }
129         
130         info.mem = info.memmax - info.mem;
131         info.swap = info.swapmax - info.swap;
132
133         info.bufmem = info.cached + info.buffers;
134
135         info.mask |= (1 << INFO_MEM) | (1 << INFO_BUFFERS);
136 }
137
138 static FILE *net_dev_fp;
139 static FILE *net_wireless_fp;
140
141 inline void update_net_stats()
142 {
143         static int rep;
144         // FIXME: arbitrary size chosen to keep code simple.
145         int i, i2;
146         unsigned int curtmp1, curtmp2;
147         unsigned int k;
148         struct ifconf conf;
149
150
151         char buf[256];
152         double delta;
153
154         /* get delta */
155         delta = current_update_time - last_update_time;
156         if (delta <= 0.0001)
157                 return;
158
159         /* open file and ignore first two lines */
160         if (net_dev_fp == NULL) {
161                 net_dev_fp = open_file("/proc/net/dev", &rep);
162         }
163         else
164                 fseek(net_dev_fp, 0, SEEK_SET);
165         if (!net_dev_fp)
166                 return;
167
168         fgets(buf, 255, net_dev_fp);    /* garbage */
169         fgets(buf, 255, net_dev_fp);    /* garbage (field names) */
170
171         /* read each interface */
172         for (i2 = 0; i2 < 16; i2++) {
173                 struct net_stat *ns;
174                 char *s, *p;
175                 long long r, t, last_recv, last_trans;
176
177                 if (fgets(buf, 255, net_dev_fp) == NULL) {
178                         break;
179                 }
180                 p = buf;
181                 while (isspace((int) *p))
182                         p++;
183
184                 s = p;
185
186                 while (*p && *p != ':')
187                         p++;
188                 if (*p == '\0')
189                         continue;
190                 *p = '\0';
191                 p++;
192
193                 ns = get_net_stat(s);
194                 ns->up = 1;
195                 memset(&(ns->addr.sa_data), 0, 14);
196                 last_recv = ns->recv;
197                 last_trans = ns->trans;
198
199                 sscanf(p,
200                        /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
201                        "%Ld  %*d     %*d  %*d  %*d  %*d   %*d        %*d       %Ld",
202                        &r, &t);
203
204                 /* if recv or trans is less than last time, an overflow happened */
205
206                 if (r < ns->last_read_recv)
207                         ns->recv +=
208                             ((long long) 4294967295U -
209                              ns->last_read_recv) + r;
210                 else
211                         ns->recv += (r - ns->last_read_recv);
212                 ns->last_read_recv = r;
213
214                 if (t < ns->last_read_trans)
215                         ns->trans +=
216                             ((long long) 4294967295U -
217                              ns->last_read_trans) + t;
218                 else
219                         ns->trans += (t - ns->last_read_trans);
220                 ns->last_read_trans = t;
221
222                 /*** ip addr patch ***/
223                 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
224
225                 conf.ifc_buf = malloc(sizeof(struct ifreq) * 16);
226
227                 conf.ifc_len = sizeof(struct ifreq) * 16;
228
229                 ioctl((long) i, SIOCGIFCONF, &conf);
230
231                 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
232                         struct net_stat *ns;
233                         ns = get_net_stat(((struct ifreq *) conf.
234                                            ifc_buf)[k].ifr_ifrn.ifrn_name);
235                         ns->addr =
236                             ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.
237                             ifru_addr;
238                 }
239
240                 close((long) i);
241
242                 free(conf.ifc_buf);
243
244
245                 /*** end ip addr patch ***/
246
247
248                 /* calculate speeds */
249                 ns->net_rec[0] = (ns->recv - last_recv) / delta;
250                 ns->net_trans[0] = (ns->trans - last_trans) / delta;
251                 curtmp1 = 0;
252                 curtmp2 = 0;
253                 // get an average
254                 for (i = 0; (unsigned) i < info.net_avg_samples; i++) {
255                         curtmp1 += ns->net_rec[i];
256                         curtmp2 += ns->net_trans[i];
257                 }
258                 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
259                 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
260                 if (info.net_avg_samples > 1) {
261                         for (i = info.net_avg_samples; i > 1; i--) {
262                                 ns->net_rec[i - 1] = ns->net_rec[i - 2];
263                                 ns->net_trans[i - 1] =
264                                     ns->net_trans[i - 2];
265                         }
266                 }
267
268
269
270         }
271
272         /* fclose(net_dev_fp); net_dev_fp = NULL; */
273 }
274
275 inline void update_wifi_stats()
276 {
277         /** wireless stats patch by Bobby Beckmann **/
278         static int rep;
279         int i;
280         char buf[256];
281         /*open file and ignore first two lines       sorry, this code sucks ass right now, i'll clean it up later */
282         if (net_wireless_fp == NULL)
283                 net_wireless_fp = open_file("/proc/net/wireless", &rep);
284         else
285                 fseek(net_wireless_fp, 0, SEEK_SET);
286         if (net_wireless_fp == NULL)
287                 return;
288
289         fgets(buf, 255, net_wireless_fp);       /* garbage */
290         fgets(buf, 255, net_wireless_fp);       /* garbage (field names) */
291
292         /* read each interface */
293         for (i = 0; i < 16; i++) {
294                 struct net_stat *ns;
295                 char *s, *p;
296                 int l, m, n;
297
298                 if (fgets(buf, 255, net_wireless_fp) == NULL)
299                         break;
300                 p = buf;
301                 while (isspace((int) *p))
302                         p++;
303
304                 s = p;
305
306                 while (*p && *p != ':')
307                         p++;
308                 if (*p == '\0')
309                         continue;
310                 *p = '\0';
311                 p++;
312
313                 ns = get_net_stat(s);
314
315                 sscanf(p, "%*d   %d.  %d.  %d", &l, &m, &n);
316
317                 ns->linkstatus = (int) (log(MIN(MAX(l,1),92)) / log(92) * 100);
318
319         }
320
321         /*** end wireless patch ***/
322 }
323
324 int result;
325
326 void update_total_processes()
327 {
328         update_sysinfo();
329 }
330
331 #define CPU_SAMPLE_COUNT 15
332 struct cpu_info {
333         unsigned long long cpu_user;
334         unsigned long long cpu_system;
335         unsigned long long cpu_nice;
336         unsigned long long cpu_idle;
337         unsigned long long cpu_iowait;
338         unsigned long long cpu_irq;
339         unsigned long long cpu_softirq;
340         unsigned long long cpu_steal;
341         unsigned long long cpu_total;
342         unsigned long long cpu_active_total;
343         unsigned long long cpu_last_total;
344         unsigned long long cpu_last_active_total;
345         double cpu_val[CPU_SAMPLE_COUNT];
346 };
347 static short cpu_setup = 0;
348 static int rep;
349
350
351 static FILE *stat_fp;
352
353 /* 
354    determine if this kernel gives us "extended" statistics information in /proc/stat. 
355    Kernels around 2.5 and earlier only reported user, system, nice and idle values in proc stat. 
356    Kernels around 2.6 and greater report these PLUS iowait, irq, softirq, and steal 
357 */
358 void determine_longstat(char * buf) { 
359         unsigned long long iowait=0;
360         KFLAG_SETOFF(KFLAG_IS_LONGSTAT);        
361         /* scanf will either return -1 or 1 because there is only 1 assignment  */
362         if (sscanf(buf, "%*s %*d %*d %*d %*d %llu",&iowait)>0) KFLAG_SETON(KFLAG_IS_LONGSTAT);
363 }
364
365 void get_cpu_count()
366 {
367         if (info.cpu_usage) {
368                 return;
369         }
370         char buf[256];
371         if (stat_fp == NULL)
372                 stat_fp = open_file("/proc/stat", &rep);
373         else
374                 fseek(stat_fp, 0, SEEK_SET);
375         if (stat_fp == NULL)
376                 return;
377
378         info.cpu_count = 0;
379
380         while (!feof(stat_fp)) {
381                 if (fgets(buf, 255, stat_fp) == NULL)
382                         break;
383
384                 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
385                         if (info.cpu_count == 0) {
386                                 determine_longstat(buf);
387                         }
388                         info.cpu_count++;
389                 }
390         }
391         info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
392 }
393
394 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
395 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
396
397 inline static void update_stat()
398 {
399         static struct cpu_info *cpu = NULL;
400         char buf[256];
401         unsigned int i;
402         unsigned int index;
403         double curtmp;
404         char * stat_template=NULL; 
405         unsigned int malloc_cpu_size=0;
406         
407
408         if (!cpu_setup) {
409                 get_cpu_count();
410                 cpu_setup = 1;
411         }
412
413         if (stat_template == NULL) {
414                 stat_template = KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT ;
415         }       
416
417         if (cpu == NULL) {
418                 malloc_cpu_size = (info.cpu_count + 1) *  sizeof(struct cpu_info);
419                 cpu = malloc(malloc_cpu_size);
420                 memset(cpu, 0, malloc_cpu_size);
421         }
422
423         if (stat_fp == NULL) {
424                 stat_fp = open_file("/proc/stat", &rep);
425         } else {
426                 fseek(stat_fp, 0, SEEK_SET);
427         }
428         if (stat_fp == NULL) {
429                 return;
430         }
431         index = 0;
432         while (!feof(stat_fp)) {
433                 if (fgets(buf, 255, stat_fp) == NULL)
434                         break;
435
436                 if (strncmp(buf, "procs_running ", 14) == 0) {
437                         sscanf(buf, "%*s %hu", &info.run_procs);
438                         info.mask |= (1 << INFO_RUN_PROCS);
439                 } else if (strncmp(buf, "cpu", 3) == 0) {
440                         index = isdigit(buf[3]) ? ((int)buf[3]) - 0x2F : 0;
441                         sscanf(buf, stat_template 
442                                 , &(cpu[index].cpu_user)
443                                 , &(cpu[index].cpu_nice)
444                                 , &(cpu[index].cpu_system)
445                                 , &(cpu[index].cpu_idle)
446                                 , &(cpu[index].cpu_iowait)
447                                 , &(cpu[index].cpu_irq)
448                                 , &(cpu[index].cpu_softirq)
449                                 , &(cpu[index].cpu_steal)
450                                 );
451
452                         cpu[index].cpu_total = cpu[index].cpu_user 
453                                          + cpu[index].cpu_nice 
454                                          + cpu[index].cpu_system 
455                                          + cpu[index].cpu_idle 
456                                          + cpu[index].cpu_iowait 
457                                          + cpu[index].cpu_irq
458                                          + cpu[index].cpu_softirq
459                                          + cpu[index].cpu_steal 
460                                          ; 
461
462                         cpu[index].cpu_active_total = cpu[index].cpu_total - (cpu[index].cpu_idle + cpu[index].cpu_iowait);
463                         info.mask |= (1 << INFO_CPU);
464
465                         double delta = current_update_time - last_update_time;
466                         if (delta <= 0.001) return;     
467
468                         cpu[index].cpu_val[0] = (cpu[index].cpu_active_total -  cpu[index].cpu_last_active_total) / 
469                                                 (float )(cpu[index].cpu_total - cpu[index].cpu_last_total); 
470                         curtmp = 0;
471                         for (i=0; i < info.cpu_avg_samples; i++ ) {
472                                 curtmp += cpu[index].cpu_val[i];
473                         }
474                         /* TESTING -- I've removed this, because I don't think it is right. You shouldn't divide 
475                                       by the cpu count here ... removing for testing */
476                         /* if (index == 0) {
477                                 info.cpu_usage[index] = curtmp / info.cpu_avg_samples / info.cpu_count; 
478                         } else {
479                                 info.cpu_usage[index] = curtmp / info.cpu_avg_samples;
480                         }  */
481                         /* TESTING -- this line replaces the prev. "suspect" if/else */
482                         info.cpu_usage[index] = curtmp / info.cpu_avg_samples;
483
484                         cpu[index].cpu_last_total = cpu[index].cpu_total;
485                         cpu[index].cpu_last_active_total = cpu[index].cpu_active_total;
486                         for (i = info.cpu_avg_samples - 1; i > 0; i--) {
487                                 cpu[index].cpu_val[i] = cpu[index].cpu_val[i - 1];
488                         }
489                 }
490
491         }
492 }
493
494 void update_running_processes()
495 {
496         update_stat();
497 }
498
499 void update_cpu_usage()
500 {
501         update_stat();
502 }
503
504 void update_load_average()
505 {
506 #ifdef HAVE_GETLOADAVG
507         double v[3];
508         getloadavg(v, 3);
509         info.loadavg[0] = (float) v[0];
510         info.loadavg[1] = (float) v[1];
511         info.loadavg[2] = (float) v[2];
512 #else
513         static int rep;
514         FILE *fp;
515
516         fp = open_file("/proc/loadavg", &rep);
517         if (!fp) {
518                 v[0] = v[1] = v[2] = 0.0;
519                 return;
520         }
521
522         fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
523                &info.loadavg[2]);
524
525         fclose(fp);
526 #endif
527 }
528
529 #define PROC_I8K "/proc/i8k"
530 #define I8K_DELIM " "
531 static char *i8k_procbuf = NULL;
532 void update_i8k()
533 {
534         FILE *fp;
535         if (!i8k_procbuf) {
536                 i8k_procbuf = (char*)malloc(128*sizeof(char));
537         }
538         if ((fp = fopen(PROC_I8K,"r")) == NULL) {
539                 CRIT_ERR("/proc/i8k doesn't exist! use insmod to make sure the kernel driver is loaded...");
540         }
541
542         memset(&i8k_procbuf[0],0,128);
543         if (fread(&i8k_procbuf[0],sizeof(char),128,fp) == 0) {
544                 ERR("something wrong with /proc/i8k...");
545         }
546
547         fclose(fp);
548
549   i8k.version = strtok(&i8k_procbuf[0],I8K_DELIM);
550         i8k.bios = strtok(NULL,I8K_DELIM);
551         i8k.serial = strtok(NULL,I8K_DELIM);
552         i8k.cpu_temp = strtok(NULL,I8K_DELIM);
553         i8k.left_fan_status = strtok(NULL,I8K_DELIM);   
554         i8k.right_fan_status = strtok(NULL,I8K_DELIM);  
555         i8k.left_fan_rpm = strtok(NULL,I8K_DELIM);
556         i8k.right_fan_rpm = strtok(NULL,I8K_DELIM);
557         i8k.ac_status = strtok(NULL,I8K_DELIM);
558         i8k.buttons_status = strtok(NULL,I8K_DELIM);
559 }
560
561
562 /***********************************************************/
563 /***********************************************************/
564 /***********************************************************/
565
566 static int no_dots(const struct dirent *d)
567 {
568         if (d->d_name[0] == '.')
569                 return 0;
570         return 1;
571 }
572
573 static int
574 get_first_file_in_a_directory(const char *dir, char *s, int *rep)
575 {
576         struct dirent **namelist;
577         int i, n;
578
579         n = scandir(dir, &namelist, no_dots, alphasort);
580         if (n < 0) {
581                 if (!rep || !*rep) {
582                         ERR("scandir for %s: %s", dir, strerror(errno));
583                         if (rep)
584                                 *rep = 1;
585                 }
586                 return 0;
587         } else {
588                 if (n == 0)
589                         return 0;
590
591                 strncpy(s, namelist[0]->d_name, 255);
592                 s[255] = '\0';
593
594                 for (i = 0; i < n; i++)
595                         free(namelist[i]);
596                 free(namelist);
597
598                 return 1;
599         }
600 }
601
602 #define I2C_DIR "/sys/bus/i2c/devices/"
603
604 int
605 open_i2c_sensor(const char *dev, const char *type, int n, int *div,
606                 char *devtype)
607 {
608         char path[256];
609         char buf[256];
610         int fd;
611         int divfd;
612
613         /* if i2c device is NULL or *, get first */
614         if (dev == NULL || strcmp(dev, "*") == 0) {
615                 static int rep;
616                 if (!get_first_file_in_a_directory(I2C_DIR, buf, &rep))
617                         return -1;
618                 dev = buf;
619         }
620
621         /* change vol to in */
622         if (strcmp(type, "vol") == 0)
623                 type = "in";
624
625         if (strcmp(type, "tempf") == 0) {
626                 snprintf(path, 255, I2C_DIR "%s/%s%d_input", dev, "temp", n);
627         } else {
628                 snprintf(path, 255, I2C_DIR "%s/%s%d_input", dev, type, n);
629         }
630         strncpy(devtype, path, 255);
631
632         /* open file */
633         fd = open(path, O_RDONLY);
634         if (fd < 0) {
635                 CRIT_ERR("can't open '%s': %s\nplease fix i2c or remove it from Conky", path, strerror(errno));
636         }
637
638         if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
639             || strcmp(type, "tempf") == 0)
640                 *div = 1;
641         else
642                 *div = 0;
643         /* fan does not use *_div as a read divisor */
644         if (strcmp("fan", type) == 0)
645                 return fd;
646
647         /* test if *_div file exist, open it and use it as divisor */
648         if (strcmp(type, "tempf") == 0) {
649                 snprintf(path, 255, I2C_DIR "%s/%s%d_div", "one", "two",
650                          n);
651         } else {
652                 snprintf(path, 255, I2C_DIR "%s/%s%d_div", dev, type, n);
653         }
654
655         divfd = open(path, O_RDONLY);
656         if (divfd > 0) {
657                 /* read integer */
658                 char divbuf[64];
659                 unsigned int divn;
660                 divn = read(divfd, divbuf, 63);
661                 /* should read until n == 0 but I doubt that kernel will give these
662                  * in multiple pieces. :) */
663                 divbuf[divn] = '\0';
664                 *div = atoi(divbuf);
665         }
666
667         close(divfd);
668
669         return fd;
670 }
671
672 double get_i2c_info(int *fd, int div, char *devtype, char *type)
673 {
674         int val = 0;
675
676         if (*fd <= 0)
677                 return 0;
678
679         lseek(*fd, 0, SEEK_SET);
680
681         /* read integer */
682         {
683                 char buf[64];
684                 unsigned int n;
685                 n = read(*fd, buf, 63);
686                 /* should read until n == 0 but I doubt that kernel will give these
687                  * in multiple pieces. :) */
688                 buf[n] = '\0';
689                 val = atoi(buf);
690         }
691
692         close(*fd);
693         /* open file */
694         *fd = open(devtype, O_RDONLY);
695         if (*fd < 0)
696                 ERR("can't open '%s': %s", devtype, strerror(errno));
697
698         /* My dirty hack for computing CPU value 
699          * Filedil, from forums.gentoo.org
700          */
701 /*      if (strstr(devtype, "temp1_input") != NULL)
702         return -15.096+1.4893*(val / 1000.0); */
703
704
705         /* divide voltage and temperature by 1000 */
706         /* or if any other divisor is given, use that */
707         if (strcmp(type, "tempf") == 0) {
708                 if (div > 1)
709                         return ((val / div + 40) * 9.0 / 5) - 40;
710                 else if (div)
711                         return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
712                 else
713                         return ((val + 40) * 9.0 / 5) - 40;
714         } else {
715                 if (div > 1)
716                         return val / div;
717                 else if (div)
718                         return val / 1000.0;
719                 else
720                         return val;
721         }
722 }
723
724 /* Prior to kernel version 2.6.12, the CPU fan speed was available
725  * in ADT746X_FAN_OLD, whereas later kernel versions provide this
726  * information in ADT746X_FAN.
727  */
728 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
729 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
730
731 void get_adt746x_fan( char * p_client_buffer, size_t client_buffer_size )
732 {
733         static int rep;
734         char adt746x_fan_state[64];
735         FILE *fp;
736
737         if ( !p_client_buffer || client_buffer_size <= 0 )
738                 return;
739
740         if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
741                  && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL)
742
743         {
744                 sprintf(adt746x_fan_state, "adt746x not found");
745         }
746         else
747         {
748                 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
749                 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
750                 fclose(fp);
751         }
752
753         snprintf( p_client_buffer, client_buffer_size, "%s", adt746x_fan_state );
754         return;
755 }
756
757 /* Prior to kernel version 2.6.12, the CPU temperature was found
758  * in ADT746X_CPU_OLD, whereas later kernel versions provide this
759  * information in ADT746X_CPU.
760  */
761 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
762 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
763
764 void get_adt746x_cpu( char * p_client_buffer, size_t client_buffer_size )
765 {
766         static int rep;
767         char adt746x_cpu_state[64];
768         FILE *fp;
769
770         if ( !p_client_buffer || client_buffer_size <= 0 )
771                 return;
772         
773         if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
774                  && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL)
775         {
776                 sprintf(adt746x_cpu_state, "adt746x not found");
777         }
778         else
779         {
780                 fscanf(fp, "%2s", adt746x_cpu_state);
781                 fclose(fp);
782         }
783
784         snprintf( p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state ); 
785         return;
786 }
787
788 /* Thanks to "Walt Nelson" <wnelsonjr@comcast.net> */
789
790 /***********************************************************************/
791 /*
792  *  This file is part of x86info.
793  *  (C) 2001 Dave Jones.
794  *
795  *  Licensed under the terms of the GNU GPL License version 2.
796  *
797  * Estimate CPU MHz routine by Andrea Arcangeli <andrea@suse.de>
798  * Small changes by David Sterba <sterd9am@ss1000.ms.mff.cuni.cz>
799  *
800  */
801 #if  defined(__i386) || defined(__x86_64)
802 __inline__ unsigned long long int rdtsc()
803 {
804         unsigned long long int x;
805         __asm__ volatile (".byte 0x0f, 0x31":"=A" (x));
806         return x;
807 }
808 #endif
809
810 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
811 void get_freq_dynamic( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor )
812 {
813 #if  defined(__i386) || defined(__x86_64)
814         struct timezone tz;
815         struct timeval tvstart, tvstop;
816         unsigned long long cycles[2];   /* gotta be 64 bit */
817         unsigned int microseconds;      /* total time taken */
818
819         if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 )
820              return;
821
822         memset(&tz, 0, sizeof(tz));
823
824         /* get this function in cached memory */
825         gettimeofday(&tvstart, &tz);
826         cycles[0] = rdtsc();
827         gettimeofday(&tvstart, &tz);
828
829         /* we don't trust that this is any specific length of time */
830         usleep(100);
831         cycles[1] = rdtsc();
832         gettimeofday(&tvstop, &tz);
833         microseconds = ((tvstop.tv_sec - tvstart.tv_sec) * 1000000) +
834             (tvstop.tv_usec - tvstart.tv_usec);
835
836         snprintf( p_client_buffer, client_buffer_size, p_format, (float)((cycles[1] - cycles[0]) / microseconds) / divisor );
837         return;
838 #else
839 /* FIXME: hardwired: get freq for first cpu!
840    this whole function needs to be rethought and redone for
841    multi-cpu/multi-core/multi-threaded environments and 
842    arbitrary combinations thereof 
843 */
844         get_freq( p_client_buffer, client_buffer_size, p_format, divisor, 1 );
845         return;
846 #endif
847 }
848
849
850 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
851 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
852
853 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
854 void get_freq( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor, unsigned int cpu )
855 {
856         FILE *f;
857         char frequency[32];
858         char s[256];
859         double freq = 0;
860         char current_freq_file[128];
861         
862         cpu--;
863         snprintf(current_freq_file, 127, "%s/cpu%d/%s",
864                  CPUFREQ_PREFIX, cpu, CPUFREQ_POSTFIX);
865
866         if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 )
867                 return;
868
869         f = fopen(current_freq_file, "r");
870         if (f) {
871                 /* if there's a cpufreq /sys node, read the current frequency from this node;
872                  * divide by 1000 to get Mhz. */
873                 if (fgets(s, sizeof(s), f)) {
874                         s[strlen(s)-1] = '\0';
875                         freq = strtod(s, NULL);
876                 }
877                 fclose(f);
878                 snprintf( p_client_buffer, client_buffer_size, p_format, (freq/1000)/divisor );
879                 return;
880         }
881         
882         cpu++;
883         f = fopen("/proc/cpuinfo", "r");                //open the CPU information file
884         if (!f)
885             return;
886
887         while (fgets(s, sizeof(s), f) != NULL){         //read the file
888
889 #if defined(__i386) || defined(__x86_64)
890                 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {        //and search for the cpu mhz
891 #else
892 #if defined(__alpha)
893                 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {          // different on alpha
894 #else
895                 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {  // this is different on ppc for some reason
896 #endif // defined(__alpha)
897 #endif // defined(__i386) || defined(__x86_64)
898
899                 strcpy(frequency, strchr(s, ':') + 2);  //copy just the number
900 #if defined(__alpha)
901                 frequency[strlen(frequency) - 6] = '\0';// strip " est.\n"
902                 freq = strtod(frequency, NULL)/1000000; // kernel reports in Hz 
903 #else
904                 frequency[strlen(frequency) - 1] = '\0'; // strip \n
905                 freq = strtod(frequency, NULL);
906 #endif
907                 break;
908                 }
909                 if (strncmp(s, "processor", 9) == 0) {
910                     cpu--; 
911                     continue;
912                 }
913                 
914         }
915         
916         fclose(f);
917         snprintf( p_client_buffer, client_buffer_size, p_format, (float)freq/divisor );
918         return;
919 }
920
921 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
922
923 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
924 void get_voltage( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor, unsigned int cpu )
925 {
926 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks 
927    something like this:
928 # frequency voltage
929 1800000 1340
930 1600000 1292
931 1400000 1100
932 1200000 988
933 1000000 1116
934 800000 1004
935 600000 988
936 */
937
938 /* Peter Tarjan (ptarjan@citromail.hu) */
939         FILE *f;
940         char s[256];
941         int freq = 0;
942         int voltage = 0;
943         char current_freq_file[128];
944         int freq_comp = 0;
945         
946
947 /* build the voltage file name */
948         cpu--;
949         snprintf(current_freq_file, 127, "%s/cpu%d/%s",
950                  CPUFREQ_PREFIX, cpu, CPUFREQ_POSTFIX);
951
952         if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 )
953                 return;
954
955         /* read the current cpu frequency from the /sys node */
956         f = fopen(current_freq_file, "r");
957         if (f) {
958             if (fgets(s, sizeof(s), f)) {
959                 s[strlen(s)-1] = '\0';
960                 freq = strtod(s, NULL);
961             }
962             fclose(f);
963         }
964         else 
965             {
966                 ERR("voltage: No %s.", current_freq_file);
967                 fclose(f);
968                 return;
969             }
970
971         snprintf(current_freq_file, 127, "%s/cpu%d/%s",
972                  CPUFREQ_PREFIX, cpu, CPUFREQ_VOLTAGE);
973
974 /* use the current cpu frequency to find the corresponding voltage */
975         f = fopen(current_freq_file, "r");
976
977         if (f)
978         {
979             while (!feof(f))
980             {
981                 char line[256];
982                 if (fgets(line, 255, f) == NULL) break;
983                 sscanf(line, "%d %d", &freq_comp, &voltage);
984                 if(freq_comp == freq) break;
985             }
986             fclose(f);
987         }
988         else
989         {
990             ERR("voltage: No %s.", current_freq_file);
991             fclose(f);
992             return;
993         }
994
995         snprintf( p_client_buffer, client_buffer_size, p_format, (float)voltage/divisor );
996         return;
997
998 }
999
1000 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1001
1002 void get_acpi_fan( char * p_client_buffer, size_t client_buffer_size )
1003 {
1004         static int rep;
1005         char buf[256];
1006         char buf2[256];
1007         FILE *fp;
1008
1009         if ( !p_client_buffer || client_buffer_size <= 0 )
1010                 return;
1011
1012         /* yeah, slow... :/ */
1013         if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep))
1014         {
1015                 snprintf( p_client_buffer, client_buffer_size, "no fans?" );
1016                 return;
1017         }
1018
1019         snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf );
1020
1021         fp = open_file(buf2, &rep);
1022         if (!fp) {
1023                 snprintf( p_client_buffer, client_buffer_size, "can't open fan's state file" );
1024                 return;
1025         }
1026         memset(buf,0,sizeof(buf));
1027         fscanf(fp, "%*s %99s", buf);
1028         fclose(fp);
1029
1030         snprintf( p_client_buffer, client_buffer_size, "%s", buf );
1031
1032         return;
1033 }
1034
1035 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1036
1037 void get_acpi_ac_adapter( char * p_client_buffer, size_t client_buffer_size )
1038 {
1039         static int rep;
1040         char buf[256];
1041         char buf2[256];
1042         FILE *fp;
1043
1044         if ( !p_client_buffer || client_buffer_size <= 0 )
1045                 return;
1046
1047         /* yeah, slow... :/ */
1048         if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep))
1049         {
1050                 snprintf( p_client_buffer, client_buffer_size, "no ac_adapters?" );
1051                 return; 
1052         }
1053
1054         snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf );
1055          
1056
1057         fp = open_file(buf2, &rep);
1058         if (!fp) {
1059                 snprintf( p_client_buffer, client_buffer_size, "No ac adapter found.... where is it?" );
1060                 return;
1061         }
1062         memset(buf,0,sizeof(buf));
1063         fscanf(fp, "%*s %99s", buf );
1064         fclose(fp);
1065
1066         snprintf( p_client_buffer, client_buffer_size, "%s", buf );
1067
1068         return;
1069 }
1070
1071 /*
1072 /proc/acpi/thermal_zone/THRM/cooling_mode
1073 cooling mode:            active
1074 /proc/acpi/thermal_zone/THRM/polling_frequency
1075 <polling disabled>
1076 /proc/acpi/thermal_zone/THRM/state
1077 state:                   ok
1078 /proc/acpi/thermal_zone/THRM/temperature
1079 temperature:             45 C
1080 /proc/acpi/thermal_zone/THRM/trip_points
1081 critical (S5):           73 C
1082 passive:                 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1083 */
1084
1085 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1086 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1087
1088 int open_acpi_temperature(const char *name)
1089 {
1090         char path[256];
1091         char buf[256];
1092         int fd;
1093
1094         if (name == NULL || strcmp(name, "*") == 0) {
1095                 static int rep;
1096                 if (!get_first_file_in_a_directory
1097                     (ACPI_THERMAL_DIR, buf, &rep))
1098                         return -1;
1099                 name = buf;
1100         }
1101
1102         snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1103
1104         fd = open(path, O_RDONLY);
1105         if (fd < 0)
1106                 ERR("can't open '%s': %s", path, strerror(errno));
1107
1108         return fd;
1109 }
1110
1111 static double last_acpi_temp;
1112 static double last_acpi_temp_time;
1113
1114 double get_acpi_temperature(int fd)
1115 {
1116         if (fd <= 0)
1117                 return 0;
1118
1119         /* don't update acpi temperature too often */
1120         if (current_update_time - last_acpi_temp_time < 11.32) {
1121                 return last_acpi_temp;
1122         }
1123         last_acpi_temp_time = current_update_time;
1124
1125         /* seek to beginning */
1126         lseek(fd, 0, SEEK_SET);
1127
1128         /* read */
1129         {
1130                 char buf[256];
1131                 int n;
1132                 n = read(fd, buf, 255);
1133                 if (n < 0)
1134                         ERR("can't read fd %d: %s", fd, strerror(errno));
1135                 else {
1136                         buf[n] = '\0';
1137                         sscanf(buf, "temperature: %lf", &last_acpi_temp);
1138                 }
1139         }
1140
1141         return last_acpi_temp;
1142 }
1143
1144 /*
1145 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info 
1146 present:                 yes
1147 design capacity:         4400 mAh
1148 last full capacity:      4064 mAh
1149 battery technology:      rechargeable
1150 design voltage:          14800 mV
1151 design capacity warning: 300 mAh
1152 design capacity low:     200 mAh
1153 capacity granularity 1:  32 mAh
1154 capacity granularity 2:  32 mAh
1155 model number:            02KT
1156 serial number:           16922
1157 battery type:            LION
1158 OEM info:                SANYO
1159 */
1160
1161 /*
1162 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1163 present:                 yes
1164 capacity state:          ok
1165 charging state:          unknown
1166 present rate:            0 mA
1167 remaining capacity:      4064 mAh
1168 present voltage:         16608 mV
1169 */
1170
1171 /*
1172 2213<@jupet kellari ö> jupet@lagi-unstable:~$ cat /proc/apm 
1173 2213<@jupet kellari ö> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1174 2213<@jupet kellari ö> (-1 ollee ei akkua kiinni, koska akku on pöydällä)
1175 2214<@jupet kellari ö> jupet@lagi-unstable:~$ cat /proc/apm 
1176 2214<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1177
1178 2238<@jupet kellari ö> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1179 2239<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1180
1181 2240<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori päällä
1182 2241<@jupet kellari ö> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori päällä mutta ilman verkkovirtaa
1183 */
1184
1185 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1186 #define APM_PATH "/proc/apm"
1187
1188 static FILE *acpi_bat_fp;
1189 static FILE *apm_bat_fp;
1190
1191 static int acpi_last_full;
1192
1193 static char last_battery_str[64];
1194
1195 static double last_battery_time;
1196
1197 void get_battery_stuff(char *buf, unsigned int n, const char *bat)
1198 {
1199         static int rep, rep2;
1200         char acpi_path[128];
1201         snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1202
1203         /* don't update battery too often */
1204         if (current_update_time - last_battery_time < 29.5) {
1205                 snprintf(buf, n, "%s", last_battery_str);
1206                 return;
1207         }
1208         last_battery_time = current_update_time;
1209
1210         /* first try ACPI */
1211
1212         if (acpi_bat_fp == NULL && apm_bat_fp == NULL)
1213                 acpi_bat_fp = open_file(acpi_path, &rep);
1214
1215         if (acpi_bat_fp != NULL) {
1216                 int present_rate = -1;
1217                 int remaining_capacity = -1;
1218                 char charging_state[64];
1219
1220                 /* read last full capacity if it's zero */
1221                 if (acpi_last_full == 0) {
1222                         static int rep;
1223                         char path[128];
1224                         FILE *fp;
1225                         snprintf(path, 127,
1226                                  ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1227                         fp = open_file(path, &rep);
1228                         if (fp != NULL) {
1229                                 while (!feof(fp)) {
1230                                         char b[256];
1231                                         if (fgets(b, 256, fp) == NULL)
1232                                                 break;
1233                                         if (sscanf(b, "last full capacity: %d", &acpi_last_full) != 0) {
1234                                                 break;
1235                                         }
1236                                 }
1237
1238                                 fclose(fp);
1239                         }
1240                 }
1241
1242                 fseek(acpi_bat_fp, 0, SEEK_SET);
1243
1244                 strcpy(charging_state, "unknown");
1245
1246                 while (!feof(acpi_bat_fp)) {
1247                         char buf[256];
1248                         if (fgets(buf, 256, acpi_bat_fp) == NULL)
1249                                 break;
1250
1251                         /* let's just hope units are ok */
1252                         if (buf[0] == 'c')
1253                                 sscanf(buf, "charging state: %63s",
1254                                        charging_state);
1255                         else if (buf[0] == 'p')
1256                                 sscanf(buf, "present rate: %d",
1257                                        &present_rate);
1258                         else if (buf[0] == 'r')
1259                                 sscanf(buf, "remaining capacity: %d",
1260                                        &remaining_capacity);
1261                 }
1262
1263                 /* charging */
1264                 if (strcmp(charging_state, "charging") == 0) {
1265                         if (acpi_last_full != 0 && present_rate > 0) {
1266                                 strcpy(last_battery_str, "charging ");
1267                                 format_seconds(last_battery_str + 9,
1268                                                63 - 9,
1269                                                (acpi_last_full -
1270                                                 remaining_capacity) * 60 *
1271                                                60 / present_rate);
1272                         } else if (acpi_last_full != 0
1273                                    && present_rate <= 0) {
1274                                 sprintf(last_battery_str, "charging %d%%",
1275                                         remaining_capacity * 100 /
1276                                         acpi_last_full);
1277                         } else {
1278                                 strcpy(last_battery_str, "charging");
1279                         }
1280                 }
1281                 /* discharging */
1282                 else if (strcmp(charging_state, "discharging") == 0) {
1283                         if (present_rate > 0)
1284                                 format_seconds(last_battery_str, 63,
1285                                                (remaining_capacity * 60 *
1286                                                 60) / present_rate);
1287                         else
1288                                 sprintf(last_battery_str,
1289                                         "discharging %d%%",
1290                                         remaining_capacity * 100 /
1291                                         acpi_last_full);
1292                 }
1293                 /* charged */
1294                 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1295                 else if (strcmp(charging_state, "charged") == 0) {
1296                                 strcpy(last_battery_str, "charged");
1297                 }
1298                 /* unknown, probably full / AC */
1299                 else {
1300                         if (acpi_last_full != 0
1301                             && remaining_capacity != acpi_last_full)
1302                                 sprintf(last_battery_str, "unknown %d%%",
1303                                         remaining_capacity * 100 /
1304                                         acpi_last_full);
1305                         else
1306                                 strcpy(last_battery_str, "AC");
1307                 }
1308         } else {
1309                 /* APM */
1310                 if (apm_bat_fp == NULL)
1311                         apm_bat_fp = open_file(APM_PATH, &rep2);
1312
1313                 if (apm_bat_fp != NULL) {
1314                         int ac, status, flag, life;
1315
1316                         fscanf(apm_bat_fp,
1317                                "%*s %*s %*x %x   %x       %x     %d%%",
1318                                &ac, &status, &flag, &life);
1319
1320                         if (life == -1) {
1321                                 /* could check now that there is ac */
1322                                 snprintf(last_battery_str, 64, "AC");
1323                         } else if (ac && life != 100) { /* could check that status==3 here? */
1324                                 snprintf(last_battery_str, 64,
1325                                          "charging %d%%", life);
1326                         } else {
1327                                 snprintf(last_battery_str, 64, "%d%%",
1328                                          life);
1329                         }
1330
1331                         /* it seemed to buffer it so file must be closed (or could use syscalls
1332                          * directly but I don't feel like coding it now) */
1333                         fclose(apm_bat_fp);
1334                         apm_bat_fp = NULL;
1335                 }
1336         }
1337
1338         snprintf(buf, n, "%s", last_battery_str);
1339 }
1340
1341 /* On Apple powerbook and ibook:
1342 $ cat /proc/pmu/battery_0
1343 flags      : 00000013
1344 charge     : 3623
1345 max_charge : 3720
1346 current    : 388
1347 voltage    : 16787
1348 time rem.  : 900
1349 $ cat /proc/pmu/info
1350 PMU driver version     : 2
1351 PMU firmware version   : 0c
1352 AC Power               : 1
1353 Battery count          : 1
1354 */
1355
1356 /* defines as in <linux/pmu.h> */
1357 #define PMU_BATT_PRESENT        0x00000001
1358 #define PMU_BATT_CHARGING       0x00000002
1359
1360 static FILE* pmu_battery_fp;
1361 static FILE* pmu_info_fp;
1362 static char pb_battery_info[3][32];
1363 static double pb_battery_info_update;
1364  
1365 #define PMU_PATH "/proc/pmu"
1366 void get_powerbook_batt_info(char *buf, size_t n, int i)
1367 {
1368         static int rep;
1369         const char* batt_path = PMU_PATH "/battery_0";
1370         const char* info_path = PMU_PATH "/info";
1371         int flags, charge, max_charge, ac = -1;
1372         long time = -1;
1373
1374         /* don't update battery too often */
1375         if (current_update_time - pb_battery_info_update < 29.5) {
1376                 snprintf(buf, n, "%s", pb_battery_info[i]);
1377                 return;
1378         }
1379         pb_battery_info_update = current_update_time;
1380
1381         if (pmu_battery_fp == NULL)
1382                 pmu_battery_fp = open_file(batt_path, &rep);
1383
1384         if (pmu_battery_fp != NULL) {
1385                 rewind(pmu_battery_fp);
1386                 while (!feof(pmu_battery_fp)) {
1387                         char buf[32];
1388                         if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL)
1389                                 break;
1390
1391                         if (buf[0] == 'f')
1392                                 sscanf(buf, "flags      : %8x", &flags);
1393                         else if (buf[0] == 'c' && buf[1] == 'h')
1394                                 sscanf(buf, "charge     : %d", &charge);
1395                         else if (buf[0] == 'm')
1396                                 sscanf(buf, "max_charge : %d", &max_charge);
1397                         else if (buf[0] == 't')
1398                                 sscanf(buf, "time rem.  : %ld", &time);
1399                 }
1400         }
1401         if (pmu_info_fp == NULL)
1402                 pmu_info_fp = open_file(info_path, &rep);
1403
1404         if (pmu_info_fp != NULL) {
1405                 rewind(pmu_info_fp);
1406                 while (!feof(pmu_info_fp)) {
1407                         char buf[32];
1408                         if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL)
1409                                 break;
1410                         if (buf[0] == 'A')
1411                                 sscanf(buf, "AC Power               : %d", &ac);
1412                 }
1413         }
1414         /* update status string */
1415         if ((ac && !(flags & PMU_BATT_PRESENT)))
1416                 strcpy(pb_battery_info[PB_BATT_STATUS], "AC");
1417         else if (ac && (flags & PMU_BATT_PRESENT)
1418                   && !(flags & PMU_BATT_CHARGING))
1419                 strcpy(pb_battery_info[PB_BATT_STATUS], "charged");
1420         else if ((flags & PMU_BATT_PRESENT)
1421                 && (flags & PMU_BATT_CHARGING))
1422                 strcpy(pb_battery_info[PB_BATT_STATUS], "charging");
1423         else
1424                 strcpy(pb_battery_info[PB_BATT_STATUS], "discharging");
1425
1426         /* update percentage string */
1427         if (time == 0)
1428                 pb_battery_info[PB_BATT_PERCENT][0] = 0; 
1429         else
1430                 snprintf(pb_battery_info[PB_BATT_PERCENT],
1431                         sizeof(pb_battery_info[PB_BATT_PERCENT]),
1432                         "%d%%", (charge * 100)/max_charge);
1433
1434         /* update time string */
1435         if (time == 0) /* fully charged or battery not present */
1436                 pb_battery_info[PB_BATT_TIME][0] = 0; 
1437         else if (time < 60*60) /* don't show secs */
1438                 format_seconds_short(pb_battery_info[PB_BATT_TIME],
1439                         sizeof(pb_battery_info[PB_BATT_TIME]), time);
1440         else
1441                 format_seconds(pb_battery_info[PB_BATT_TIME],
1442                         sizeof(pb_battery_info[PB_BATT_TIME]), time);
1443
1444         snprintf(buf, n, "%s", pb_battery_info[i]);
1445 }
1446
1447 void update_top()
1448 {
1449         show_nice_processes = 1;
1450         process_find_top(info.cpu, info.memu);
1451         info.first_process = get_first_process();
1452 }
1453
1454
1455 /*
1456  *  The following ifdefs were adapted from gkrellm
1457  */
1458 #include <linux/major.h>
1459
1460 #if ! defined (MD_MAJOR)
1461 #define MD_MAJOR 9
1462 #endif
1463
1464 #if !defined(LVM_BLK_MAJOR)
1465 #define LVM_BLK_MAJOR 58
1466 #endif
1467
1468 #if !defined(NBD_MAJOR)
1469 #define NBD_MAJOR 43
1470 #endif
1471
1472 void update_diskio()
1473 {
1474         static unsigned int last = UINT_MAX;
1475         static FILE* fp;
1476
1477         char buf[512];
1478         int major, minor;
1479         unsigned int current = 0;
1480         unsigned int reads, writes = 0;
1481         int col_count = 0;
1482
1483         if (!fp) {
1484                 fp = fopen("/proc/diskstats", "r");
1485         } else {
1486                 fseek(fp, 0, SEEK_SET);
1487         }
1488
1489         /* read reads and writes from all disks (minor = 0), including
1490          * cd-roms and floppies, and summ them up
1491          */
1492         current = 0;
1493         while (!feof(fp)) {
1494                 fgets(buf, 512, fp);
1495                 col_count = sscanf(buf, "%u %u %*s %*u %*u %u %*u %*u %*u %u",
1496                                    &major, &minor, &reads, &writes);
1497                 /* ignore subdevices (they have only 3 matching entries in their line)
1498                  * and virtual devices (LVM, network block devices, RAM disks, Loopback)
1499                  *
1500                  * XXX ignore devices which are part of a SW RAID (MD_MAJOR)
1501                  */
1502                 if (col_count > 3 &&
1503                     major != LVM_BLK_MAJOR && major != NBD_MAJOR &&
1504                     major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
1505                         current += reads + writes;
1506                 }
1507         }
1508
1509         /* since the values in /proc/diststats are absolute, we have
1510          * to substract our last reading. The numbers stand for
1511          * "sectors read", and we therefore have to divide by two to
1512          * get KB */
1513         int tot = ((double)(current-last)/2);
1514         if (last > current) {
1515                 /* we hit this either if it's the very first time we
1516                  * run this, or when /proc/diskstats overflows; while
1517                  * 0 is not correct, it's at least not way off */
1518                 tot = 0;
1519         }
1520         last = current;
1521
1522         diskio_value = tot;
1523 }
1524
1525 /* Here come the IBM ACPI-specific things. For reference, see
1526  http://ibm-acpi.sourceforge.net/README
1527 If IBM ACPI is installed, /proc/acpi/ibm contains the following files:
1528 bay
1529 beep
1530 bluetooth
1531 brightness
1532 cmos
1533 dock
1534 driver
1535 ecdump
1536 fan
1537 hotkey
1538 led
1539 light
1540 thermal
1541 video
1542 volume
1543 The content of these files is described in detail in the aforementioned
1544 README - some of them also in the following functions accessing them.
1545 Peter Tarjan (ptarjan@citromail.hu)
1546 */
1547
1548 #define IBM_ACPI_DIR "/proc/acpi/ibm"
1549
1550 void get_ibm_acpi_fan( char * p_client_buffer, size_t client_buffer_size )
1551 {
1552 /* get fan speed on IBM/Lenovo laptops running the ibm acpi.
1553    /proc/acpi/ibm/fan looks like this (3 lines):
1554 status:         disabled
1555 speed:          2944
1556 commands:       enable, disable
1557 Peter Tarjan (ptarjan@citromail.hu)
1558 */
1559
1560     if ( !p_client_buffer || client_buffer_size <= 0 )
1561         return;
1562     
1563     FILE *fp;
1564     unsigned int speed=0;
1565     char fan[128];
1566     snprintf(fan, 127, "%s/fan",IBM_ACPI_DIR);    
1567
1568     fp = fopen(fan, "r");
1569     if (fp != NULL)
1570     {
1571         while (!feof(fp))
1572         {
1573             char line[256];
1574             if (fgets(line, 255, fp) == NULL) break;
1575             if (sscanf(line, "speed: %d", &speed)) break;       
1576         }
1577     }
1578     else 
1579     {
1580         CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove ibm* from your Conky config file.", fan, strerror(errno));
1581     }
1582
1583     fclose(fp);
1584     snprintf( p_client_buffer, client_buffer_size, "%d", speed );
1585     return;
1586     
1587 }    
1588
1589 static double last_ibm_acpi_temp_time;
1590 void get_ibm_acpi_temps()
1591 {
1592 /* get the measured temperatures from the temperature sensors 
1593    on IBM/Lenovo laptops running the ibm acpi.
1594    There are 8 values in /proc/acpi/ibm/thermal, and according to 
1595    http://ibm-acpi.sourceforge.net/README
1596    these mean the following (at least on an IBM R51...)
1597 0:  CPU (also on the T series laptops)
1598 1:  Mini PCI Module (?)
1599 2:  HDD (?)
1600 3:  GPU (also on the T series laptops)
1601 4:  Battery (?)
1602 5:  N/A
1603 6:  Battery (?)
1604 7:  N/A 
1605    I'm not too sure about those with the question mark, but the values I'm 
1606    reading from *my* thermal file (on a T42p) look realistic for the 
1607    hdd and the battery. 
1608    #5 and #7 are always -128. 
1609    /proc/acpi/ibm/thermal looks like this (1 line):
1610 temperatures:   41 43 31 46 33 -128 29 -128
1611 Peter Tarjan (ptarjan@citromail.hu)
1612 */
1613
1614 /*    don't update too often */
1615     if (current_update_time - last_ibm_acpi_temp_time < 10.00) 
1616     {
1617         return;
1618     }
1619     last_ibm_acpi_temp_time = current_update_time; 
1620     
1621 /*    if ( !p_client_buffer || client_buffer_size <= 0 )
1622       return; */
1623
1624     FILE *fp;
1625
1626     char thermal[128];
1627     snprintf(thermal, 127, "%s/thermal",IBM_ACPI_DIR);    
1628     fp = fopen(thermal, "r");
1629
1630     if (fp != NULL)
1631     {
1632         while (!feof(fp))
1633         {
1634             char line[256];
1635             if (fgets(line, 255, fp) == NULL) break;
1636             if (sscanf(line, "temperatures: %d %d %d %d %d %d %d %d",
1637                        &ibm_acpi.temps[0], &ibm_acpi.temps[1],
1638                        &ibm_acpi.temps[2], &ibm_acpi.temps[3],
1639                        &ibm_acpi.temps[4], &ibm_acpi.temps[5], 
1640                        &ibm_acpi.temps[6], &ibm_acpi.temps[7])) break;
1641         }
1642     }
1643     else 
1644     {
1645         CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove ibm* from your Conky config file.", thermal, strerror(errno));
1646     }
1647
1648     fclose(fp);
1649
1650 }              
1651
1652
1653 void get_ibm_acpi_volume( char * p_client_buffer, size_t client_buffer_size )
1654 {
1655
1656 /* get volume (0-14) on IBM/Lenovo laptops running the ibm acpi.
1657    "Volume" here is none of the mixer volumes, but a "master of masters"
1658    volume adjusted by the IBM volume keys.
1659    /proc/acpi/ibm/fan looks like this (4 lines):
1660 level:          4
1661 mute:           off
1662 commands:       up, down, mute
1663 commands:       level <level> (<level> is 0-15)
1664 Peter Tarjan (ptarjan@citromail.hu)
1665 */
1666     
1667     if ( !p_client_buffer || client_buffer_size <= 0 )
1668         return;
1669     
1670     FILE *fp;
1671
1672     char volume[128];
1673     snprintf(volume, 127, "%s/volume",IBM_ACPI_DIR);    
1674     unsigned int vol=-1;
1675     char mute[3]="";
1676
1677     fp = fopen(volume, "r");
1678     if (fp != NULL)
1679     {
1680         while (!feof(fp))
1681         {
1682             char line[256];
1683             if (fgets(line, 255, fp) == NULL) break;
1684             if (sscanf(line, "level: %d", &vol)) continue;
1685             if (sscanf(line, "mute: %s", mute)) break;
1686         }
1687     }
1688     else 
1689     {
1690         CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove ibm* from your Conky config file.", volume, strerror(errno));
1691     }
1692
1693     fclose(fp);
1694
1695     if (strcmp(mute, "on")==0)
1696     {
1697         snprintf( p_client_buffer, client_buffer_size, "%s", "mute" );
1698         return;
1699     }
1700     else
1701     {
1702         snprintf( p_client_buffer, client_buffer_size, "%d", vol );
1703         return;
1704     }
1705
1706 }
1707
1708 /*static FILE *fp=NULL;*/
1709
1710 void get_ibm_acpi_brightness(char * p_client_buffer, size_t client_buffer_size)
1711 {
1712 /* get LCD brightness on IBM/Lenovo laptops running the ibm acpi.
1713    /proc/acpi/ibm/brightness looks like this (3 lines):
1714 level:          7
1715 commands:       up, down
1716 commands:       level <level> (<level> is 0-7)
1717 Peter Tarjan (ptarjan@citromail.hu)
1718 */
1719
1720     if ( !p_client_buffer || client_buffer_size <= 0 )
1721         return;
1722
1723     FILE *fp;
1724     unsigned int brightness=0;
1725     char filename[128];
1726     snprintf(filename, 127, "%s/brightness",IBM_ACPI_DIR);    
1727
1728     fp = fopen(filename, "r");
1729     if (fp != NULL)
1730     {
1731         while (!feof(fp))
1732         {
1733             char line[256];
1734             if (fgets(line, 255, fp) == NULL) break;
1735             if (sscanf(line, "level: %d", &brightness)) break;  
1736         }
1737     }
1738     else 
1739     {
1740         CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove ibm* from your Conky config file.", filename, strerror(errno));
1741     }
1742
1743     fclose(fp);
1744
1745     snprintf( p_client_buffer, client_buffer_size, "%d", brightness );
1746     return;
1747     
1748 }