Make a description of ${cpu} variable not so confusing.
[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 char 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 0;
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 1;
880         }
881         
882         cpu++;
883         f = fopen("/proc/cpuinfo", "r");                //open the CPU information file
884         if (!f) {
885                 perror("Conky: Failed to access '/proc/cpuinfo' at get_freq()");
886                 return 0;
887         }
888
889         while (fgets(s, sizeof(s), f) != NULL){         //read the file
890
891 #if defined(__i386) || defined(__x86_64)
892                 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {        //and search for the cpu mhz
893 #else
894 #if defined(__alpha)
895                 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {          // different on alpha
896 #else
897                 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {  // this is different on ppc for some reason
898 #endif // defined(__alpha)
899 #endif // defined(__i386) || defined(__x86_64)
900
901                 strcpy(frequency, strchr(s, ':') + 2);  //copy just the number
902 #if defined(__alpha)
903                 frequency[strlen(frequency) - 6] = '\0';// strip " est.\n"
904                 freq = strtod(frequency, NULL)/1000000; // kernel reports in Hz 
905 #else
906                 frequency[strlen(frequency) - 1] = '\0'; // strip \n
907                 freq = strtod(frequency, NULL);
908 #endif
909                 break;
910                 }
911                 if (strncmp(s, "processor", 9) == 0) {
912                     cpu--; 
913                     continue;
914                 }
915                 
916         }
917         
918         fclose(f);
919         snprintf( p_client_buffer, client_buffer_size, p_format, (float)freq/divisor );
920         return 1;
921 }
922
923 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
924
925 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
926 char get_voltage( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor, unsigned int cpu )
927 {
928 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks 
929    something like this:
930 # frequency voltage
931 1800000 1340
932 1600000 1292
933 1400000 1100
934 1200000 988
935 1000000 1116
936 800000 1004
937 600000 988
938 */
939
940 /* Peter Tarjan (ptarjan@citromail.hu) */
941         FILE *f;
942         char s[256];
943         int freq = 0;
944         int voltage = 0;
945         char current_freq_file[128];
946         int freq_comp = 0;
947         
948
949 /* build the voltage file name */
950         cpu--;
951         snprintf(current_freq_file, 127, "%s/cpu%d/%s",
952                  CPUFREQ_PREFIX, cpu, CPUFREQ_POSTFIX);
953
954         if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 )
955                 return 0;
956
957         /* read the current cpu frequency from the /sys node */
958         f = fopen(current_freq_file, "r");
959         if (f) {
960             if (fgets(s, sizeof(s), f)) {
961                 s[strlen(s)-1] = '\0';
962                 freq = strtod(s, NULL);
963             }
964             fclose(f);
965         } else {
966                 fprintf(stderr, "Conky: Failed to access '%s' at ", current_freq_file);
967                 perror("get_voltage()");
968                 if (f) {
969                         fclose(f);
970                 }
971                 return 0;
972             }
973
974         snprintf(current_freq_file, 127, "%s/cpu%d/%s",
975                  CPUFREQ_PREFIX, cpu, CPUFREQ_VOLTAGE);
976
977 /* use the current cpu frequency to find the corresponding voltage */
978         f = fopen(current_freq_file, "r");
979
980         if (f) {
981                 while (!feof(f)) {
982                         char line[256];
983                         if (fgets(line, 255, f) == NULL) break;
984                         sscanf(line, "%d %d", &freq_comp, &voltage);
985                         if(freq_comp == freq) break;
986                 }
987                 fclose(f);
988         } else {
989                 fprintf(stderr, "Conky: Failed to access '%s' at ", current_freq_file);
990                 perror("get_voltage()");
991                 if (f) {
992                         fclose(f);
993                 }
994                 return 0;
995         }
996         snprintf( p_client_buffer, client_buffer_size, p_format, (float)voltage/divisor );
997         return 1;
998
999 }
1000
1001 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1002
1003 void get_acpi_fan( char * p_client_buffer, size_t client_buffer_size )
1004 {
1005         static int rep;
1006         char buf[256];
1007         char buf2[256];
1008         FILE *fp;
1009
1010         if ( !p_client_buffer || client_buffer_size <= 0 )
1011                 return;
1012
1013         /* yeah, slow... :/ */
1014         if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep))
1015         {
1016                 snprintf( p_client_buffer, client_buffer_size, "no fans?" );
1017                 return;
1018         }
1019
1020         snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf );
1021
1022         fp = open_file(buf2, &rep);
1023         if (!fp) {
1024                 snprintf( p_client_buffer, client_buffer_size, "can't open fan's state file" );
1025                 return;
1026         }
1027         memset(buf,0,sizeof(buf));
1028         fscanf(fp, "%*s %99s", buf);
1029         fclose(fp);
1030
1031         snprintf( p_client_buffer, client_buffer_size, "%s", buf );
1032
1033         return;
1034 }
1035
1036 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1037
1038 void get_acpi_ac_adapter( char * p_client_buffer, size_t client_buffer_size )
1039 {
1040         static int rep;
1041         char buf[256];
1042         char buf2[256];
1043         FILE *fp;
1044
1045         if ( !p_client_buffer || client_buffer_size <= 0 )
1046                 return;
1047
1048         /* yeah, slow... :/ */
1049         if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep))
1050         {
1051                 snprintf( p_client_buffer, client_buffer_size, "no ac_adapters?" );
1052                 return; 
1053         }
1054
1055         snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf );
1056          
1057
1058         fp = open_file(buf2, &rep);
1059         if (!fp) {
1060                 snprintf( p_client_buffer, client_buffer_size, "No ac adapter found.... where is it?" );
1061                 return;
1062         }
1063         memset(buf,0,sizeof(buf));
1064         fscanf(fp, "%*s %99s", buf );
1065         fclose(fp);
1066
1067         snprintf( p_client_buffer, client_buffer_size, "%s", buf );
1068
1069         return;
1070 }
1071
1072 /*
1073 /proc/acpi/thermal_zone/THRM/cooling_mode
1074 cooling mode:            active
1075 /proc/acpi/thermal_zone/THRM/polling_frequency
1076 <polling disabled>
1077 /proc/acpi/thermal_zone/THRM/state
1078 state:                   ok
1079 /proc/acpi/thermal_zone/THRM/temperature
1080 temperature:             45 C
1081 /proc/acpi/thermal_zone/THRM/trip_points
1082 critical (S5):           73 C
1083 passive:                 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1084 */
1085
1086 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1087 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1088
1089 int open_acpi_temperature(const char *name)
1090 {
1091         char path[256];
1092         char buf[256];
1093         int fd;
1094
1095         if (name == NULL || strcmp(name, "*") == 0) {
1096                 static int rep;
1097                 if (!get_first_file_in_a_directory
1098                     (ACPI_THERMAL_DIR, buf, &rep))
1099                         return -1;
1100                 name = buf;
1101         }
1102
1103         snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1104
1105         fd = open(path, O_RDONLY);
1106         if (fd < 0)
1107                 ERR("can't open '%s': %s", path, strerror(errno));
1108
1109         return fd;
1110 }
1111
1112 static double last_acpi_temp;
1113 static double last_acpi_temp_time;
1114
1115 double get_acpi_temperature(int fd)
1116 {
1117         if (fd <= 0)
1118                 return 0;
1119
1120         /* don't update acpi temperature too often */
1121         if (current_update_time - last_acpi_temp_time < 11.32) {
1122                 return last_acpi_temp;
1123         }
1124         last_acpi_temp_time = current_update_time;
1125
1126         /* seek to beginning */
1127         lseek(fd, 0, SEEK_SET);
1128
1129         /* read */
1130         {
1131                 char buf[256];
1132                 int n;
1133                 n = read(fd, buf, 255);
1134                 if (n < 0)
1135                         ERR("can't read fd %d: %s", fd, strerror(errno));
1136                 else {
1137                         buf[n] = '\0';
1138                         sscanf(buf, "temperature: %lf", &last_acpi_temp);
1139                 }
1140         }
1141
1142         return last_acpi_temp;
1143 }
1144
1145 /*
1146 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info 
1147 present:                 yes
1148 design capacity:         4400 mAh
1149 last full capacity:      4064 mAh
1150 battery technology:      rechargeable
1151 design voltage:          14800 mV
1152 design capacity warning: 300 mAh
1153 design capacity low:     200 mAh
1154 capacity granularity 1:  32 mAh
1155 capacity granularity 2:  32 mAh
1156 model number:            02KT
1157 serial number:           16922
1158 battery type:            LION
1159 OEM info:                SANYO
1160 */
1161
1162 /*
1163 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1164 present:                 yes
1165 capacity state:          ok
1166 charging state:          unknown
1167 present rate:            0 mA
1168 remaining capacity:      4064 mAh
1169 present voltage:         16608 mV
1170 */
1171
1172 /*
1173 2213<@jupet kellari ö> jupet@lagi-unstable:~$ cat /proc/apm 
1174 2213<@jupet kellari ö> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1175 2213<@jupet kellari ö> (-1 ollee ei akkua kiinni, koska akku on pöydällä)
1176 2214<@jupet kellari ö> jupet@lagi-unstable:~$ cat /proc/apm 
1177 2214<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1178
1179 2238<@jupet kellari ö> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1180 2239<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1181
1182 2240<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori päällä
1183 2241<@jupet kellari ö> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori päällä mutta ilman verkkovirtaa
1184 */
1185
1186 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1187 #define APM_PATH "/proc/apm"
1188
1189 static FILE *acpi_bat_fp;
1190 static FILE *apm_bat_fp;
1191
1192 static int acpi_last_full;
1193
1194 static char last_battery_str[64];
1195
1196 static double last_battery_time;
1197
1198 void get_battery_stuff(char *buf, unsigned int n, const char *bat)
1199 {
1200         static int rep, rep2;
1201         char acpi_path[128];
1202         snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1203
1204         /* don't update battery too often */
1205         if (current_update_time - last_battery_time < 29.5) {
1206                 snprintf(buf, n, "%s", last_battery_str);
1207                 return;
1208         }
1209         last_battery_time = current_update_time;
1210
1211         /* first try ACPI */
1212
1213         if (acpi_bat_fp == NULL && apm_bat_fp == NULL)
1214                 acpi_bat_fp = open_file(acpi_path, &rep);
1215
1216         if (acpi_bat_fp != NULL) {
1217                 int present_rate = -1;
1218                 int remaining_capacity = -1;
1219                 char charging_state[64];
1220                 char present[4];
1221
1222                 /* read last full capacity if it's zero */
1223                 if (acpi_last_full == 0) {
1224                         static int rep;
1225                         char path[128];
1226                         FILE *fp;
1227                         snprintf(path, 127,
1228                                  ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1229                         fp = open_file(path, &rep);
1230                         if (fp != NULL) {
1231                                 while (!feof(fp)) {
1232                                         char b[256];
1233                                         if (fgets(b, 256, fp) == NULL)
1234                                                 break;
1235                                         if (sscanf(b, "last full capacity: %d", &acpi_last_full) != 0) {
1236                                                 break;
1237                                         }
1238                                 }
1239
1240                                 fclose(fp);
1241                         }
1242                 }
1243
1244                 fseek(acpi_bat_fp, 0, SEEK_SET);
1245
1246                 strcpy(charging_state, "unknown");
1247
1248                 while (!feof(acpi_bat_fp)) {
1249                         char buf[256];
1250                         if (fgets(buf, 256, acpi_bat_fp) == NULL)
1251                                 break;
1252
1253                         /* let's just hope units are ok */
1254                         if (buf[0] == 'p') {
1255                                 sscanf(buf, "present: %4s", present);
1256                         } else if (buf[0] == 'c')
1257                                 sscanf(buf, "charging state: %63s",
1258                                        charging_state);
1259                         else if (buf[0] == 'p')
1260                                 sscanf(buf, "present rate: %d",
1261                                        &present_rate);
1262                         else if (buf[0] == 'r')
1263                                 sscanf(buf, "remaining capacity: %d",
1264                                        &remaining_capacity);
1265                 }
1266                 
1267                 /* not present */
1268                 if (strcmp(present, "no") == 0) {
1269                         strncpy(last_battery_str, "not present", 64);
1270                 }
1271                 /* charging */
1272                 else if (strcmp(charging_state, "charging") == 0) {
1273                         if (acpi_last_full != 0 && present_rate > 0) {
1274                                 snprintf(last_battery_str, 63, "charging %i%% ", (int) (remaining_capacity / acpi_last_full) * 100);
1275                                 format_seconds(last_battery_str + 14,
1276                                                63 - 14,
1277                                                (acpi_last_full -
1278                                                 remaining_capacity) * 60 *
1279                                                60 / present_rate);
1280                         } else if (acpi_last_full != 0
1281                                    && present_rate <= 0) {
1282                                 snprintf(last_battery_str, 64, "charging %d%%",
1283                                         remaining_capacity * 100 /
1284                                         acpi_last_full);
1285                         } else {
1286                                 strncpy(last_battery_str, "charging", 63);
1287                         }
1288                 }
1289                 /* discharging */
1290                 else if (strncmp(charging_state, "discharging", 64) == 0) {
1291                         if (present_rate > 0) {
1292                                 snprintf(last_battery_str, 63, "discharging %i%% ", (int)(remaining_capacity / acpi_last_full) * 100);
1293                                 format_seconds(last_battery_str + 17, 63 - 17,
1294                                                (remaining_capacity * 60 *
1295                                                 60) / present_rate);
1296                         } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1297                                 snprintf(last_battery_str, 64, "full");
1298                         } else {
1299                                 snprintf(last_battery_str, 64,
1300                                         "discharging %d%%",
1301                                         remaining_capacity * 100 /
1302                                         acpi_last_full);
1303                         }
1304                 }
1305                 /* charged */
1306                 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1307                 else if (strncmp(charging_state, "charged", 64) == 0) {
1308                                 strcpy(last_battery_str, "charged");
1309                 } 
1310                 /* unknown, probably full / AC */
1311                 else {
1312                         if (acpi_last_full != 0
1313                             && remaining_capacity != acpi_last_full)
1314                                 snprintf(last_battery_str, 64, "unknown %d%%",
1315                                         remaining_capacity * 100 /
1316                                         acpi_last_full);
1317                         else
1318                                 strncpy(last_battery_str, "AC", 64);
1319                 }
1320         } else {
1321                 /* APM */
1322                 if (apm_bat_fp == NULL)
1323                         apm_bat_fp = open_file(APM_PATH, &rep2);
1324
1325                 if (apm_bat_fp != NULL) {
1326                         int ac, status, flag, life;
1327
1328                         fscanf(apm_bat_fp,
1329                                "%*s %*s %*x %x   %x       %x     %d%%",
1330                                &ac, &status, &flag, &life);
1331
1332                         if (life == -1) {
1333                                 /* could check now that there is ac */
1334                                 snprintf(last_battery_str, 64, "AC");
1335                         } else if (ac && life != 100) { /* could check that status==3 here? */
1336                                 snprintf(last_battery_str, 64,
1337                                          "charging %d%%", life);
1338                         } else {
1339                                 snprintf(last_battery_str, 64, "%d%%",
1340                                          life);
1341                         }
1342
1343                         /* it seemed to buffer it so file must be closed (or could use syscalls
1344                          * directly but I don't feel like coding it now) */
1345                         fclose(apm_bat_fp);
1346                         apm_bat_fp = NULL;
1347                 }
1348         }
1349
1350         snprintf(buf, n, "%s", last_battery_str);
1351 }
1352
1353 /* On Apple powerbook and ibook:
1354 $ cat /proc/pmu/battery_0
1355 flags      : 00000013
1356 charge     : 3623
1357 max_charge : 3720
1358 current    : 388
1359 voltage    : 16787
1360 time rem.  : 900
1361 $ cat /proc/pmu/info
1362 PMU driver version     : 2
1363 PMU firmware version   : 0c
1364 AC Power               : 1
1365 Battery count          : 1
1366 */
1367
1368 /* defines as in <linux/pmu.h> */
1369 #define PMU_BATT_PRESENT        0x00000001
1370 #define PMU_BATT_CHARGING       0x00000002
1371
1372 static FILE* pmu_battery_fp;
1373 static FILE* pmu_info_fp;
1374 static char pb_battery_info[3][32];
1375 static double pb_battery_info_update;
1376  
1377 #define PMU_PATH "/proc/pmu"
1378 void get_powerbook_batt_info(char *buf, size_t n, int i)
1379 {
1380         static int rep;
1381         const char* batt_path = PMU_PATH "/battery_0";
1382         const char* info_path = PMU_PATH "/info";
1383         int flags, charge, max_charge, ac = -1;
1384         long time = -1;
1385
1386         /* don't update battery too often */
1387         if (current_update_time - pb_battery_info_update < 29.5) {
1388                 snprintf(buf, n, "%s", pb_battery_info[i]);
1389                 return;
1390         }
1391         pb_battery_info_update = current_update_time;
1392
1393         if (pmu_battery_fp == NULL)
1394                 pmu_battery_fp = open_file(batt_path, &rep);
1395
1396         if (pmu_battery_fp != NULL) {
1397                 rewind(pmu_battery_fp);
1398                 while (!feof(pmu_battery_fp)) {
1399                         char buf[32];
1400                         if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL)
1401                                 break;
1402
1403                         if (buf[0] == 'f')
1404                                 sscanf(buf, "flags      : %8x", &flags);
1405                         else if (buf[0] == 'c' && buf[1] == 'h')
1406                                 sscanf(buf, "charge     : %d", &charge);
1407                         else if (buf[0] == 'm')
1408                                 sscanf(buf, "max_charge : %d", &max_charge);
1409                         else if (buf[0] == 't')
1410                                 sscanf(buf, "time rem.  : %ld", &time);
1411                 }
1412         }
1413         if (pmu_info_fp == NULL)
1414                 pmu_info_fp = open_file(info_path, &rep);
1415
1416         if (pmu_info_fp != NULL) {
1417                 rewind(pmu_info_fp);
1418                 while (!feof(pmu_info_fp)) {
1419                         char buf[32];
1420                         if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL)
1421                                 break;
1422                         if (buf[0] == 'A')
1423                                 sscanf(buf, "AC Power               : %d", &ac);
1424                 }
1425         }
1426         /* update status string */
1427         if ((ac && !(flags & PMU_BATT_PRESENT)))
1428                 strcpy(pb_battery_info[PB_BATT_STATUS], "AC");
1429         else if (ac && (flags & PMU_BATT_PRESENT)
1430                   && !(flags & PMU_BATT_CHARGING))
1431                 strcpy(pb_battery_info[PB_BATT_STATUS], "charged");
1432         else if ((flags & PMU_BATT_PRESENT)
1433                 && (flags & PMU_BATT_CHARGING))
1434                 strcpy(pb_battery_info[PB_BATT_STATUS], "charging");
1435         else
1436                 strcpy(pb_battery_info[PB_BATT_STATUS], "discharging");
1437
1438         /* update percentage string */
1439         if (time == 0)
1440                 pb_battery_info[PB_BATT_PERCENT][0] = 0; 
1441         else
1442                 snprintf(pb_battery_info[PB_BATT_PERCENT],
1443                         sizeof(pb_battery_info[PB_BATT_PERCENT]),
1444                         "%d%%", (charge * 100)/max_charge);
1445
1446         /* update time string */
1447         if (time == 0) /* fully charged or battery not present */
1448                 pb_battery_info[PB_BATT_TIME][0] = 0; 
1449         else if (time < 60*60) /* don't show secs */
1450                 format_seconds_short(pb_battery_info[PB_BATT_TIME],
1451                         sizeof(pb_battery_info[PB_BATT_TIME]), time);
1452         else
1453                 format_seconds(pb_battery_info[PB_BATT_TIME],
1454                         sizeof(pb_battery_info[PB_BATT_TIME]), time);
1455
1456         snprintf(buf, n, "%s", pb_battery_info[i]);
1457 }
1458
1459 void update_top()
1460 {
1461         show_nice_processes = 1;
1462         process_find_top(info.cpu, info.memu);
1463         info.first_process = get_first_process();
1464 }
1465
1466
1467 /*
1468  *  The following ifdefs were adapted from gkrellm
1469  */
1470 #include <linux/major.h>
1471
1472 #if ! defined (MD_MAJOR)
1473 #define MD_MAJOR 9
1474 #endif
1475
1476 #if !defined(LVM_BLK_MAJOR)
1477 #define LVM_BLK_MAJOR 58
1478 #endif
1479
1480 #if !defined(NBD_MAJOR)
1481 #define NBD_MAJOR 43
1482 #endif
1483
1484 void update_diskio()
1485 {
1486         static unsigned int last = UINT_MAX;
1487         static FILE* fp;
1488
1489         char buf[512];
1490         int major, minor;
1491         unsigned int current = 0;
1492         unsigned int reads, writes = 0;
1493         int col_count = 0;
1494
1495         if (!fp) {
1496                 fp = fopen("/proc/diskstats", "r");
1497         } else {
1498                 fseek(fp, 0, SEEK_SET);
1499         }
1500
1501         /* read reads and writes from all disks (minor = 0), including
1502          * cd-roms and floppies, and summ them up
1503          */
1504         current = 0;
1505         while (!feof(fp)) {
1506                 fgets(buf, 512, fp);
1507                 col_count = sscanf(buf, "%u %u %*s %*u %*u %u %*u %*u %*u %u",
1508                                    &major, &minor, &reads, &writes);
1509                 /* ignore subdevices (they have only 3 matching entries in their line)
1510                  * and virtual devices (LVM, network block devices, RAM disks, Loopback)
1511                  *
1512                  * XXX ignore devices which are part of a SW RAID (MD_MAJOR)
1513                  */
1514                 if (col_count > 3 &&
1515                     major != LVM_BLK_MAJOR && major != NBD_MAJOR &&
1516                     major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
1517                         current += reads + writes;
1518                 }
1519         }
1520
1521         /* since the values in /proc/diststats are absolute, we have
1522          * to substract our last reading. The numbers stand for
1523          * "sectors read", and we therefore have to divide by two to
1524          * get KB */
1525         int tot = ((double)(current-last)/2);
1526         if (last > current) {
1527                 /* we hit this either if it's the very first time we
1528                  * run this, or when /proc/diskstats overflows; while
1529                  * 0 is not correct, it's at least not way off */
1530                 tot = 0;
1531         }
1532         last = current;
1533
1534         diskio_value = tot;
1535 }
1536
1537 /* Here come the IBM ACPI-specific things. For reference, see
1538  http://ibm-acpi.sourceforge.net/README
1539 If IBM ACPI is installed, /proc/acpi/ibm contains the following files:
1540 bay
1541 beep
1542 bluetooth
1543 brightness
1544 cmos
1545 dock
1546 driver
1547 ecdump
1548 fan
1549 hotkey
1550 led
1551 light
1552 thermal
1553 video
1554 volume
1555 The content of these files is described in detail in the aforementioned
1556 README - some of them also in the following functions accessing them.
1557 Peter Tarjan (ptarjan@citromail.hu)
1558 */
1559
1560 #define IBM_ACPI_DIR "/proc/acpi/ibm"
1561
1562 void get_ibm_acpi_fan( char * p_client_buffer, size_t client_buffer_size )
1563 {
1564 /* get fan speed on IBM/Lenovo laptops running the ibm acpi.
1565    /proc/acpi/ibm/fan looks like this (3 lines):
1566 status:         disabled
1567 speed:          2944
1568 commands:       enable, disable
1569 Peter Tarjan (ptarjan@citromail.hu)
1570 */
1571
1572     if ( !p_client_buffer || client_buffer_size <= 0 )
1573         return;
1574     
1575     FILE *fp;
1576     unsigned int speed=0;
1577     char fan[128];
1578     snprintf(fan, 127, "%s/fan",IBM_ACPI_DIR);    
1579
1580     fp = fopen(fan, "r");
1581     if (fp != NULL)
1582     {
1583         while (!feof(fp))
1584         {
1585             char line[256];
1586             if (fgets(line, 255, fp) == NULL) break;
1587             if (sscanf(line, "speed: %d", &speed)) break;       
1588         }
1589     }
1590     else 
1591     {
1592         CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove ibm* from your Conky config file.", fan, strerror(errno));
1593     }
1594
1595     fclose(fp);
1596     snprintf( p_client_buffer, client_buffer_size, "%d", speed );
1597     return;
1598     
1599 }    
1600
1601 static double last_ibm_acpi_temp_time;
1602 void get_ibm_acpi_temps()
1603 {
1604 /* get the measured temperatures from the temperature sensors 
1605    on IBM/Lenovo laptops running the ibm acpi.
1606    There are 8 values in /proc/acpi/ibm/thermal, and according to 
1607    http://ibm-acpi.sourceforge.net/README
1608    these mean the following (at least on an IBM R51...)
1609 0:  CPU (also on the T series laptops)
1610 1:  Mini PCI Module (?)
1611 2:  HDD (?)
1612 3:  GPU (also on the T series laptops)
1613 4:  Battery (?)
1614 5:  N/A
1615 6:  Battery (?)
1616 7:  N/A 
1617    I'm not too sure about those with the question mark, but the values I'm 
1618    reading from *my* thermal file (on a T42p) look realistic for the 
1619    hdd and the battery. 
1620    #5 and #7 are always -128. 
1621    /proc/acpi/ibm/thermal looks like this (1 line):
1622 temperatures:   41 43 31 46 33 -128 29 -128
1623 Peter Tarjan (ptarjan@citromail.hu)
1624 */
1625
1626 /*    don't update too often */
1627     if (current_update_time - last_ibm_acpi_temp_time < 10.00) 
1628     {
1629         return;
1630     }
1631     last_ibm_acpi_temp_time = current_update_time; 
1632     
1633 /*    if ( !p_client_buffer || client_buffer_size <= 0 )
1634       return; */
1635
1636     FILE *fp;
1637
1638     char thermal[128];
1639     snprintf(thermal, 127, "%s/thermal",IBM_ACPI_DIR);    
1640     fp = fopen(thermal, "r");
1641
1642     if (fp != NULL)
1643     {
1644         while (!feof(fp))
1645         {
1646             char line[256];
1647             if (fgets(line, 255, fp) == NULL) break;
1648             if (sscanf(line, "temperatures: %d %d %d %d %d %d %d %d",
1649                        &ibm_acpi.temps[0], &ibm_acpi.temps[1],
1650                        &ibm_acpi.temps[2], &ibm_acpi.temps[3],
1651                        &ibm_acpi.temps[4], &ibm_acpi.temps[5], 
1652                        &ibm_acpi.temps[6], &ibm_acpi.temps[7])) break;
1653         }
1654     }
1655     else 
1656     {
1657         CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove ibm* from your Conky config file.", thermal, strerror(errno));
1658     }
1659
1660     fclose(fp);
1661
1662 }              
1663
1664
1665 void get_ibm_acpi_volume( char * p_client_buffer, size_t client_buffer_size )
1666 {
1667
1668 /* get volume (0-14) on IBM/Lenovo laptops running the ibm acpi.
1669    "Volume" here is none of the mixer volumes, but a "master of masters"
1670    volume adjusted by the IBM volume keys.
1671    /proc/acpi/ibm/fan looks like this (4 lines):
1672 level:          4
1673 mute:           off
1674 commands:       up, down, mute
1675 commands:       level <level> (<level> is 0-15)
1676 Peter Tarjan (ptarjan@citromail.hu)
1677 */
1678     
1679     if ( !p_client_buffer || client_buffer_size <= 0 )
1680         return;
1681     
1682     FILE *fp;
1683
1684     char volume[128];
1685     snprintf(volume, 127, "%s/volume",IBM_ACPI_DIR);    
1686     unsigned int vol=-1;
1687     char mute[3]="";
1688
1689     fp = fopen(volume, "r");
1690     if (fp != NULL)
1691     {
1692         while (!feof(fp))
1693         {
1694             char line[256];
1695             if (fgets(line, 255, fp) == NULL) break;
1696             if (sscanf(line, "level: %d", &vol)) continue;
1697             if (sscanf(line, "mute: %s", mute)) break;
1698         }
1699     }
1700     else 
1701     {
1702         CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove ibm* from your Conky config file.", volume, strerror(errno));
1703     }
1704
1705     fclose(fp);
1706
1707     if (strcmp(mute, "on")==0)
1708     {
1709         snprintf( p_client_buffer, client_buffer_size, "%s", "mute" );
1710         return;
1711     }
1712     else
1713     {
1714         snprintf( p_client_buffer, client_buffer_size, "%d", vol );
1715         return;
1716     }
1717
1718 }
1719
1720 /*static FILE *fp=NULL;*/
1721
1722 void get_ibm_acpi_brightness(char * p_client_buffer, size_t client_buffer_size)
1723 {
1724 /* get LCD brightness on IBM/Lenovo laptops running the ibm acpi.
1725    /proc/acpi/ibm/brightness looks like this (3 lines):
1726 level:          7
1727 commands:       up, down
1728 commands:       level <level> (<level> is 0-7)
1729 Peter Tarjan (ptarjan@citromail.hu)
1730 */
1731
1732     if ( !p_client_buffer || client_buffer_size <= 0 )
1733         return;
1734
1735     FILE *fp;
1736     unsigned int brightness=0;
1737     char filename[128];
1738     snprintf(filename, 127, "%s/brightness",IBM_ACPI_DIR);    
1739
1740     fp = fopen(filename, "r");
1741     if (fp != NULL)
1742     {
1743         while (!feof(fp))
1744         {
1745             char line[256];
1746             if (fgets(line, 255, fp) == NULL) break;
1747             if (sscanf(line, "level: %d", &brightness)) break;  
1748         }
1749     }
1750     else 
1751     {
1752         CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove ibm* from your Conky config file.", filename, strerror(errno));
1753     }
1754
1755     fclose(fp);
1756
1757     snprintf( p_client_buffer, client_buffer_size, "%d", brightness );
1758     return;
1759     
1760 }