how about actually reading the battery vars -- doh
[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         /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
409         if (!cpu_setup || !info.cpu_usage) {
410                 get_cpu_count();
411                 cpu_setup = 1;
412         }
413
414         if (stat_template == NULL) {
415                 stat_template = KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT ;
416         }       
417
418         if (cpu == NULL) {
419                 malloc_cpu_size = (info.cpu_count + 1) *  sizeof(struct cpu_info);
420                 cpu = malloc(malloc_cpu_size);
421                 memset(cpu, 0, malloc_cpu_size);
422         }
423
424         if (stat_fp == NULL) {
425                 stat_fp = open_file("/proc/stat", &rep);
426         } else {
427                 fseek(stat_fp, 0, SEEK_SET);
428         }
429         if (stat_fp == NULL) {
430                 return;
431         }
432         index = 0;
433         while (!feof(stat_fp)) {
434                 if (fgets(buf, 255, stat_fp) == NULL)
435                         break;
436
437                 if (strncmp(buf, "procs_running ", 14) == 0) {
438                         sscanf(buf, "%*s %hu", &info.run_procs);
439                         info.mask |= (1 << INFO_RUN_PROCS);
440                 } else if (strncmp(buf, "cpu", 3) == 0) {
441                         index = isdigit(buf[3]) ? ((int)buf[3]) - 0x2F : 0;
442                         sscanf(buf, stat_template 
443                                 , &(cpu[index].cpu_user)
444                                 , &(cpu[index].cpu_nice)
445                                 , &(cpu[index].cpu_system)
446                                 , &(cpu[index].cpu_idle)
447                                 , &(cpu[index].cpu_iowait)
448                                 , &(cpu[index].cpu_irq)
449                                 , &(cpu[index].cpu_softirq)
450                                 , &(cpu[index].cpu_steal)
451                                 );
452
453                         cpu[index].cpu_total = cpu[index].cpu_user 
454                                          + cpu[index].cpu_nice 
455                                          + cpu[index].cpu_system 
456                                          + cpu[index].cpu_idle 
457                                          + cpu[index].cpu_iowait 
458                                          + cpu[index].cpu_irq
459                                          + cpu[index].cpu_softirq
460                                          + cpu[index].cpu_steal 
461                                          ; 
462
463                         cpu[index].cpu_active_total = cpu[index].cpu_total - (cpu[index].cpu_idle + cpu[index].cpu_iowait);
464                         info.mask |= (1 << INFO_CPU);
465
466                         double delta = current_update_time - last_update_time;
467                         if (delta <= 0.001) return;     
468
469                         cpu[index].cpu_val[0] = (cpu[index].cpu_active_total -  cpu[index].cpu_last_active_total) / 
470                                                 (float )(cpu[index].cpu_total - cpu[index].cpu_last_total); 
471                         curtmp = 0;
472                         for (i=0; i < info.cpu_avg_samples; i++ ) {
473                                 curtmp += cpu[index].cpu_val[i];
474                         }
475                         /* TESTING -- I've removed this, because I don't think it is right. You shouldn't divide 
476                                       by the cpu count here ... removing for testing */
477                         /* if (index == 0) {
478                                 info.cpu_usage[index] = curtmp / info.cpu_avg_samples / info.cpu_count; 
479                         } else {
480                                 info.cpu_usage[index] = curtmp / info.cpu_avg_samples;
481                         }  */
482                         /* TESTING -- this line replaces the prev. "suspect" if/else */
483                         info.cpu_usage[index] = curtmp / info.cpu_avg_samples;
484
485                         cpu[index].cpu_last_total = cpu[index].cpu_total;
486                         cpu[index].cpu_last_active_total = cpu[index].cpu_active_total;
487                         for (i = info.cpu_avg_samples - 1; i > 0; i--) {
488                                 cpu[index].cpu_val[i] = cpu[index].cpu_val[i - 1];
489                         }
490                 }
491
492         }
493 }
494
495 void update_running_processes()
496 {
497         update_stat();
498 }
499
500 void update_cpu_usage()
501 {
502         update_stat();
503 }
504
505 void update_load_average()
506 {
507 #ifdef HAVE_GETLOADAVG
508         double v[3];
509         getloadavg(v, 3);
510         info.loadavg[0] = (float) v[0];
511         info.loadavg[1] = (float) v[1];
512         info.loadavg[2] = (float) v[2];
513 #else
514         static int rep;
515         FILE *fp;
516
517         fp = open_file("/proc/loadavg", &rep);
518         if (!fp) {
519                 v[0] = v[1] = v[2] = 0.0;
520                 return;
521         }
522
523         fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
524                &info.loadavg[2]);
525
526         fclose(fp);
527 #endif
528 }
529
530 #define PROC_I8K "/proc/i8k"
531 #define I8K_DELIM " "
532 static char *i8k_procbuf = NULL;
533 void update_i8k()
534 {
535         FILE *fp;
536         if (!i8k_procbuf) {
537                 i8k_procbuf = (char*)malloc(128*sizeof(char));
538         }
539         if ((fp = fopen(PROC_I8K,"r")) == NULL) {
540                 CRIT_ERR("/proc/i8k doesn't exist! use insmod to make sure the kernel driver is loaded...");
541         }
542
543         memset(&i8k_procbuf[0],0,128);
544         if (fread(&i8k_procbuf[0],sizeof(char),128,fp) == 0) {
545                 ERR("something wrong with /proc/i8k...");
546         }
547
548         fclose(fp);
549
550   i8k.version = strtok(&i8k_procbuf[0],I8K_DELIM);
551         i8k.bios = strtok(NULL,I8K_DELIM);
552         i8k.serial = strtok(NULL,I8K_DELIM);
553         i8k.cpu_temp = strtok(NULL,I8K_DELIM);
554         i8k.left_fan_status = strtok(NULL,I8K_DELIM);   
555         i8k.right_fan_status = strtok(NULL,I8K_DELIM);  
556         i8k.left_fan_rpm = strtok(NULL,I8K_DELIM);
557         i8k.right_fan_rpm = strtok(NULL,I8K_DELIM);
558         i8k.ac_status = strtok(NULL,I8K_DELIM);
559         i8k.buttons_status = strtok(NULL,I8K_DELIM);
560 }
561
562
563 /***********************************************************/
564 /***********************************************************/
565 /***********************************************************/
566
567 static int no_dots(const struct dirent *d)
568 {
569         if (d->d_name[0] == '.')
570                 return 0;
571         return 1;
572 }
573
574 static int
575 get_first_file_in_a_directory(const char *dir, char *s, int *rep)
576 {
577         struct dirent **namelist;
578         int i, n;
579
580         n = scandir(dir, &namelist, no_dots, alphasort);
581         if (n < 0) {
582                 if (!rep || !*rep) {
583                         ERR("scandir for %s: %s", dir, strerror(errno));
584                         if (rep)
585                                 *rep = 1;
586                 }
587                 return 0;
588         } else {
589                 if (n == 0)
590                         return 0;
591
592                 strncpy(s, namelist[0]->d_name, 255);
593                 s[255] = '\0';
594
595                 for (i = 0; i < n; i++)
596                         free(namelist[i]);
597                 free(namelist);
598
599                 return 1;
600         }
601 }
602
603 #define I2C_DIR "/sys/bus/i2c/devices/"
604
605 int
606 open_i2c_sensor(const char *dev, const char *type, int n, int *div,
607                 char *devtype)
608 {
609         char path[256];
610         char buf[256];
611         int fd;
612         int divfd;
613
614         /* if i2c device is NULL or *, get first */
615         if (dev == NULL || strcmp(dev, "*") == 0) {
616                 static int rep;
617                 if (!get_first_file_in_a_directory(I2C_DIR, buf, &rep))
618                         return -1;
619                 dev = buf;
620         }
621
622         /* change vol to in */
623         if (strcmp(type, "vol") == 0)
624                 type = "in";
625
626         if (strcmp(type, "tempf") == 0) {
627                 snprintf(path, 255, I2C_DIR "%s/%s%d_input", dev, "temp", n);
628         } else {
629                 snprintf(path, 255, I2C_DIR "%s/%s%d_input", dev, type, n);
630         }
631         strncpy(devtype, path, 255);
632
633         /* open file */
634         fd = open(path, O_RDONLY);
635         if (fd < 0) {
636                 CRIT_ERR("can't open '%s': %s\nplease fix i2c or remove it from Conky", path, strerror(errno));
637         }
638
639         if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
640             || strcmp(type, "tempf") == 0)
641                 *div = 1;
642         else
643                 *div = 0;
644         /* fan does not use *_div as a read divisor */
645         if (strcmp("fan", type) == 0)
646                 return fd;
647
648         /* test if *_div file exist, open it and use it as divisor */
649         if (strcmp(type, "tempf") == 0) {
650                 snprintf(path, 255, I2C_DIR "%s/%s%d_div", "one", "two",
651                          n);
652         } else {
653                 snprintf(path, 255, I2C_DIR "%s/%s%d_div", dev, type, n);
654         }
655
656         divfd = open(path, O_RDONLY);
657         if (divfd > 0) {
658                 /* read integer */
659                 char divbuf[64];
660                 unsigned int divn;
661                 divn = read(divfd, divbuf, 63);
662                 /* should read until n == 0 but I doubt that kernel will give these
663                  * in multiple pieces. :) */
664                 divbuf[divn] = '\0';
665                 *div = atoi(divbuf);
666         }
667
668         close(divfd);
669
670         return fd;
671 }
672
673 double get_i2c_info(int *fd, int div, char *devtype, char *type)
674 {
675         int val = 0;
676
677         if (*fd <= 0)
678                 return 0;
679
680         lseek(*fd, 0, SEEK_SET);
681
682         /* read integer */
683         {
684                 char buf[64];
685                 unsigned int n;
686                 n = read(*fd, buf, 63);
687                 /* should read until n == 0 but I doubt that kernel will give these
688                  * in multiple pieces. :) */
689                 buf[n] = '\0';
690                 val = atoi(buf);
691         }
692
693         close(*fd);
694         /* open file */
695         *fd = open(devtype, O_RDONLY);
696         if (*fd < 0)
697                 ERR("can't open '%s': %s", devtype, strerror(errno));
698
699         /* My dirty hack for computing CPU value 
700          * Filedil, from forums.gentoo.org
701          */
702 /*      if (strstr(devtype, "temp1_input") != NULL)
703         return -15.096+1.4893*(val / 1000.0); */
704
705
706         /* divide voltage and temperature by 1000 */
707         /* or if any other divisor is given, use that */
708         if (strcmp(type, "tempf") == 0) {
709                 if (div > 1)
710                         return ((val / div + 40) * 9.0 / 5) - 40;
711                 else if (div)
712                         return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
713                 else
714                         return ((val + 40) * 9.0 / 5) - 40;
715         } else {
716                 if (div > 1)
717                         return val / div;
718                 else if (div)
719                         return val / 1000.0;
720                 else
721                         return val;
722         }
723 }
724
725 /* Prior to kernel version 2.6.12, the CPU fan speed was available
726  * in ADT746X_FAN_OLD, whereas later kernel versions provide this
727  * information in ADT746X_FAN.
728  */
729 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
730 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
731
732 void get_adt746x_fan( char * p_client_buffer, size_t client_buffer_size )
733 {
734         static int rep;
735         char adt746x_fan_state[64];
736         FILE *fp;
737
738         if ( !p_client_buffer || client_buffer_size <= 0 )
739                 return;
740
741         if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
742                  && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL)
743
744         {
745                 sprintf(adt746x_fan_state, "adt746x not found");
746         }
747         else
748         {
749                 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
750                 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
751                 fclose(fp);
752         }
753
754         snprintf( p_client_buffer, client_buffer_size, "%s", adt746x_fan_state );
755         return;
756 }
757
758 /* Prior to kernel version 2.6.12, the CPU temperature was found
759  * in ADT746X_CPU_OLD, whereas later kernel versions provide this
760  * information in ADT746X_CPU.
761  */
762 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
763 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
764
765 void get_adt746x_cpu( char * p_client_buffer, size_t client_buffer_size )
766 {
767         static int rep;
768         char adt746x_cpu_state[64];
769         FILE *fp;
770
771         if ( !p_client_buffer || client_buffer_size <= 0 )
772                 return;
773         
774         if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
775                  && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL)
776         {
777                 sprintf(adt746x_cpu_state, "adt746x not found");
778         }
779         else
780         {
781                 fscanf(fp, "%2s", adt746x_cpu_state);
782                 fclose(fp);
783         }
784
785         snprintf( p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state ); 
786         return;
787 }
788
789 /* Thanks to "Walt Nelson" <wnelsonjr@comcast.net> */
790
791 /***********************************************************************/
792 /*
793  *  This file is part of x86info.
794  *  (C) 2001 Dave Jones.
795  *
796  *  Licensed under the terms of the GNU GPL License version 2.
797  *
798  * Estimate CPU MHz routine by Andrea Arcangeli <andrea@suse.de>
799  * Small changes by David Sterba <sterd9am@ss1000.ms.mff.cuni.cz>
800  *
801  */
802 #if  defined(__i386) || defined(__x86_64)
803 __inline__ unsigned long long int rdtsc()
804 {
805         unsigned long long int x;
806         __asm__ volatile (".byte 0x0f, 0x31":"=A" (x));
807         return x;
808 }
809 #endif
810
811 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
812 void get_freq_dynamic( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor )
813 {
814 #if  defined(__i386) || defined(__x86_64)
815         struct timezone tz;
816         struct timeval tvstart, tvstop;
817         unsigned long long cycles[2];   /* gotta be 64 bit */
818         unsigned int microseconds;      /* total time taken */
819
820         if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 )
821              return;
822
823         memset(&tz, 0, sizeof(tz));
824
825         /* get this function in cached memory */
826         gettimeofday(&tvstart, &tz);
827         cycles[0] = rdtsc();
828         gettimeofday(&tvstart, &tz);
829
830         /* we don't trust that this is any specific length of time */
831         usleep(100);
832         cycles[1] = rdtsc();
833         gettimeofday(&tvstop, &tz);
834         microseconds = ((tvstop.tv_sec - tvstart.tv_sec) * 1000000) +
835             (tvstop.tv_usec - tvstart.tv_usec);
836
837         snprintf( p_client_buffer, client_buffer_size, p_format, (float)((cycles[1] - cycles[0]) / microseconds) / divisor );
838         return;
839 #else
840 /* FIXME: hardwired: get freq for first cpu!
841    this whole function needs to be rethought and redone for
842    multi-cpu/multi-core/multi-threaded environments and 
843    arbitrary combinations thereof 
844 */
845         get_freq( p_client_buffer, client_buffer_size, p_format, divisor, 1 );
846         return;
847 #endif
848 }
849
850
851 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
852 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
853
854 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
855 char get_freq( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor, unsigned int cpu )
856 {
857         FILE *f;
858         char frequency[32];
859         char s[256];
860         double freq = 0;
861         char current_freq_file[128];
862         
863         cpu--;
864         snprintf(current_freq_file, 127, "%s/cpu%d/%s",
865                  CPUFREQ_PREFIX, cpu, CPUFREQ_POSTFIX);
866
867         if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 )
868                 return 0;
869         
870         f = fopen(current_freq_file, "r");
871         if (f) {
872                 /* if there's a cpufreq /sys node, read the current frequency from this node;
873                  * divide by 1000 to get Mhz. */
874                 if (fgets(s, sizeof(s), f)) {
875                         s[strlen(s)-1] = '\0';
876                         freq = strtod(s, NULL);
877                 }
878                 fclose(f);
879                 snprintf( p_client_buffer, client_buffer_size, p_format, (freq/1000)/divisor );
880                 return 1;
881         }
882         
883         cpu++;
884         f = fopen("/proc/cpuinfo", "r");                //open the CPU information file
885         if (!f) {
886                 perror("Conky: Failed to access '/proc/cpuinfo' at get_freq()");
887                 return 0;
888         }
889
890         while (fgets(s, sizeof(s), f) != NULL){         //read the file
891
892 #if defined(__i386) || defined(__x86_64)
893                 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {        //and search for the cpu mhz
894 #else
895 #if defined(__alpha)
896                 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {          // different on alpha
897 #else
898                 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {  // this is different on ppc for some reason
899 #endif // defined(__alpha)
900 #endif // defined(__i386) || defined(__x86_64)
901
902                 strcpy(frequency, strchr(s, ':') + 2);  //copy just the number
903 #if defined(__alpha)
904                 frequency[strlen(frequency) - 6] = '\0';// strip " est.\n"
905                 freq = strtod(frequency, NULL)/1000000; // kernel reports in Hz 
906 #else
907                 frequency[strlen(frequency) - 1] = '\0'; // strip \n
908                 freq = strtod(frequency, NULL);
909 #endif
910                 break;
911                 }
912                 if (strncmp(s, "processor", 9) == 0) {
913                     cpu--; 
914                     continue;
915                 }
916                 
917         }
918         
919         fclose(f);
920         snprintf( p_client_buffer, client_buffer_size, p_format, (float)freq/divisor );
921         return 1;
922 }
923
924 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
925
926 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
927 char get_voltage( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor, unsigned int cpu )
928 {
929 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks 
930    something like this:
931 # frequency voltage
932 1800000 1340
933 1600000 1292
934 1400000 1100
935 1200000 988
936 1000000 1116
937 800000 1004
938 600000 988
939 */
940
941 /* Peter Tarjan (ptarjan@citromail.hu) */
942         FILE *f;
943         char s[256];
944         int freq = 0;
945         int voltage = 0;
946         char current_freq_file[128];
947         int freq_comp = 0;
948         
949
950 /* build the voltage file name */
951         cpu--;
952         snprintf(current_freq_file, 127, "%s/cpu%d/%s",
953                  CPUFREQ_PREFIX, cpu, CPUFREQ_POSTFIX);
954
955         if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 )
956                 return 0;
957
958         /* read the current cpu frequency from the /sys node */
959         f = fopen(current_freq_file, "r");
960         if (f) {
961             if (fgets(s, sizeof(s), f)) {
962                 s[strlen(s)-1] = '\0';
963                 freq = strtod(s, NULL);
964             }
965             fclose(f);
966         } else {
967                 fprintf(stderr, "Conky: Failed to access '%s' at ", current_freq_file);
968                 perror("get_voltage()");
969                 if (f) {
970                         fclose(f);
971                 }
972                 return 0;
973             }
974
975         snprintf(current_freq_file, 127, "%s/cpu%d/%s",
976                  CPUFREQ_PREFIX, cpu, CPUFREQ_VOLTAGE);
977
978 /* use the current cpu frequency to find the corresponding voltage */
979         f = fopen(current_freq_file, "r");
980
981         if (f) {
982                 while (!feof(f)) {
983                         char line[256];
984                         if (fgets(line, 255, f) == NULL) break;
985                         sscanf(line, "%d %d", &freq_comp, &voltage);
986                         if(freq_comp == freq) break;
987                 }
988                 fclose(f);
989         } else {
990                 fprintf(stderr, "Conky: Failed to access '%s' at ", current_freq_file);
991                 perror("get_voltage()");
992                 if (f) {
993                         fclose(f);
994                 }
995                 return 0;
996         }
997         snprintf( p_client_buffer, client_buffer_size, p_format, (float)voltage/divisor );
998         return 1;
999
1000 }
1001
1002 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1003
1004 void get_acpi_fan( char * p_client_buffer, size_t client_buffer_size )
1005 {
1006         static int rep;
1007         char buf[256];
1008         char buf2[256];
1009         FILE *fp;
1010
1011         if ( !p_client_buffer || client_buffer_size <= 0 )
1012                 return;
1013
1014         /* yeah, slow... :/ */
1015         if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep))
1016         {
1017                 snprintf( p_client_buffer, client_buffer_size, "no fans?" );
1018                 return;
1019         }
1020
1021         snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf );
1022
1023         fp = open_file(buf2, &rep);
1024         if (!fp) {
1025                 snprintf( p_client_buffer, client_buffer_size, "can't open fan's state file" );
1026                 return;
1027         }
1028         memset(buf,0,sizeof(buf));
1029         fscanf(fp, "%*s %99s", buf);
1030         fclose(fp);
1031
1032         snprintf( p_client_buffer, client_buffer_size, "%s", buf );
1033
1034         return;
1035 }
1036
1037 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1038
1039 void get_acpi_ac_adapter( char * p_client_buffer, size_t client_buffer_size )
1040 {
1041         static int rep;
1042         char buf[256];
1043         char buf2[256];
1044         FILE *fp;
1045
1046         if ( !p_client_buffer || client_buffer_size <= 0 )
1047                 return;
1048
1049         /* yeah, slow... :/ */
1050         if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep))
1051         {
1052                 snprintf( p_client_buffer, client_buffer_size, "no ac_adapters?" );
1053                 return; 
1054         }
1055
1056         snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf );
1057          
1058
1059         fp = open_file(buf2, &rep);
1060         if (!fp) {
1061                 snprintf( p_client_buffer, client_buffer_size, "No ac adapter found.... where is it?" );
1062                 return;
1063         }
1064         memset(buf,0,sizeof(buf));
1065         fscanf(fp, "%*s %99s", buf );
1066         fclose(fp);
1067
1068         snprintf( p_client_buffer, client_buffer_size, "%s", buf );
1069
1070         return;
1071 }
1072
1073 /*
1074 /proc/acpi/thermal_zone/THRM/cooling_mode
1075 cooling mode:            active
1076 /proc/acpi/thermal_zone/THRM/polling_frequency
1077 <polling disabled>
1078 /proc/acpi/thermal_zone/THRM/state
1079 state:                   ok
1080 /proc/acpi/thermal_zone/THRM/temperature
1081 temperature:             45 C
1082 /proc/acpi/thermal_zone/THRM/trip_points
1083 critical (S5):           73 C
1084 passive:                 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1085 */
1086
1087 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1088 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1089
1090 int open_acpi_temperature(const char *name)
1091 {
1092         char path[256];
1093         char buf[256];
1094         int fd;
1095
1096         if (name == NULL || strcmp(name, "*") == 0) {
1097                 static int rep;
1098                 if (!get_first_file_in_a_directory
1099                     (ACPI_THERMAL_DIR, buf, &rep))
1100                         return -1;
1101                 name = buf;
1102         }
1103
1104         snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1105
1106         fd = open(path, O_RDONLY);
1107         if (fd < 0)
1108                 ERR("can't open '%s': %s", path, strerror(errno));
1109
1110         return fd;
1111 }
1112
1113 static double last_acpi_temp;
1114 static double last_acpi_temp_time;
1115
1116 double get_acpi_temperature(int fd)
1117 {
1118         if (fd <= 0)
1119                 return 0;
1120
1121         /* don't update acpi temperature too often */
1122         if (current_update_time - last_acpi_temp_time < 11.32) {
1123                 return last_acpi_temp;
1124         }
1125         last_acpi_temp_time = current_update_time;
1126
1127         /* seek to beginning */
1128         lseek(fd, 0, SEEK_SET);
1129
1130         /* read */
1131         {
1132                 char buf[256];
1133                 int n;
1134                 n = read(fd, buf, 255);
1135                 if (n < 0)
1136                         ERR("can't read fd %d: %s", fd, strerror(errno));
1137                 else {
1138                         buf[n] = '\0';
1139                         sscanf(buf, "temperature: %lf", &last_acpi_temp);
1140                 }
1141         }
1142
1143         return last_acpi_temp;
1144 }
1145
1146 /*
1147 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info 
1148 present:                 yes
1149 design capacity:         4400 mAh
1150 last full capacity:      4064 mAh
1151 battery technology:      rechargeable
1152 design voltage:          14800 mV
1153 design capacity warning: 300 mAh
1154 design capacity low:     200 mAh
1155 capacity granularity 1:  32 mAh
1156 capacity granularity 2:  32 mAh
1157 model number:            02KT
1158 serial number:           16922
1159 battery type:            LION
1160 OEM info:                SANYO
1161 */
1162
1163 /*
1164 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1165 present:                 yes
1166 capacity state:          ok
1167 charging state:          unknown
1168 present rate:            0 mA
1169 remaining capacity:      4064 mAh
1170 present voltage:         16608 mV
1171 */
1172
1173 /*
1174 2213<@jupet kellari ö> jupet@lagi-unstable:~$ cat /proc/apm 
1175 2213<@jupet kellari ö> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1176 2213<@jupet kellari ö> (-1 ollee ei akkua kiinni, koska akku on pöydällä)
1177 2214<@jupet kellari ö> jupet@lagi-unstable:~$ cat /proc/apm 
1178 2214<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1179
1180 2238<@jupet kellari ö> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1181 2239<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1182
1183 2240<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori päällä
1184 2241<@jupet kellari ö> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori päällä mutta ilman verkkovirtaa
1185 */
1186
1187 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1188 #define APM_PATH "/proc/apm"
1189
1190 static FILE *acpi_bat_fp;
1191 static FILE *apm_bat_fp;
1192
1193 static int acpi_last_full;
1194
1195 static char last_battery_str[64];
1196
1197 static double last_battery_time;
1198
1199 void get_battery_stuff(char *buf, unsigned int n, const char *bat)
1200 {
1201         static int rep, rep2;
1202         char acpi_path[128];
1203         snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1204
1205         /* don't update battery too often */
1206         if (current_update_time - last_battery_time < 29.5) {
1207                 snprintf(buf, n, "%s", last_battery_str);
1208                 return;
1209         }
1210         last_battery_time = current_update_time;
1211
1212         /* first try ACPI */
1213
1214         if (acpi_bat_fp == NULL && apm_bat_fp == NULL)
1215                 acpi_bat_fp = open_file(acpi_path, &rep);
1216
1217         if (acpi_bat_fp != NULL) {
1218                 int present_rate = -1;
1219                 int remaining_capacity = -1;
1220                 char charging_state[64];
1221                 char present[4];
1222
1223                 /* read last full capacity if it's zero */
1224                 if (acpi_last_full == 0) {
1225                         static int rep;
1226                         char path[128];
1227                         FILE *fp;
1228                         snprintf(path, 127,
1229                                  ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1230                         fp = open_file(path, &rep);
1231                         if (fp != NULL) {
1232                                 while (!feof(fp)) {
1233                                         char b[256];
1234                                         if (fgets(b, 256, fp) == NULL)
1235                                                 break;
1236                                         if (sscanf(b, "last full capacity: %d", &acpi_last_full) != 0) {
1237                                                 break;
1238                                         }
1239                                 }
1240
1241                                 fclose(fp);
1242                         }
1243                 }
1244
1245                 fseek(acpi_bat_fp, 0, SEEK_SET);
1246
1247                 strcpy(charging_state, "unknown");
1248
1249                 while (!feof(acpi_bat_fp)) {
1250                         char buf[256];
1251                         if (fgets(buf, 256, acpi_bat_fp) == NULL)
1252                                 break;
1253
1254                         /* let's just hope units are ok */
1255                         if (strncmp (buf, "present:", 8) == 0) 
1256                                 sscanf(buf, "present: %4s", present);
1257                         else if (strncmp (buf, "charging state:", 15) == 0)
1258                                 sscanf(buf, "charging state: %63s", charging_state);
1259                         else if (strncmp (buf, "present rate:", 13) == 0)
1260                                 sscanf(buf, "present rate: %d", &present_rate);
1261                         else if (strncmp(buf, "remaining capacity:", 19) == 0)
1262                                 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1263                 }
1264                 
1265                 /* not present */
1266                 if (strcmp(present, "no") == 0) {
1267                         strncpy(last_battery_str, "not present", 64);
1268                 }
1269                 /* charging */
1270                 else if (strcmp(charging_state, "charging") == 0) {
1271                         if (acpi_last_full != 0 && present_rate > 0) {
1272                                 snprintf(last_battery_str, 63, "charging %i%% ", (int) (remaining_capacity / acpi_last_full) * 100);
1273                                 format_seconds(last_battery_str + 14,
1274                                                63 - 14,
1275                                                (acpi_last_full -
1276                                                 remaining_capacity) * 60 *
1277                                                60 / present_rate);
1278                         } else if (acpi_last_full != 0
1279                                    && present_rate <= 0) {
1280                                 snprintf(last_battery_str, 64, "charging %d%%",
1281                                         remaining_capacity * 100 /
1282                                         acpi_last_full);
1283                         } else {
1284                                 strncpy(last_battery_str, "charging", 63);
1285                         }
1286                 }
1287                 /* discharging */
1288                 else if (strncmp(charging_state, "discharging", 64) == 0) {
1289                         if (present_rate > 0) {
1290                                 snprintf(last_battery_str, 63, "discharging %i%% ", (int)(remaining_capacity / acpi_last_full) * 100);
1291                                 format_seconds(last_battery_str + 17, 63 - 17,
1292                                                (remaining_capacity * 60 *
1293                                                 60) / present_rate);
1294                         } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1295                                 snprintf(last_battery_str, 64, "full");
1296                         } else {
1297                                 snprintf(last_battery_str, 64,
1298                                         "discharging %d%%",
1299                                         remaining_capacity * 100 /
1300                                         acpi_last_full);
1301                         }
1302                 }
1303                 /* charged */
1304                 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1305                 else if (strncmp(charging_state, "charged", 64) == 0) {
1306                                 strcpy(last_battery_str, "charged");
1307                 } 
1308                 /* unknown, probably full / AC */
1309                 else {
1310                         if (acpi_last_full != 0
1311                             && remaining_capacity != acpi_last_full)
1312                                 snprintf(last_battery_str, 64, "unknown %d%%",
1313                                         remaining_capacity * 100 /
1314                                         acpi_last_full);
1315                         else
1316                                 strncpy(last_battery_str, "AC", 64);
1317                 }
1318         } else {
1319                 /* APM */
1320                 if (apm_bat_fp == NULL)
1321                         apm_bat_fp = open_file(APM_PATH, &rep2);
1322
1323                 if (apm_bat_fp != NULL) {
1324                         int ac, status, flag, life;
1325
1326                         fscanf(apm_bat_fp,
1327                                "%*s %*s %*x %x   %x       %x     %d%%",
1328                                &ac, &status, &flag, &life);
1329
1330                         if (life == -1) {
1331                                 /* could check now that there is ac */
1332                                 snprintf(last_battery_str, 64, "AC");
1333                         } else if (ac && life != 100) { /* could check that status==3 here? */
1334                                 snprintf(last_battery_str, 64,
1335                                          "charging %d%%", life);
1336                         } else {
1337                                 snprintf(last_battery_str, 64, "%d%%",
1338                                          life);
1339                         }
1340
1341                         /* it seemed to buffer it so file must be closed (or could use syscalls
1342                          * directly but I don't feel like coding it now) */
1343                         fclose(apm_bat_fp);
1344                         apm_bat_fp = NULL;
1345                 }
1346         }
1347
1348         snprintf(buf, n, "%s", last_battery_str);
1349 }
1350
1351 /* On Apple powerbook and ibook:
1352 $ cat /proc/pmu/battery_0
1353 flags      : 00000013
1354 charge     : 3623
1355 max_charge : 3720
1356 current    : 388
1357 voltage    : 16787
1358 time rem.  : 900
1359 $ cat /proc/pmu/info
1360 PMU driver version     : 2
1361 PMU firmware version   : 0c
1362 AC Power               : 1
1363 Battery count          : 1
1364 */
1365
1366 /* defines as in <linux/pmu.h> */
1367 #define PMU_BATT_PRESENT        0x00000001
1368 #define PMU_BATT_CHARGING       0x00000002
1369
1370 static FILE* pmu_battery_fp;
1371 static FILE* pmu_info_fp;
1372 static char pb_battery_info[3][32];
1373 static double pb_battery_info_update;
1374  
1375 #define PMU_PATH "/proc/pmu"
1376 void get_powerbook_batt_info(char *buf, size_t n, int i)
1377 {
1378         static int rep;
1379         const char* batt_path = PMU_PATH "/battery_0";
1380         const char* info_path = PMU_PATH "/info";
1381         int flags, charge, max_charge, ac = -1;
1382         long time = -1;
1383
1384         /* don't update battery too often */
1385         if (current_update_time - pb_battery_info_update < 29.5) {
1386                 snprintf(buf, n, "%s", pb_battery_info[i]);
1387                 return;
1388         }
1389         pb_battery_info_update = current_update_time;
1390
1391         if (pmu_battery_fp == NULL)
1392                 pmu_battery_fp = open_file(batt_path, &rep);
1393
1394         if (pmu_battery_fp != NULL) {
1395                 rewind(pmu_battery_fp);
1396                 while (!feof(pmu_battery_fp)) {
1397                         char buf[32];
1398                         if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL)
1399                                 break;
1400
1401                         if (buf[0] == 'f')
1402                                 sscanf(buf, "flags      : %8x", &flags);
1403                         else if (buf[0] == 'c' && buf[1] == 'h')
1404                                 sscanf(buf, "charge     : %d", &charge);
1405                         else if (buf[0] == 'm')
1406                                 sscanf(buf, "max_charge : %d", &max_charge);
1407                         else if (buf[0] == 't')
1408                                 sscanf(buf, "time rem.  : %ld", &time);
1409                 }
1410         }
1411         if (pmu_info_fp == NULL)
1412                 pmu_info_fp = open_file(info_path, &rep);
1413
1414         if (pmu_info_fp != NULL) {
1415                 rewind(pmu_info_fp);
1416                 while (!feof(pmu_info_fp)) {
1417                         char buf[32];
1418                         if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL)
1419                                 break;
1420                         if (buf[0] == 'A')
1421                                 sscanf(buf, "AC Power               : %d", &ac);
1422                 }
1423         }
1424         /* update status string */
1425         if ((ac && !(flags & PMU_BATT_PRESENT)))
1426                 strcpy(pb_battery_info[PB_BATT_STATUS], "AC");
1427         else if (ac && (flags & PMU_BATT_PRESENT)
1428                   && !(flags & PMU_BATT_CHARGING))
1429                 strcpy(pb_battery_info[PB_BATT_STATUS], "charged");
1430         else if ((flags & PMU_BATT_PRESENT)
1431                 && (flags & PMU_BATT_CHARGING))
1432                 strcpy(pb_battery_info[PB_BATT_STATUS], "charging");
1433         else
1434                 strcpy(pb_battery_info[PB_BATT_STATUS], "discharging");
1435
1436         /* update percentage string */
1437         if (time == 0)
1438                 pb_battery_info[PB_BATT_PERCENT][0] = 0; 
1439         else
1440                 snprintf(pb_battery_info[PB_BATT_PERCENT],
1441                         sizeof(pb_battery_info[PB_BATT_PERCENT]),
1442                         "%d%%", (charge * 100)/max_charge);
1443
1444         /* update time string */
1445         if (time == 0) /* fully charged or battery not present */
1446                 pb_battery_info[PB_BATT_TIME][0] = 0; 
1447         else if (time < 60*60) /* don't show secs */
1448                 format_seconds_short(pb_battery_info[PB_BATT_TIME],
1449                         sizeof(pb_battery_info[PB_BATT_TIME]), time);
1450         else
1451                 format_seconds(pb_battery_info[PB_BATT_TIME],
1452                         sizeof(pb_battery_info[PB_BATT_TIME]), time);
1453
1454         snprintf(buf, n, "%s", pb_battery_info[i]);
1455 }
1456
1457 void update_top()
1458 {
1459         show_nice_processes = 1;
1460         process_find_top(info.cpu, info.memu);
1461         info.first_process = get_first_process();
1462 }
1463
1464
1465 /*
1466  *  The following ifdefs were adapted from gkrellm
1467  */
1468 #include <linux/major.h>
1469
1470 #if ! defined (MD_MAJOR)
1471 #define MD_MAJOR 9
1472 #endif
1473
1474 #if !defined(LVM_BLK_MAJOR)
1475 #define LVM_BLK_MAJOR 58
1476 #endif
1477
1478 #if !defined(NBD_MAJOR)
1479 #define NBD_MAJOR 43
1480 #endif
1481
1482 void update_diskio()
1483 {
1484         static unsigned int last = UINT_MAX;
1485         static FILE* fp;
1486
1487         char buf[512];
1488         int major, minor;
1489         unsigned int current = 0;
1490         unsigned int reads, writes = 0;
1491         int col_count = 0;
1492
1493         if (!fp) {
1494                 fp = fopen("/proc/diskstats", "r");
1495         } else {
1496                 fseek(fp, 0, SEEK_SET);
1497         }
1498
1499         /* read reads and writes from all disks (minor = 0), including
1500          * cd-roms and floppies, and summ them up
1501          */
1502         current = 0;
1503         while (!feof(fp)) {
1504                 fgets(buf, 512, fp);
1505                 col_count = sscanf(buf, "%u %u %*s %*u %*u %u %*u %*u %*u %u",
1506                                    &major, &minor, &reads, &writes);
1507                 /* ignore subdevices (they have only 3 matching entries in their line)
1508                  * and virtual devices (LVM, network block devices, RAM disks, Loopback)
1509                  *
1510                  * XXX ignore devices which are part of a SW RAID (MD_MAJOR)
1511                  */
1512                 if (col_count > 3 &&
1513                     major != LVM_BLK_MAJOR && major != NBD_MAJOR &&
1514                     major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
1515                         current += reads + writes;
1516                 }
1517         }
1518
1519         /* since the values in /proc/diststats are absolute, we have
1520          * to substract our last reading. The numbers stand for
1521          * "sectors read", and we therefore have to divide by two to
1522          * get KB */
1523         int tot = ((double)(current-last)/2);
1524         if (last > current) {
1525                 /* we hit this either if it's the very first time we
1526                  * run this, or when /proc/diskstats overflows; while
1527                  * 0 is not correct, it's at least not way off */
1528                 tot = 0;
1529         }
1530         last = current;
1531
1532         diskio_value = tot;
1533 }
1534
1535 /* Here come the IBM ACPI-specific things. For reference, see
1536  http://ibm-acpi.sourceforge.net/README
1537 If IBM ACPI is installed, /proc/acpi/ibm contains the following files:
1538 bay
1539 beep
1540 bluetooth
1541 brightness
1542 cmos
1543 dock
1544 driver
1545 ecdump
1546 fan
1547 hotkey
1548 led
1549 light
1550 thermal
1551 video
1552 volume
1553 The content of these files is described in detail in the aforementioned
1554 README - some of them also in the following functions accessing them.
1555 Peter Tarjan (ptarjan@citromail.hu)
1556 */
1557
1558 #define IBM_ACPI_DIR "/proc/acpi/ibm"
1559
1560 void get_ibm_acpi_fan( char * p_client_buffer, size_t client_buffer_size )
1561 {
1562 /* get fan speed on IBM/Lenovo laptops running the ibm acpi.
1563    /proc/acpi/ibm/fan looks like this (3 lines):
1564 status:         disabled
1565 speed:          2944
1566 commands:       enable, disable
1567 Peter Tarjan (ptarjan@citromail.hu)
1568 */
1569
1570     if ( !p_client_buffer || client_buffer_size <= 0 )
1571         return;
1572     
1573     FILE *fp;
1574     unsigned int speed=0;
1575     char fan[128];
1576     snprintf(fan, 127, "%s/fan",IBM_ACPI_DIR);    
1577
1578     fp = fopen(fan, "r");
1579     if (fp != NULL)
1580     {
1581         while (!feof(fp))
1582         {
1583             char line[256];
1584             if (fgets(line, 255, fp) == NULL) break;
1585             if (sscanf(line, "speed: %d", &speed)) break;       
1586         }
1587     }
1588     else 
1589     {
1590         CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove ibm* from your Conky config file.", fan, strerror(errno));
1591     }
1592
1593     fclose(fp);
1594     snprintf( p_client_buffer, client_buffer_size, "%d", speed );
1595     return;
1596     
1597 }    
1598
1599 static double last_ibm_acpi_temp_time;
1600 void get_ibm_acpi_temps()
1601 {
1602 /* get the measured temperatures from the temperature sensors 
1603    on IBM/Lenovo laptops running the ibm acpi.
1604    There are 8 values in /proc/acpi/ibm/thermal, and according to 
1605    http://ibm-acpi.sourceforge.net/README
1606    these mean the following (at least on an IBM R51...)
1607 0:  CPU (also on the T series laptops)
1608 1:  Mini PCI Module (?)
1609 2:  HDD (?)
1610 3:  GPU (also on the T series laptops)
1611 4:  Battery (?)
1612 5:  N/A
1613 6:  Battery (?)
1614 7:  N/A 
1615    I'm not too sure about those with the question mark, but the values I'm 
1616    reading from *my* thermal file (on a T42p) look realistic for the 
1617    hdd and the battery. 
1618    #5 and #7 are always -128. 
1619    /proc/acpi/ibm/thermal looks like this (1 line):
1620 temperatures:   41 43 31 46 33 -128 29 -128
1621 Peter Tarjan (ptarjan@citromail.hu)
1622 */
1623
1624 /*    don't update too often */
1625     if (current_update_time - last_ibm_acpi_temp_time < 10.00) 
1626     {
1627         return;
1628     }
1629     last_ibm_acpi_temp_time = current_update_time; 
1630     
1631 /*    if ( !p_client_buffer || client_buffer_size <= 0 )
1632       return; */
1633
1634     FILE *fp;
1635
1636     char thermal[128];
1637     snprintf(thermal, 127, "%s/thermal",IBM_ACPI_DIR);    
1638     fp = fopen(thermal, "r");
1639
1640     if (fp != NULL)
1641     {
1642         while (!feof(fp))
1643         {
1644             char line[256];
1645             if (fgets(line, 255, fp) == NULL) break;
1646             if (sscanf(line, "temperatures: %d %d %d %d %d %d %d %d",
1647                        &ibm_acpi.temps[0], &ibm_acpi.temps[1],
1648                        &ibm_acpi.temps[2], &ibm_acpi.temps[3],
1649                        &ibm_acpi.temps[4], &ibm_acpi.temps[5], 
1650                        &ibm_acpi.temps[6], &ibm_acpi.temps[7])) break;
1651         }
1652     }
1653     else 
1654     {
1655         CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove ibm* from your Conky config file.", thermal, strerror(errno));
1656     }
1657
1658     fclose(fp);
1659
1660 }              
1661
1662
1663 void get_ibm_acpi_volume( char * p_client_buffer, size_t client_buffer_size )
1664 {
1665
1666 /* get volume (0-14) on IBM/Lenovo laptops running the ibm acpi.
1667    "Volume" here is none of the mixer volumes, but a "master of masters"
1668    volume adjusted by the IBM volume keys.
1669    /proc/acpi/ibm/fan looks like this (4 lines):
1670 level:          4
1671 mute:           off
1672 commands:       up, down, mute
1673 commands:       level <level> (<level> is 0-15)
1674 Peter Tarjan (ptarjan@citromail.hu)
1675 */
1676     
1677     if ( !p_client_buffer || client_buffer_size <= 0 )
1678         return;
1679     
1680     FILE *fp;
1681
1682     char volume[128];
1683     snprintf(volume, 127, "%s/volume",IBM_ACPI_DIR);    
1684     unsigned int vol=-1;
1685     char mute[3]="";
1686
1687     fp = fopen(volume, "r");
1688     if (fp != NULL)
1689     {
1690         while (!feof(fp))
1691         {
1692             char line[256];
1693             if (fgets(line, 255, fp) == NULL) break;
1694             if (sscanf(line, "level: %d", &vol)) continue;
1695             if (sscanf(line, "mute: %s", mute)) break;
1696         }
1697     }
1698     else 
1699     {
1700         CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove ibm* from your Conky config file.", volume, strerror(errno));
1701     }
1702
1703     fclose(fp);
1704
1705     if (strcmp(mute, "on")==0)
1706     {
1707         snprintf( p_client_buffer, client_buffer_size, "%s", "mute" );
1708         return;
1709     }
1710     else
1711     {
1712         snprintf( p_client_buffer, client_buffer_size, "%d", vol );
1713         return;
1714     }
1715
1716 }
1717
1718 /*static FILE *fp=NULL;*/
1719
1720 void get_ibm_acpi_brightness(char * p_client_buffer, size_t client_buffer_size)
1721 {
1722 /* get LCD brightness on IBM/Lenovo laptops running the ibm acpi.
1723    /proc/acpi/ibm/brightness looks like this (3 lines):
1724 level:          7
1725 commands:       up, down
1726 commands:       level <level> (<level> is 0-7)
1727 Peter Tarjan (ptarjan@citromail.hu)
1728 */
1729
1730     if ( !p_client_buffer || client_buffer_size <= 0 )
1731         return;
1732
1733     FILE *fp;
1734     unsigned int brightness=0;
1735     char filename[128];
1736     snprintf(filename, 127, "%s/brightness",IBM_ACPI_DIR);    
1737
1738     fp = fopen(filename, "r");
1739     if (fp != NULL)
1740     {
1741         while (!feof(fp))
1742         {
1743             char line[256];
1744             if (fgets(line, 255, fp) == NULL) break;
1745             if (sscanf(line, "level: %d", &brightness)) break;  
1746         }
1747     }
1748     else 
1749     {
1750         CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove ibm* from your Conky config file.", filename, strerror(errno));
1751     }
1752
1753     fclose(fp);
1754
1755     snprintf( p_client_buffer, client_buffer_size, "%d", brightness );
1756     return;
1757     
1758 }