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