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