infopipe improvements
[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         char buf[256];
368         if (stat_fp == NULL)
369                 stat_fp = open_file("/proc/stat", &rep);
370         else
371                 fseek(stat_fp, 0, SEEK_SET);
372         if (stat_fp == NULL)
373                 return;
374
375         info.cpu_count = 0;
376
377         while (!feof(stat_fp)) {
378                 if (fgets(buf, 255, stat_fp) == NULL)
379                         break;
380
381                 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
382                         if (info.cpu_count == 0) {
383                                 determine_longstat(buf);
384                         }
385                         info.cpu_count++;
386                 }
387         }
388         info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
389
390 }
391
392 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
393 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
394
395 inline static void update_stat()
396 {
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         if (!cpu_setup) {
407                 get_cpu_count();
408                 cpu_setup = 1;
409         }
410
411         if (stat_template == NULL) {
412                 stat_template = KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT ;
413         }       
414
415         if (cpu == NULL) {
416                 malloc_cpu_size = (info.cpu_count + 1) *  sizeof(struct cpu_info);
417                 cpu = malloc(malloc_cpu_size);
418                 memset(cpu, 0, malloc_cpu_size);
419         }
420
421         if (stat_fp == NULL) {
422                 stat_fp = open_file("/proc/stat", &rep);
423         } else {
424                 fseek(stat_fp, 0, SEEK_SET);
425         }
426         if (stat_fp == NULL) {
427                 return;
428         }
429         index = 0;
430         while (!feof(stat_fp)) {
431                 if (fgets(buf, 255, stat_fp) == NULL)
432                         break;
433
434                 if (strncmp(buf, "procs_running ", 14) == 0) {
435                         sscanf(buf, "%*s %hu", &info.run_procs);
436                         info.mask |= (1 << INFO_RUN_PROCS);
437                 } else if (strncmp(buf, "cpu", 3) == 0) {
438                         index = isdigit(buf[3]) ? ((int)buf[3]) - 0x2F : 0;
439                         sscanf(buf, stat_template 
440                                 , &(cpu[index].cpu_user)
441                                 , &(cpu[index].cpu_nice)
442                                 , &(cpu[index].cpu_system)
443                                 , &(cpu[index].cpu_idle)
444                                 , &(cpu[index].cpu_iowait)
445                                 , &(cpu[index].cpu_irq)
446                                 , &(cpu[index].cpu_softirq)
447                                 , &(cpu[index].cpu_steal)
448                                 );
449
450                         cpu[index].cpu_total = cpu[index].cpu_user 
451                                          + cpu[index].cpu_nice 
452                                          + cpu[index].cpu_system 
453                                          + cpu[index].cpu_idle 
454                                          + cpu[index].cpu_iowait 
455                                          + cpu[index].cpu_irq
456                                          + cpu[index].cpu_softirq
457                                          + cpu[index].cpu_steal 
458                                          ; 
459
460                         cpu[index].cpu_active_total = cpu[index].cpu_total - (cpu[index].cpu_idle + cpu[index].cpu_iowait);
461                         info.mask |= (1 << INFO_CPU);
462
463                         double delta = current_update_time - last_update_time;
464                         if (delta <= 0.001) return;     
465
466                         cpu[index].cpu_val[0] = (cpu[index].cpu_active_total -  cpu[index].cpu_last_active_total) / 
467                                                 (float )(cpu[index].cpu_total - cpu[index].cpu_last_total); 
468                         curtmp = 0;
469                         for (i=0; i < info.cpu_avg_samples; i++ ) {
470                                 curtmp += cpu[index].cpu_val[i];
471                         }
472                         /* TESTING -- I've removed this, because I don't think it is right. You shouldn't divide 
473                                       by the cpu count here ... removing for testing */
474                         /* if (index == 0) {
475                                 info.cpu_usage[index] = curtmp / info.cpu_avg_samples / info.cpu_count; 
476                         } else {
477                                 info.cpu_usage[index] = curtmp / info.cpu_avg_samples;
478                         }  */
479                         /* TESTING -- this line replaces the prev. "suspect" if/else */
480                         info.cpu_usage[index] = curtmp / info.cpu_avg_samples;
481
482                         cpu[index].cpu_last_total = cpu[index].cpu_total;
483                         cpu[index].cpu_last_active_total = cpu[index].cpu_active_total;
484                         for (i = info.cpu_avg_samples - 1; i > 0; i--) {
485                                 cpu[index].cpu_val[i] = cpu[index].cpu_val[i - 1];
486                         }
487                 }
488
489         }
490 }
491
492 void update_running_processes()
493 {
494         update_stat();
495 }
496
497 void update_cpu_usage()
498 {
499         update_stat();
500 }
501
502 void update_load_average()
503 {
504 #ifdef HAVE_GETLOADAVG
505         double v[3];
506         getloadavg(v, 3);
507         info.loadavg[0] = (float) v[0];
508         info.loadavg[1] = (float) v[1];
509         info.loadavg[2] = (float) v[2];
510 #else
511         static int rep;
512         FILE *fp;
513
514         fp = open_file("/proc/loadavg", &rep);
515         if (!fp) {
516                 v[0] = v[1] = v[2] = 0.0;
517                 return;
518         }
519
520         fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
521                &info.loadavg[2]);
522
523         fclose(fp);
524 #endif
525 }
526
527 #define PROC_I8K "/proc/i8k"
528 #define I8K_DELIM " "
529 static char *i8k_procbuf = NULL;
530 void update_i8k()
531 {
532         FILE *fp;
533         if (!i8k_procbuf) {
534                 i8k_procbuf = (char*)malloc(128*sizeof(char));
535         }
536         if ((fp = fopen(PROC_I8K,"r")) == NULL) {
537                 CRIT_ERR("/proc/i8k doesn't exist! use insmod to make sure the kernel driver is loaded...");
538         }
539
540         memset(&i8k_procbuf[0],0,128);
541         if (fread(&i8k_procbuf[0],sizeof(char),128,fp) == 0) {
542                 ERR("something wrong with /proc/i8k...");
543         }
544
545         fclose(fp);
546
547   i8k.version = strtok(&i8k_procbuf[0],I8K_DELIM);
548         i8k.bios = strtok(NULL,I8K_DELIM);
549         i8k.serial = strtok(NULL,I8K_DELIM);
550         i8k.cpu_temp = strtok(NULL,I8K_DELIM);
551         i8k.left_fan_status = strtok(NULL,I8K_DELIM);   
552         i8k.right_fan_status = strtok(NULL,I8K_DELIM);  
553         i8k.left_fan_rpm = strtok(NULL,I8K_DELIM);
554         i8k.right_fan_rpm = strtok(NULL,I8K_DELIM);
555         i8k.ac_status = strtok(NULL,I8K_DELIM);
556         i8k.buttons_status = strtok(NULL,I8K_DELIM);
557 }
558
559
560 /***********************************************************/
561 /***********************************************************/
562 /***********************************************************/
563
564 static int no_dots(const struct dirent *d)
565 {
566         if (d->d_name[0] == '.')
567                 return 0;
568         return 1;
569 }
570
571 static int
572 get_first_file_in_a_directory(const char *dir, char *s, int *rep)
573 {
574         struct dirent **namelist;
575         int i, n;
576
577         n = scandir(dir, &namelist, no_dots, alphasort);
578         if (n < 0) {
579                 if (!rep || !*rep) {
580                         ERR("scandir for %s: %s", dir, strerror(errno));
581                         if (rep)
582                                 *rep = 1;
583                 }
584                 return 0;
585         } else {
586                 if (n == 0)
587                         return 0;
588
589                 strncpy(s, namelist[0]->d_name, 255);
590                 s[255] = '\0';
591
592                 for (i = 0; i < n; i++)
593                         free(namelist[i]);
594                 free(namelist);
595
596                 return 1;
597         }
598 }
599
600 #define I2C_DIR "/sys/bus/i2c/devices/"
601
602 int
603 open_i2c_sensor(const char *dev, const char *type, int n, int *div,
604                 char *devtype)
605 {
606         char path[256];
607         char buf[256];
608         int fd;
609         int divfd;
610
611         /* if i2c device is NULL or *, get first */
612         if (dev == NULL || strcmp(dev, "*") == 0) {
613                 static int rep;
614                 if (!get_first_file_in_a_directory(I2C_DIR, buf, &rep))
615                         return -1;
616                 dev = buf;
617         }
618
619         /* change vol to in */
620         if (strcmp(type, "vol") == 0)
621                 type = "in";
622
623         if (strcmp(type, "tempf") == 0) {
624                 snprintf(path, 255, I2C_DIR "%s/%s%d_input", dev, "temp", n);
625         } else {
626                 snprintf(path, 255, I2C_DIR "%s/%s%d_input", dev, type, n);
627         }
628         strncpy(devtype, path, 255);
629
630         /* open file */
631         fd = open(path, O_RDONLY);
632         if (fd < 0) {
633                 CRIT_ERR("can't open '%s': %s\nplease fix i2c or remove it from Conky", path, strerror(errno));
634         }
635
636         if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
637             || strcmp(type, "tempf") == 0)
638                 *div = 1;
639         else
640                 *div = 0;
641         /* fan does not use *_div as a read divisor */
642         if (strcmp("fan", type) == 0)
643                 return fd;
644
645         /* test if *_div file exist, open it and use it as divisor */
646         if (strcmp(type, "tempf") == 0) {
647                 snprintf(path, 255, I2C_DIR "%s/%s%d_div", "one", "two",
648                          n);
649         } else {
650                 snprintf(path, 255, I2C_DIR "%s/%s%d_div", dev, type, n);
651         }
652
653         divfd = open(path, O_RDONLY);
654         if (divfd > 0) {
655                 /* read integer */
656                 char divbuf[64];
657                 unsigned int divn;
658                 divn = read(divfd, divbuf, 63);
659                 /* should read until n == 0 but I doubt that kernel will give these
660                  * in multiple pieces. :) */
661                 divbuf[divn] = '\0';
662                 *div = atoi(divbuf);
663         }
664
665         close(divfd);
666
667         return fd;
668 }
669
670 double get_i2c_info(int *fd, int div, char *devtype, char *type)
671 {
672         int val = 0;
673
674         if (*fd <= 0)
675                 return 0;
676
677         lseek(*fd, 0, SEEK_SET);
678
679         /* read integer */
680         {
681                 char buf[64];
682                 unsigned int n;
683                 n = read(*fd, buf, 63);
684                 /* should read until n == 0 but I doubt that kernel will give these
685                  * in multiple pieces. :) */
686                 buf[n] = '\0';
687                 val = atoi(buf);
688         }
689
690         close(*fd);
691         /* open file */
692         *fd = open(devtype, O_RDONLY);
693         if (*fd < 0)
694                 ERR("can't open '%s': %s", devtype, strerror(errno));
695
696         /* My dirty hack for computing CPU value 
697          * Filedil, from forums.gentoo.org
698          */
699 /*      if (strstr(devtype, "temp1_input") != NULL)
700         return -15.096+1.4893*(val / 1000.0); */
701
702
703         /* divide voltage and temperature by 1000 */
704         /* or if any other divisor is given, use that */
705         if (strcmp(type, "tempf") == 0) {
706                 if (div > 1)
707                         return ((val / div + 40) * 9.0 / 5) - 40;
708                 else if (div)
709                         return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
710                 else
711                         return ((val + 40) * 9.0 / 5) - 40;
712         } else {
713                 if (div > 1)
714                         return val / div;
715                 else if (div)
716                         return val / 1000.0;
717                 else
718                         return val;
719         }
720 }
721
722 #define ADT746X_FAN "/sys/devices/temperatures/cpu_fan_speed"
723
724 void get_adt746x_fan( char * p_client_buffer, size_t client_buffer_size )
725 {
726         static int rep;
727         char adt746x_fan_state[64];
728         FILE *fp;
729
730         if ( !p_client_buffer || client_buffer_size <= 0 )
731                 return;
732
733         fp = open_file(ADT746X_FAN, &rep);
734         if (!fp) 
735         {
736                 sprintf(adt746x_fan_state, "adt746x not found");
737         }
738         else
739         {
740                 fscanf(fp, "%s", adt746x_fan_state);
741                 fclose(fp);
742         }
743
744         snprintf( p_client_buffer, client_buffer_size, "%s", adt746x_fan_state );
745         return;
746 }
747
748 #define ADT746X_CPU "/sys/devices/temperatures/cpu_temperature"
749
750 void get_adt746x_cpu( char * p_client_buffer, size_t client_buffer_size )
751 {
752         static int rep;
753         char adt746x_cpu_state[64];
754         FILE *fp;
755
756         if ( !p_client_buffer || client_buffer_size <= 0 )
757                 return;
758         
759         fp = open_file(ADT746X_CPU, &rep);
760         if (!fp)
761         {
762                 sprintf(adt746x_cpu_state, "adt746x not found");
763         }
764         else
765         {
766                 fscanf(fp, "%2s", adt746x_cpu_state);
767                 fclose(fp);
768         }
769
770         snprintf( p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state ); 
771         return;
772 }
773
774 /* Thanks to "Walt Nelson" <wnelsonjr@comcast.net> */
775
776 /***********************************************************************/
777 /*
778  *  This file is part of x86info.
779  *  (C) 2001 Dave Jones.
780  *
781  *  Licensed under the terms of the GNU GPL License version 2.
782  *
783  * Estimate CPU MHz routine by Andrea Arcangeli <andrea@suse.de>
784  * Small changes by David Sterba <sterd9am@ss1000.ms.mff.cuni.cz>
785  *
786  */
787 #if  defined(__i386) || defined(__x86_64)
788 __inline__ unsigned long long int rdtsc()
789 {
790         unsigned long long int x;
791         __asm__ volatile (".byte 0x0f, 0x31":"=A" (x));
792         return x;
793 }
794 #endif
795
796 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
797 void get_freq_dynamic( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor )
798 {
799 #if  defined(__i386) || defined(__x86_64)
800         struct timezone tz;
801         struct timeval tvstart, tvstop;
802         unsigned long long cycles[2];   /* gotta be 64 bit */
803         unsigned int microseconds;      /* total time taken */
804
805         if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 )
806              return;
807
808         memset(&tz, 0, sizeof(tz));
809
810         /* get this function in cached memory */
811         gettimeofday(&tvstart, &tz);
812         cycles[0] = rdtsc();
813         gettimeofday(&tvstart, &tz);
814
815         /* we don't trust that this is any specific length of time */
816         usleep(100);
817         cycles[1] = rdtsc();
818         gettimeofday(&tvstop, &tz);
819         microseconds = ((tvstop.tv_sec - tvstart.tv_sec) * 1000000) +
820             (tvstop.tv_usec - tvstart.tv_usec);
821
822         snprintf( p_client_buffer, client_buffer_size, p_format, (float)((cycles[1] - cycles[0]) / microseconds) / divisor );
823         return;
824 #else
825         get_freq( p_client_buffer, client_buffer_size, p_format, divisor );
826         return;
827 #endif
828 }
829
830 #define CPUFREQ_CURRENT "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq"
831
832 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
833 void get_freq( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor )
834 {
835         FILE *f;
836         char frequency[32];
837         char s[256];
838         double freq = 0;
839
840         if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 )
841                 return;
842
843         f = fopen(CPUFREQ_CURRENT, "r");
844         if (f) {
845                 /* if there's a cpufreq /sys node, read the current frequency from this node;
846                  * divide by 1000 to get Mhz. */
847                 if (fgets(s, sizeof(s), f)) {
848                         s[strlen(s)-1] = '\0';
849                         freq = strtod(s, NULL);
850                 }
851                 fclose(f);
852                 snprintf( p_client_buffer, client_buffer_size, p_format, (freq/1000)/divisor );
853                 return;
854         }
855         
856         f = fopen("/proc/cpuinfo", "r");                //open the CPU information file
857         if (!f)
858             return;
859
860         while (fgets(s, sizeof(s), f) != NULL){         //read the file
861 #if defined(__i386) || defined(__x86_64)
862                 if (strncmp(s, "cpu MHz", 7) == 0) {    //and search for the cpu mhz
863 #else
864                 if (strncmp(s, "clock", 5) == 0) {      // this is different on ppc for some reason
865 #endif
866                 strcpy(frequency, strchr(s, ':') + 2);  //copy just the number
867                 frequency[strlen(frequency) - 1] = '\0'; // strip \n
868                 freq = strtod(frequency, NULL);
869                 break;
870                 }
871         }
872         
873         fclose(f);
874         snprintf( p_client_buffer, client_buffer_size, p_format, (float)freq/divisor );
875         return;
876 }
877
878
879 #define ACPI_FAN_DIR "/proc/acpi/fan/"
880
881 void get_acpi_fan( char * p_client_buffer, size_t client_buffer_size )
882 {
883         static int rep;
884         char buf[256];
885         char buf2[256];
886         FILE *fp;
887
888         if ( !p_client_buffer || client_buffer_size <= 0 )
889                 return;
890
891         /* yeah, slow... :/ */
892         if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep))
893         {
894                 snprintf( p_client_buffer, client_buffer_size, "no fans?" );
895                 return;
896         }
897
898         snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf );
899
900         fp = open_file(buf2, &rep);
901         if (!fp) {
902                 snprintf( p_client_buffer, client_buffer_size, "can't open fan's state file" );
903                 return;
904         }
905         memset(buf,0,sizeof(buf));
906         fscanf(fp, "%*s %99s", buf);
907         fclose(fp);
908
909         snprintf( p_client_buffer, client_buffer_size, "%s", buf );
910
911         return;
912 }
913
914 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
915
916 void get_acpi_ac_adapter( char * p_client_buffer, size_t client_buffer_size )
917 {
918         static int rep;
919         char buf[256];
920         char buf2[256];
921         FILE *fp;
922
923         if ( !p_client_buffer || client_buffer_size <= 0 )
924                 return;
925
926         /* yeah, slow... :/ */
927         if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep))
928         {
929                 snprintf( p_client_buffer, client_buffer_size, "no ac_adapters?" );
930                 return; 
931         }
932
933         snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf );
934          
935
936         fp = open_file(buf2, &rep);
937         if (!fp) {
938                 snprintf( p_client_buffer, client_buffer_size, "No ac adapter found.... where is it?" );
939                 return;
940         }
941         memset(buf,0,sizeof(buf));
942         fscanf(fp, "%*s %99s", buf );
943         fclose(fp);
944
945         snprintf( p_client_buffer, client_buffer_size, "%s", buf );
946
947         return;
948 }
949
950 /*
951 /proc/acpi/thermal_zone/THRM/cooling_mode
952 cooling mode:            active
953 /proc/acpi/thermal_zone/THRM/polling_frequency
954 <polling disabled>
955 /proc/acpi/thermal_zone/THRM/state
956 state:                   ok
957 /proc/acpi/thermal_zone/THRM/temperature
958 temperature:             45 C
959 /proc/acpi/thermal_zone/THRM/trip_points
960 critical (S5):           73 C
961 passive:                 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
962 */
963
964 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
965 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
966
967 int open_acpi_temperature(const char *name)
968 {
969         char path[256];
970         char buf[256];
971         int fd;
972
973         if (name == NULL || strcmp(name, "*") == 0) {
974                 static int rep;
975                 if (!get_first_file_in_a_directory
976                     (ACPI_THERMAL_DIR, buf, &rep))
977                         return -1;
978                 name = buf;
979         }
980
981         snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
982
983         fd = open(path, O_RDONLY);
984         if (fd < 0)
985                 ERR("can't open '%s': %s", path, strerror(errno));
986
987         return fd;
988 }
989
990 static double last_acpi_temp;
991 static double last_acpi_temp_time;
992
993 double get_acpi_temperature(int fd)
994 {
995         if (fd <= 0)
996                 return 0;
997
998         /* don't update acpi temperature too often */
999         if (current_update_time - last_acpi_temp_time < 11.32) {
1000                 return last_acpi_temp;
1001         }
1002         last_acpi_temp_time = current_update_time;
1003
1004         /* seek to beginning */
1005         lseek(fd, 0, SEEK_SET);
1006
1007         /* read */
1008         {
1009                 char buf[256];
1010                 int n;
1011                 n = read(fd, buf, 255);
1012                 if (n < 0)
1013                         ERR("can't read fd %d: %s", fd, strerror(errno));
1014                 else {
1015                         buf[n] = '\0';
1016                         sscanf(buf, "temperature: %lf", &last_acpi_temp);
1017                 }
1018         }
1019
1020         return last_acpi_temp;
1021 }
1022
1023 /*
1024 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info 
1025 present:                 yes
1026 design capacity:         4400 mAh
1027 last full capacity:      4064 mAh
1028 battery technology:      rechargeable
1029 design voltage:          14800 mV
1030 design capacity warning: 300 mAh
1031 design capacity low:     200 mAh
1032 capacity granularity 1:  32 mAh
1033 capacity granularity 2:  32 mAh
1034 model number:            02KT
1035 serial number:           16922
1036 battery type:            LION
1037 OEM info:                SANYO
1038 */
1039
1040 /*
1041 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1042 present:                 yes
1043 capacity state:          ok
1044 charging state:          unknown
1045 present rate:            0 mA
1046 remaining capacity:      4064 mAh
1047 present voltage:         16608 mV
1048 */
1049
1050 /*
1051 2213<@jupet kellari ö> jupet@lagi-unstable:~$ cat /proc/apm 
1052 2213<@jupet kellari ö> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1053 2213<@jupet kellari ö> (-1 ollee ei akkua kiinni, koska akku on pöydällä)
1054 2214<@jupet kellari ö> jupet@lagi-unstable:~$ cat /proc/apm 
1055 2214<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1056
1057 2238<@jupet kellari ö> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1058 2239<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1059
1060 2240<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori päällä
1061 2241<@jupet kellari ö> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori päällä mutta ilman verkkovirtaa
1062 */
1063
1064 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1065 #define APM_PATH "/proc/apm"
1066
1067 static FILE *acpi_bat_fp;
1068 static FILE *apm_bat_fp;
1069
1070 static int acpi_last_full;
1071
1072 static char last_battery_str[64];
1073
1074 static double last_battery_time;
1075
1076 void get_battery_stuff(char *buf, unsigned int n, const char *bat)
1077 {
1078         static int rep, rep2;
1079         char acpi_path[128];
1080         snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1081
1082         /* don't update battery too often */
1083         if (current_update_time - last_battery_time < 29.5) {
1084                 snprintf(buf, n, "%s", last_battery_str);
1085                 return;
1086         }
1087         last_battery_time = current_update_time;
1088
1089         /* first try ACPI */
1090
1091         if (acpi_bat_fp == NULL && apm_bat_fp == NULL)
1092                 acpi_bat_fp = open_file(acpi_path, &rep);
1093
1094         if (acpi_bat_fp != NULL) {
1095                 int present_rate = -1;
1096                 int remaining_capacity = -1;
1097                 char charging_state[64];
1098
1099                 /* read last full capacity if it's zero */
1100                 if (acpi_last_full == 0) {
1101                         static int rep;
1102                         char path[128];
1103                         FILE *fp;
1104                         snprintf(path, 127,
1105                                  ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1106                         fp = open_file(path, &rep);
1107                         if (fp != NULL) {
1108                                 while (!feof(fp)) {
1109                                         char b[256];
1110                                         if (fgets(b, 256, fp) == NULL)
1111                                                 break;
1112
1113                                         if (sscanf
1114                                             (b, "last full capacity: %d",
1115                                              &acpi_last_full) != 0)
1116                                                 break;
1117                                 }
1118
1119                                 fclose(fp);
1120                         }
1121                 }
1122
1123                 fseek(acpi_bat_fp, 0, SEEK_SET);
1124
1125                 strcpy(charging_state, "unknown");
1126
1127                 while (!feof(acpi_bat_fp)) {
1128                         char buf[256];
1129                         if (fgets(buf, 256, acpi_bat_fp) == NULL)
1130                                 break;
1131
1132                         /* let's just hope units are ok */
1133                         if (buf[0] == 'c')
1134                                 sscanf(buf, "charging state: %63s",
1135                                        charging_state);
1136                         else if (buf[0] == 'p')
1137                                 sscanf(buf, "present rate: %d",
1138                                        &present_rate);
1139                         else if (buf[0] == 'r')
1140                                 sscanf(buf, "remaining capacity: %d",
1141                                        &remaining_capacity);
1142                 }
1143
1144                 /* charging */
1145                 if (strcmp(charging_state, "charging") == 0) {
1146                         if (acpi_last_full != 0 && present_rate > 0) {
1147                                 strcpy(last_battery_str, "charging ");
1148                                 format_seconds(last_battery_str + 9,
1149                                                63 - 9,
1150                                                (acpi_last_full -
1151                                                 remaining_capacity) * 60 *
1152                                                60 / present_rate);
1153                         } else if (acpi_last_full != 0
1154                                    && present_rate <= 0) {
1155                                 sprintf(last_battery_str, "charging %d%%",
1156                                         remaining_capacity * 100 /
1157                                         acpi_last_full);
1158                         } else {
1159                                 strcpy(last_battery_str, "charging");
1160                         }
1161                 }
1162                 /* discharging */
1163                 else if (strcmp(charging_state, "discharging") == 0) {
1164                         if (present_rate > 0)
1165                                 format_seconds(last_battery_str, 63,
1166                                                (remaining_capacity * 60 *
1167                                                 60) / present_rate);
1168                         else
1169                                 sprintf(last_battery_str,
1170                                         "discharging %d%%",
1171                                         remaining_capacity * 100 /
1172                                         acpi_last_full);
1173                 }
1174                 /* charged */
1175                 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1176                 else if (strcmp(charging_state, "charged") == 0) {
1177                         if (acpi_last_full != 0
1178                             && remaining_capacity != acpi_last_full)
1179                                 sprintf(last_battery_str, "charged %d%%",
1180                                         remaining_capacity * 100 /
1181                                         acpi_last_full);
1182                         else
1183                                 strcpy(last_battery_str, "charged");
1184                 }
1185                 /* unknown, probably full / AC */
1186                 else {
1187                         if (acpi_last_full != 0
1188                             && remaining_capacity != acpi_last_full)
1189                                 sprintf(last_battery_str, "unknown %d%%",
1190                                         remaining_capacity * 100 /
1191                                         acpi_last_full);
1192                         else
1193                                 strcpy(last_battery_str, "AC");
1194                 }
1195         } else {
1196                 /* APM */
1197                 if (apm_bat_fp == NULL)
1198                         apm_bat_fp = open_file(APM_PATH, &rep2);
1199
1200                 if (apm_bat_fp != NULL) {
1201                         int ac, status, flag, life;
1202
1203                         fscanf(apm_bat_fp,
1204                                "%*s %*s %*x %x   %x       %x     %d%%",
1205                                &ac, &status, &flag, &life);
1206
1207                         if (life == -1) {
1208                                 /* could check now that there is ac */
1209                                 snprintf(last_battery_str, 64, "AC");
1210                         } else if (ac && life != 100) { /* could check that status==3 here? */
1211                                 snprintf(last_battery_str, 64,
1212                                          "charging %d%%", life);
1213                         } else {
1214                                 snprintf(last_battery_str, 64, "%d%%",
1215                                          life);
1216                         }
1217
1218                         /* it seemed to buffer it so file must be closed (or could use syscalls
1219                          * directly but I don't feel like coding it now) */
1220                         fclose(apm_bat_fp);
1221                         apm_bat_fp = NULL;
1222                 }
1223         }
1224
1225         snprintf(buf, n, "%s", last_battery_str);
1226 }
1227
1228 void update_top()
1229 {
1230         show_nice_processes = 1;
1231         process_find_top(info.cpu, info.memu);
1232         info.first_process = get_first_process();
1233 }
1234
1235
1236 /*
1237  *  The following ifdefs were adapted from gkrellm
1238  */
1239 #include <linux/major.h>
1240
1241 #if ! defined (MD_MAJOR)
1242 #define MD_MAJOR 9
1243 #endif
1244
1245 #if !defined(LVM_BLK_MAJOR)
1246 #define LVM_BLK_MAJOR 58
1247 #endif
1248
1249 #if !defined(NBD_MAJOR)
1250 #define NBD_MAJOR 43
1251 #endif
1252
1253 void update_diskio()
1254 {
1255         static unsigned int last = UINT_MAX;
1256         static FILE* fp;
1257
1258         char buf[512];
1259         int major, minor;
1260         unsigned int current = 0;
1261         unsigned int reads, writes = 0;
1262         int col_count = 0;
1263
1264         if (!fp) {
1265                 fp = fopen("/proc/diskstats", "r");
1266         } else {
1267                 fseek(fp, 0, SEEK_SET);
1268         }
1269
1270         /* read reads and writes from all disks (minor = 0), including
1271          * cd-roms and floppies, and summ them up
1272          */
1273         current = 0;
1274         while (!feof(fp)) {
1275                 fgets(buf, 512, fp);
1276                 col_count = sscanf(buf, "%u %u %*s %*u %*u %u %*u %*u %*u %u",
1277                                    &major, &minor, &reads, &writes);
1278                 /* ignore subdevices (they have only 3 matching entries in their line)
1279                  * and virtual devices (LVM, network block devices, RAM disks, Loopback)
1280                  *
1281                  * XXX ignore devices which are part of a SW RAID (MD_MAJOR)
1282                  */
1283                 if (col_count > 3 &&
1284                     major != LVM_BLK_MAJOR && major != NBD_MAJOR &&
1285                     major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
1286                         current += reads + writes;
1287                 }
1288         }
1289
1290         /* since the values in /proc/diststats are absolute, we have
1291          * to substract our last reading. The numbers stand for
1292          * "sectors read", and we therefore have to divide by two to
1293          * get KB */
1294         int tot = ((double)(current-last)/2);
1295         if (last > current) {
1296                 /* we hit this either if it's the very first time we
1297                  * run this, or when /proc/diskstats overflows; while
1298                  * 0 is not correct, it's at least not way off */
1299                 tot = 0;
1300         }
1301         last = current;
1302
1303         diskio_value = tot;
1304 }
1305