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