sticky hints
[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 defined(__alpha)
865                 if (strncmp(s, "cycle frequency [Hz]", 20) == 0) {              // different on alpha
866 #else
867                 if (strncmp(s, "clock", 5) == 0) {      // this is different on ppc for some reason
868 #endif // defined(__alpha)
869 #endif // defined(__i386) || defined(__x86_64)
870
871                 strcpy(frequency, strchr(s, ':') + 2);  //copy just the number
872 #if defined(__alpha)
873                 frequency[strlen(frequency) - 6] = '\0';// strip " est.\n"
874                 freq = strtod(frequency, NULL)/1000000; // kernel reports in Hz 
875 #else
876                 frequency[strlen(frequency) - 1] = '\0'; // strip \n
877                 freq = strtod(frequency, NULL);
878 #endif
879                 break;
880                 }
881         }
882         
883         fclose(f);
884         snprintf( p_client_buffer, client_buffer_size, p_format, (float)freq/divisor );
885         return;
886 }
887
888
889 #define ACPI_FAN_DIR "/proc/acpi/fan/"
890
891 void get_acpi_fan( char * p_client_buffer, size_t client_buffer_size )
892 {
893         static int rep;
894         char buf[256];
895         char buf2[256];
896         FILE *fp;
897
898         if ( !p_client_buffer || client_buffer_size <= 0 )
899                 return;
900
901         /* yeah, slow... :/ */
902         if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep))
903         {
904                 snprintf( p_client_buffer, client_buffer_size, "no fans?" );
905                 return;
906         }
907
908         snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf );
909
910         fp = open_file(buf2, &rep);
911         if (!fp) {
912                 snprintf( p_client_buffer, client_buffer_size, "can't open fan's state file" );
913                 return;
914         }
915         memset(buf,0,sizeof(buf));
916         fscanf(fp, "%*s %99s", buf);
917         fclose(fp);
918
919         snprintf( p_client_buffer, client_buffer_size, "%s", buf );
920
921         return;
922 }
923
924 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
925
926 void get_acpi_ac_adapter( char * p_client_buffer, size_t client_buffer_size )
927 {
928         static int rep;
929         char buf[256];
930         char buf2[256];
931         FILE *fp;
932
933         if ( !p_client_buffer || client_buffer_size <= 0 )
934                 return;
935
936         /* yeah, slow... :/ */
937         if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep))
938         {
939                 snprintf( p_client_buffer, client_buffer_size, "no ac_adapters?" );
940                 return; 
941         }
942
943         snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf );
944          
945
946         fp = open_file(buf2, &rep);
947         if (!fp) {
948                 snprintf( p_client_buffer, client_buffer_size, "No ac adapter found.... where is it?" );
949                 return;
950         }
951         memset(buf,0,sizeof(buf));
952         fscanf(fp, "%*s %99s", buf );
953         fclose(fp);
954
955         snprintf( p_client_buffer, client_buffer_size, "%s", buf );
956
957         return;
958 }
959
960 /*
961 /proc/acpi/thermal_zone/THRM/cooling_mode
962 cooling mode:            active
963 /proc/acpi/thermal_zone/THRM/polling_frequency
964 <polling disabled>
965 /proc/acpi/thermal_zone/THRM/state
966 state:                   ok
967 /proc/acpi/thermal_zone/THRM/temperature
968 temperature:             45 C
969 /proc/acpi/thermal_zone/THRM/trip_points
970 critical (S5):           73 C
971 passive:                 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
972 */
973
974 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
975 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
976
977 int open_acpi_temperature(const char *name)
978 {
979         char path[256];
980         char buf[256];
981         int fd;
982
983         if (name == NULL || strcmp(name, "*") == 0) {
984                 static int rep;
985                 if (!get_first_file_in_a_directory
986                     (ACPI_THERMAL_DIR, buf, &rep))
987                         return -1;
988                 name = buf;
989         }
990
991         snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
992
993         fd = open(path, O_RDONLY);
994         if (fd < 0)
995                 ERR("can't open '%s': %s", path, strerror(errno));
996
997         return fd;
998 }
999
1000 static double last_acpi_temp;
1001 static double last_acpi_temp_time;
1002
1003 double get_acpi_temperature(int fd)
1004 {
1005         if (fd <= 0)
1006                 return 0;
1007
1008         /* don't update acpi temperature too often */
1009         if (current_update_time - last_acpi_temp_time < 11.32) {
1010                 return last_acpi_temp;
1011         }
1012         last_acpi_temp_time = current_update_time;
1013
1014         /* seek to beginning */
1015         lseek(fd, 0, SEEK_SET);
1016
1017         /* read */
1018         {
1019                 char buf[256];
1020                 int n;
1021                 n = read(fd, buf, 255);
1022                 if (n < 0)
1023                         ERR("can't read fd %d: %s", fd, strerror(errno));
1024                 else {
1025                         buf[n] = '\0';
1026                         sscanf(buf, "temperature: %lf", &last_acpi_temp);
1027                 }
1028         }
1029
1030         return last_acpi_temp;
1031 }
1032
1033 /*
1034 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info 
1035 present:                 yes
1036 design capacity:         4400 mAh
1037 last full capacity:      4064 mAh
1038 battery technology:      rechargeable
1039 design voltage:          14800 mV
1040 design capacity warning: 300 mAh
1041 design capacity low:     200 mAh
1042 capacity granularity 1:  32 mAh
1043 capacity granularity 2:  32 mAh
1044 model number:            02KT
1045 serial number:           16922
1046 battery type:            LION
1047 OEM info:                SANYO
1048 */
1049
1050 /*
1051 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1052 present:                 yes
1053 capacity state:          ok
1054 charging state:          unknown
1055 present rate:            0 mA
1056 remaining capacity:      4064 mAh
1057 present voltage:         16608 mV
1058 */
1059
1060 /*
1061 2213<@jupet kellari ö> jupet@lagi-unstable:~$ cat /proc/apm 
1062 2213<@jupet kellari ö> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1063 2213<@jupet kellari ö> (-1 ollee ei akkua kiinni, koska akku on pöydällä)
1064 2214<@jupet kellari ö> jupet@lagi-unstable:~$ cat /proc/apm 
1065 2214<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1066
1067 2238<@jupet kellari ö> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1068 2239<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1069
1070 2240<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori päällä
1071 2241<@jupet kellari ö> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori päällä mutta ilman verkkovirtaa
1072 */
1073
1074 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1075 #define APM_PATH "/proc/apm"
1076
1077 static FILE *acpi_bat_fp;
1078 static FILE *apm_bat_fp;
1079
1080 static int acpi_last_full;
1081
1082 static char last_battery_str[64];
1083
1084 static double last_battery_time;
1085
1086 void get_battery_stuff(char *buf, unsigned int n, const char *bat)
1087 {
1088         static int rep, rep2;
1089         char acpi_path[128];
1090         int design_capacity;
1091         snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1092
1093         /* don't update battery too often */
1094         if (current_update_time - last_battery_time < 29.5) {
1095                 snprintf(buf, n, "%s", last_battery_str);
1096                 return;
1097         }
1098         last_battery_time = current_update_time;
1099
1100         /* first try ACPI */
1101
1102         if (acpi_bat_fp == NULL && apm_bat_fp == NULL)
1103                 acpi_bat_fp = open_file(acpi_path, &rep);
1104
1105         if (acpi_bat_fp != NULL) {
1106                 int present_rate = -1;
1107                 int remaining_capacity = -1;
1108                 char charging_state[64];
1109
1110                 /* read last full capacity if it's zero */
1111                 if (acpi_last_full == 0) {
1112                         static int rep;
1113                         char path[128];
1114                         FILE *fp;
1115                         snprintf(path, 127,
1116                                  ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1117                         fp = open_file(path, &rep);
1118                         if (fp != NULL) {
1119                                 while (!feof(fp)) {
1120                                         char b[256];
1121                                         if (fgets(b, 256, fp) == NULL)
1122                                                 break;
1123                                         if (sscanf(b, "design capacity: %d", &design_capacity) != 0) {
1124                                                 continue;
1125                                         }
1126                                         if (sscanf(b, "last full capacity: %d", &acpi_last_full) != 0) {
1127                                                 break;
1128                                         }
1129                                 }
1130
1131                                 fclose(fp);
1132                         }
1133                 }
1134
1135                 fseek(acpi_bat_fp, 0, SEEK_SET);
1136
1137                 strcpy(charging_state, "unknown");
1138
1139                 while (!feof(acpi_bat_fp)) {
1140                         char buf[256];
1141                         if (fgets(buf, 256, acpi_bat_fp) == NULL)
1142                                 break;
1143
1144                         /* let's just hope units are ok */
1145                         if (buf[0] == 'c')
1146                                 sscanf(buf, "charging state: %63s",
1147                                        charging_state);
1148                         else if (buf[0] == 'p')
1149                                 sscanf(buf, "present rate: %d",
1150                                        &present_rate);
1151                         else if (buf[0] == 'r')
1152                                 sscanf(buf, "remaining capacity: %d",
1153                                        &remaining_capacity);
1154                 }
1155
1156                 /* charging */
1157                 if (strcmp(charging_state, "charging") == 0) {
1158                         if (acpi_last_full != 0 && present_rate > 0) {
1159                                 strcpy(last_battery_str, "charging ");
1160                                 format_seconds(last_battery_str + 9,
1161                                                63 - 9,
1162                                                (acpi_last_full -
1163                                                 remaining_capacity) * 60 *
1164                                                60 / present_rate);
1165                         } else if (acpi_last_full != 0
1166                                    && present_rate <= 0) {
1167                                 sprintf(last_battery_str, "charging %d%%",
1168                                         remaining_capacity * 100 /
1169                                         acpi_last_full);
1170                         } else {
1171                                 strcpy(last_battery_str, "charging");
1172                         }
1173                 }
1174                 /* discharging */
1175                 else if (strcmp(charging_state, "discharging") == 0) {
1176                         if (present_rate > 0)
1177                                 format_seconds(last_battery_str, 63,
1178                                                (remaining_capacity * 60 *
1179                                                 60) / present_rate);
1180                         else
1181                                 sprintf(last_battery_str,
1182                                         "discharging %d%%",
1183                                         remaining_capacity * 100 /
1184                                         acpi_last_full);
1185                 }
1186                 /* charged */
1187                 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1188                 else if (strcmp(charging_state, "charged") == 0) {
1189                         if (acpi_last_full != 0 && remaining_capacity != acpi_last_full) {
1190                                 sprintf(last_battery_str, "charged %d%%", remaining_capacity * 100 / design_capacity);
1191                         } else {
1192                                 strcpy(last_battery_str, "charged");
1193                         }
1194                 }
1195                 /* unknown, probably full / AC */
1196                 else {
1197                         if (acpi_last_full != 0
1198                             && remaining_capacity != acpi_last_full)
1199                                 sprintf(last_battery_str, "unknown %d%%",
1200                                         remaining_capacity * 100 /
1201                                         acpi_last_full);
1202                         else
1203                                 strcpy(last_battery_str, "AC");
1204                 }
1205         } else {
1206                 /* APM */
1207                 if (apm_bat_fp == NULL)
1208                         apm_bat_fp = open_file(APM_PATH, &rep2);
1209
1210                 if (apm_bat_fp != NULL) {
1211                         int ac, status, flag, life;
1212
1213                         fscanf(apm_bat_fp,
1214                                "%*s %*s %*x %x   %x       %x     %d%%",
1215                                &ac, &status, &flag, &life);
1216
1217                         if (life == -1) {
1218                                 /* could check now that there is ac */
1219                                 snprintf(last_battery_str, 64, "AC");
1220                         } else if (ac && life != 100) { /* could check that status==3 here? */
1221                                 snprintf(last_battery_str, 64,
1222                                          "charging %d%%", life);
1223                         } else {
1224                                 snprintf(last_battery_str, 64, "%d%%",
1225                                          life);
1226                         }
1227
1228                         /* it seemed to buffer it so file must be closed (or could use syscalls
1229                          * directly but I don't feel like coding it now) */
1230                         fclose(apm_bat_fp);
1231                         apm_bat_fp = NULL;
1232                 }
1233         }
1234
1235         snprintf(buf, n, "%s", last_battery_str);
1236 }
1237
1238 void update_top()
1239 {
1240         show_nice_processes = 1;
1241         process_find_top(info.cpu, info.memu);
1242         info.first_process = get_first_process();
1243 }
1244
1245
1246 /*
1247  *  The following ifdefs were adapted from gkrellm
1248  */
1249 #include <linux/major.h>
1250
1251 #if ! defined (MD_MAJOR)
1252 #define MD_MAJOR 9
1253 #endif
1254
1255 #if !defined(LVM_BLK_MAJOR)
1256 #define LVM_BLK_MAJOR 58
1257 #endif
1258
1259 #if !defined(NBD_MAJOR)
1260 #define NBD_MAJOR 43
1261 #endif
1262
1263 void update_diskio()
1264 {
1265         static unsigned int last = UINT_MAX;
1266         static FILE* fp;
1267
1268         char buf[512];
1269         int major, minor;
1270         unsigned int current = 0;
1271         unsigned int reads, writes = 0;
1272         int col_count = 0;
1273
1274         if (!fp) {
1275                 fp = fopen("/proc/diskstats", "r");
1276         } else {
1277                 fseek(fp, 0, SEEK_SET);
1278         }
1279
1280         /* read reads and writes from all disks (minor = 0), including
1281          * cd-roms and floppies, and summ them up
1282          */
1283         current = 0;
1284         while (!feof(fp)) {
1285                 fgets(buf, 512, fp);
1286                 col_count = sscanf(buf, "%u %u %*s %*u %*u %u %*u %*u %*u %u",
1287                                    &major, &minor, &reads, &writes);
1288                 /* ignore subdevices (they have only 3 matching entries in their line)
1289                  * and virtual devices (LVM, network block devices, RAM disks, Loopback)
1290                  *
1291                  * XXX ignore devices which are part of a SW RAID (MD_MAJOR)
1292                  */
1293                 if (col_count > 3 &&
1294                     major != LVM_BLK_MAJOR && major != NBD_MAJOR &&
1295                     major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
1296                         current += reads + writes;
1297                 }
1298         }
1299
1300         /* since the values in /proc/diststats are absolute, we have
1301          * to substract our last reading. The numbers stand for
1302          * "sectors read", and we therefore have to divide by two to
1303          * get KB */
1304         int tot = ((double)(current-last)/2);
1305         if (last > current) {
1306                 /* we hit this either if it's the very first time we
1307                  * run this, or when /proc/diskstats overflows; while
1308                  * 0 is not correct, it's at least not way off */
1309                 tot = 0;
1310         }
1311         last = current;
1312
1313         diskio_value = tot;
1314 }
1315