get rid of annoying warning
[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 static struct sysinfo s_info;
33
34 static int show_nice_processes;
35
36 void prepare_update()
37 {
38 }
39
40 static void update_sysinfo()
41 {
42         sysinfo(&s_info);
43
44         info.uptime = (double) s_info.uptime;
45
46         /* there was some problem with these */
47 #if 0
48 //      info.loadavg[0] = s_info.loads[0] / 100000.0f;
49         info.loadavg[1] = s_info.loads[1] / 100000.0f;
50         info.loadavg[2] = s_info.loads[2] / 100000.0f;
51         gkrelltop_process_find_top_three info.mask |= 1 << INFO_LOADAVG;
52 #endif
53
54         info.procs = s_info.procs;
55
56         /* these aren't nice, no cache and should check kernel version for mem_unit */
57 #if 0
58         info.memmax = s_info.totalram;
59         info.mem = s_info.totalram - s_info.freeram;
60         info.swapmax = s_info.totalswap;
61         info.swap = s_info.totalswap - s_info.swap;
62         info.mask |= 1 << INFO_MEM;
63 #endif
64
65         info.mask |= (1 << INFO_UPTIME) | (1 << INFO_PROCS);
66 }
67
68 void update_uptime()
69 {
70         /* prefers sysinfo() for uptime, I don't really know which one is better
71          * (=faster?) */
72 #ifdef USE_PROC_UPTIME
73         static int rep;
74         FILE *fp = open_file("/proc/uptime", &rep);
75         if (!fp)
76                 return 0;
77         fscanf(fp, "%lf", &info.uptime);
78         fclose(fp);
79
80         info.mask |= (1 << INFO_UPTIME);
81 #else
82         update_sysinfo();
83 #endif
84 }
85
86 /* these things are also in sysinfo except Buffers:, that's why I'm reading
87 * them from proc */
88
89 static FILE *meminfo_fp;
90
91 void update_meminfo()
92 {
93         static int rep;
94         /*  unsigned int a; */
95         char buf[256];
96
97         info.mem = info.memmax = info.swap = info.swapmax = info.bufmem =
98             info.buffers = info.cached = 0;
99
100         if (meminfo_fp == NULL)
101                 meminfo_fp = open_file("/proc/meminfo", &rep);
102         else
103                 fseek(meminfo_fp, 0, SEEK_SET);
104         if (meminfo_fp == NULL)
105                 return;
106
107         while (!feof(meminfo_fp)) {
108                 if (fgets(buf, 255, meminfo_fp) == NULL)
109                         break;
110
111                 if (strncmp(buf, "MemTotal:", 9) == 0) {
112                         sscanf(buf, "%*s %lu", &info.memmax);
113                 } else if (strncmp(buf, "MemFree:", 8) == 0) {
114                         sscanf(buf, "%*s %lu", &info.mem);
115                 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
116                         sscanf(buf, "%*s %lu", &info.swapmax);
117                 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
118                         sscanf(buf, "%*s %lu", &info.swap);
119                 } else if (strncmp(buf, "Buffers:", 8) == 0) {
120                         sscanf(buf, "%*s %lu", &info.buffers);
121                 } else if (strncmp(buf, "Cached:", 7) == 0) {
122                         sscanf(buf, "%*s %lu", &info.cached);
123                 }
124         }
125         
126         info.mem = info.memmax - info.mem;
127         info.swap = info.swapmax - info.swap;
128
129         info.bufmem = info.cached + info.buffers;
130
131         info.mask |= (1 << INFO_MEM) | (1 << INFO_BUFFERS);
132 }
133
134 static FILE *net_dev_fp;
135 static FILE *net_wireless_fp;
136
137 inline void update_net_stats()
138 {
139         static int rep;
140         // FIXME: arbitrary size chosen to keep code simple.
141         int i, i2;
142         unsigned int curtmp1, curtmp2;
143         unsigned int k;
144         struct ifconf conf;
145
146
147         char buf[256];
148         double delta;
149
150         /* get delta */
151         delta = current_update_time - last_update_time;
152         if (delta <= 0.0001)
153                 return;
154
155         /* open file and ignore first two lines */
156         if (net_dev_fp == NULL)
157                 net_dev_fp = open_file("/proc/net/dev", &rep);
158         else
159                 fseek(net_dev_fp, 0, SEEK_SET);
160         if (!net_dev_fp)
161                 return;
162
163         fgets(buf, 255, net_dev_fp);    /* garbage */
164         fgets(buf, 255, net_dev_fp);    /* garbage (field names) */
165
166         /* read each interface */
167         for (i2 = 0; i2 < 16; i2++) {
168                 struct net_stat *ns;
169                 char *s, *p;
170                 long long r, t, last_recv, last_trans;
171
172                 if (fgets(buf, 255, net_dev_fp) == NULL)
173                         break;
174                 p = buf;
175                 while (isspace((int) *p))
176                         p++;
177
178                 s = p;
179
180                 while (*p && *p != ':')
181                         p++;
182                 if (*p == '\0')
183                         continue;
184                 *p = '\0';
185                 p++;
186
187                 ns = get_net_stat(s);
188                 ns->up = 1;
189                 last_recv = ns->recv;
190                 last_trans = ns->trans;
191
192                 sscanf(p,
193                        /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
194                        "%Ld  %*d     %*d  %*d  %*d  %*d   %*d        %*d       %Ld",
195                        &r, &t);
196
197                 /* if recv or trans is less than last time, an overflow happened */
198
199                 if (r < ns->last_read_recv)
200                         ns->recv +=
201                             ((long long) 4294967295U -
202                              ns->last_read_recv) + r;
203                 else
204                         ns->recv += (r - ns->last_read_recv);
205                 ns->last_read_recv = r;
206
207                 if (t < ns->last_read_trans)
208                         ns->trans +=
209                             ((long long) 4294967295U -
210                              ns->last_read_trans) + t;
211                 else
212                         ns->trans += (t - ns->last_read_trans);
213                 ns->last_read_trans = t;
214
215                 /*** ip addr patch ***/
216                 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
217
218                 conf.ifc_buf = malloc(sizeof(struct ifreq) * 16);
219
220                 conf.ifc_len = sizeof(struct ifreq) * 16;
221
222                 ioctl((long) i, SIOCGIFCONF, &conf);
223
224                 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
225                         struct net_stat *ns;
226                         ns = get_net_stat(((struct ifreq *) conf.
227                                            ifc_buf)[k].ifr_ifrn.ifrn_name);
228                         ns->addr =
229                             ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.
230                             ifru_addr;
231                 }
232
233                 close((long) i);
234
235                 free(conf.ifc_buf);
236
237
238                 /*** end ip addr patch ***/
239
240
241                 /* calculate speeds */
242                 ns->net_rec[0] = (ns->recv - last_recv) / delta;
243                 ns->net_trans[0] = (ns->trans - last_trans) / delta;
244                 curtmp1 = 0;
245                 curtmp2 = 0;
246                 // get an average
247                 for (i = 0; (unsigned) i < info.net_avg_samples; i++) {
248                         curtmp1 += ns->net_rec[i];
249                         curtmp2 += ns->net_trans[i];
250                 }
251                 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
252                 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
253                 if (info.net_avg_samples > 1) {
254                         for (i = info.net_avg_samples; i > 1; i--) {
255                                 ns->net_rec[i - 1] = ns->net_rec[i - 2];
256                                 ns->net_trans[i - 1] =
257                                     ns->net_trans[i - 2];
258                         }
259                 }
260
261
262
263         }
264
265         /* fclose(net_dev_fp); net_dev_fp = NULL; */
266 }
267
268 inline void update_wifi_stats()
269 {
270         /** wireless stats patch by Bobby Beckmann **/
271         static int rep;
272         int i;
273         char buf[256];
274         /*open file and ignore first two lines       sorry, this code sucks ass right now, i'll clean it up later */
275         if (net_wireless_fp == NULL)
276                 net_wireless_fp = open_file("/proc/net/wireless", &rep);
277         else
278                 fseek(net_wireless_fp, 0, SEEK_SET);
279         if (net_wireless_fp == NULL)
280                 return;
281
282         fgets(buf, 255, net_wireless_fp);       /* garbage */
283         fgets(buf, 255, net_wireless_fp);       /* garbage (field names) */
284
285         /* read each interface */
286         for (i = 0; i < 16; i++) {
287                 struct net_stat *ns;
288                 char *s, *p;
289                 int l, m, n;
290
291                 if (fgets(buf, 255, net_wireless_fp) == NULL)
292                         break;
293                 p = buf;
294                 while (isspace((int) *p))
295                         p++;
296
297                 s = p;
298
299                 while (*p && *p != ':')
300                         p++;
301                 if (*p == '\0')
302                         continue;
303                 *p = '\0';
304                 p++;
305
306                 ns = get_net_stat(s);
307
308                 sscanf(p, "%*d   %d.  %d.  %d", &l, &m, &n);
309
310                 ns->linkstatus = (int) (log(MIN(MAX(l,1),92)) / log(92) * 100);
311
312         }
313
314         /*** end wireless patch ***/
315 }
316
317 int result;
318
319 void update_total_processes()
320 {
321         update_sysinfo();
322 }
323
324 #define CPU_SAMPLE_COUNT 15
325 struct cpu_info {
326         unsigned long cpu_user;
327         unsigned long cpu_system;
328         unsigned long cpu_nice;
329         double last_cpu_sum;
330         unsigned long clock_ticks;
331         double cpu_val[CPU_SAMPLE_COUNT];
332 };
333 static short cpu_setup = 0;
334 static int rep;
335
336
337 static FILE *stat_fp;
338
339 void get_cpu_count()
340 {
341         char buf[256];
342         if (stat_fp == NULL)
343                 stat_fp = open_file("/proc/stat", &rep);
344         else
345                 fseek(stat_fp, 0, SEEK_SET);
346         if (stat_fp == NULL)
347                 return;
348
349         info.cpu_count = 0;
350
351         while (!feof(stat_fp)) {
352                 if (fgets(buf, 255, stat_fp) == NULL)
353                         break;
354
355                 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
356                         info.cpu_count++;
357                 }
358         }
359         info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
360 }
361
362
363 inline static void update_stat()
364 {
365         static struct cpu_info *cpu = NULL;
366         char buf[256];
367         unsigned int i;
368         unsigned int index;
369         double curtmp;
370         if (!cpu_setup) {
371                 get_cpu_count();
372                 cpu_setup = 1;
373         }
374         if (cpu == NULL) {
375                 cpu = malloc((info.cpu_count + 1) * sizeof(struct cpu_info));
376                 for (index = 0; index < info.cpu_count + 1; ++index) {
377                         cpu[index].clock_ticks = 0;
378                         cpu[index].last_cpu_sum = 0;
379                         for (i = 0; i < CPU_SAMPLE_COUNT; ++i) {
380                                 cpu[index].cpu_val[i] = 0;
381                         }
382                 }
383         }
384         if (stat_fp == NULL) {
385                 stat_fp = open_file("/proc/stat", &rep);
386         } else {
387                 fseek(stat_fp, 0, SEEK_SET);
388         }
389         if (stat_fp == NULL) {
390                 return;
391         }
392         index = 0;
393         while (!feof(stat_fp)) {
394                 if (fgets(buf, 255, stat_fp) == NULL)
395                         break;
396
397                 if (strncmp(buf, "procs_running ", 14) == 0) {
398                         sscanf(buf, "%*s %d", &info.run_procs);
399                         info.mask |= (1 << INFO_RUN_PROCS);
400                 } else if (strncmp(buf, "cpu ", 4) == 0) {
401                         sscanf(buf, "%*s %lu %lu %lu", &(cpu[index].cpu_user), &(cpu[index].cpu_nice), &(cpu[index].cpu_system));
402                         index++;
403                         info.mask |= (1 << INFO_CPU);
404                 } else if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3]) && index <= info.cpu_count) {
405                         sscanf(buf, "%*s %lu %lu %lu", &(cpu[index].cpu_user), &(cpu[index].cpu_nice), &(cpu[index].cpu_system));
406                         index++;
407                         info.mask |= (1 << INFO_CPU);
408                 }
409         }
410         for (index = 0; index < info.cpu_count + 1; index++) {
411                 double delta;
412                 delta = current_update_time - last_update_time;
413                 if (delta <= 0.001) {
414                         return;
415                 }
416
417                 if (cpu[index].clock_ticks == 0) {
418                         cpu[index].clock_ticks = sysconf(_SC_CLK_TCK);
419                 }
420                 curtmp = 0;
421                 cpu[index].cpu_val[0] =
422                                 (cpu[index].cpu_user + cpu[index].cpu_nice + cpu[index].cpu_system -
423                                 cpu[index].last_cpu_sum) / delta / (double) cpu[index].clock_ticks;
424                 for (i = 0; i < info.cpu_avg_samples; i++) {
425                         curtmp += cpu[index].cpu_val[i];
426                 }
427                 if (index == 0) {
428                         info.cpu_usage[index] = curtmp / info.cpu_avg_samples / info.cpu_count;
429                 } else {
430                         info.cpu_usage[index] = curtmp / info.cpu_avg_samples;
431                 }
432                 cpu[index].last_cpu_sum = cpu[index].cpu_user + cpu[index].cpu_nice + cpu[index].cpu_system;
433                 for (i = info.cpu_avg_samples; i > 1; i--)
434                         cpu[index].cpu_val[i - 1] = cpu[index].cpu_val[i - 2];
435
436         }
437
438 // test code
439 // this is for getting proc shit
440 // pee pee
441 // poo
442         //
443
444
445
446
447
448
449 }
450
451 void update_running_processes()
452 {
453         update_stat();
454 }
455
456 void update_cpu_usage()
457 {
458         update_stat();
459 }
460
461 void update_load_average()
462 {
463 #ifdef HAVE_GETLOADAVG
464         double v[3];
465         getloadavg(v, 3);
466         info.loadavg[0] = (float) v[0];
467         info.loadavg[1] = (float) v[1];
468         info.loadavg[2] = (float) v[2];
469 #else
470         static int rep;
471         FILE *fp;
472
473         fp = open_file("/proc/loadavg", &rep);
474         if (!fp) {
475                 v[0] = v[1] = v[2] = 0.0;
476                 return;
477         }
478
479         fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
480                &info.loadavg[2]);
481
482         fclose(fp);
483 #endif
484 }
485
486 #define PROC_I8K "/proc/i8k"
487 #define I8K_DELIM " "
488 static char *i8k_procbuf = NULL;
489 void update_i8k()
490 {
491         FILE *fp;
492         if (!i8k_procbuf) {
493                 i8k_procbuf = (char*)malloc(128*sizeof(char));
494         }
495         if ((fp = fopen(PROC_I8K,"r")) == NULL) {
496                 CRIT_ERR("/proc/i8k doesn't exist! use insmod to make sure the kernel driver is loaded...");
497         }
498
499         memset(&i8k_procbuf[0],0,128);
500         if (fread(&i8k_procbuf[0],sizeof(char),128,fp) == 0) {
501                 ERR("something wrong with /proc/i8k...");
502         }
503
504         fclose(fp);
505
506   i8k.version = strtok(&i8k_procbuf[0],I8K_DELIM);
507         i8k.bios = strtok(NULL,I8K_DELIM);
508         i8k.serial = strtok(NULL,I8K_DELIM);
509         i8k.cpu_temp = strtok(NULL,I8K_DELIM);
510         i8k.left_fan_status = strtok(NULL,I8K_DELIM);   
511         i8k.right_fan_status = strtok(NULL,I8K_DELIM);  
512         i8k.left_fan_rpm = strtok(NULL,I8K_DELIM);
513         i8k.right_fan_rpm = strtok(NULL,I8K_DELIM);
514         i8k.ac_status = strtok(NULL,I8K_DELIM);
515         i8k.buttons_status = strtok(NULL,I8K_DELIM);
516 }
517
518
519 /***********************************************************/
520 /***********************************************************/
521 /***********************************************************/
522
523 static int no_dots(const struct dirent *d)
524 {
525         if (d->d_name[0] == '.')
526                 return 0;
527         return 1;
528 }
529
530 static int
531 get_first_file_in_a_directory(const char *dir, char *s, int *rep)
532 {
533         struct dirent **namelist;
534         int i, n;
535
536         n = scandir(dir, &namelist, no_dots, alphasort);
537         if (n < 0) {
538                 if (!rep || !*rep) {
539                         ERR("scandir for %s: %s", dir, strerror(errno));
540                         if (rep)
541                                 *rep = 1;
542                 }
543                 return 0;
544         } else {
545                 if (n == 0)
546                         return 0;
547
548                 strncpy(s, namelist[0]->d_name, 255);
549                 s[255] = '\0';
550
551                 for (i = 0; i < n; i++)
552                         free(namelist[i]);
553                 free(namelist);
554
555                 return 1;
556         }
557 }
558
559 #define I2C_DIR "/sys/bus/i2c/devices/"
560
561 int
562 open_i2c_sensor(const char *dev, const char *type, int n, int *div,
563                 char *devtype)
564 {
565         char path[256];
566         char buf[256];
567         int fd;
568         int divfd;
569
570         /* if i2c device is NULL or *, get first */
571         if (dev == NULL || strcmp(dev, "*") == 0) {
572                 static int rep;
573                 if (!get_first_file_in_a_directory(I2C_DIR, buf, &rep))
574                         return -1;
575                 dev = buf;
576         }
577
578         /* change vol to in */
579         if (strcmp(type, "vol") == 0)
580                 type = "in";
581
582         if (strcmp(type, "tempf") == 0) {
583                 snprintf(path, 255, I2C_DIR "%s/%s%d_input", dev, "temp", n);
584         } else {
585                 snprintf(path, 255, I2C_DIR "%s/%s%d_input", dev, type, n);
586         }
587         strncpy(devtype, path, 255);
588
589         /* open file */
590         fd = open(path, O_RDONLY);
591         if (fd < 0) {
592                 CRIT_ERR("can't open '%s': %s\nplease fix i2c or remove it from Conky", path, strerror(errno));
593         }
594
595         if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
596             || strcmp(type, "tempf") == 0)
597                 *div = 1;
598         else
599                 *div = 0;
600         /* fan does not use *_div as a read divisor */
601         if (strcmp("fan", type) == 0)
602                 return fd;
603
604         /* test if *_div file exist, open it and use it as divisor */
605         if (strcmp(type, "tempf") == 0) {
606                 snprintf(path, 255, I2C_DIR "%s/%s%d_div", "one", "two",
607                          n);
608         } else {
609                 snprintf(path, 255, I2C_DIR "%s/%s%d_div", dev, type, n);
610         }
611
612         divfd = open(path, O_RDONLY);
613         if (divfd > 0) {
614                 /* read integer */
615                 char divbuf[64];
616                 unsigned int divn;
617                 divn = read(divfd, divbuf, 63);
618                 /* should read until n == 0 but I doubt that kernel will give these
619                  * in multiple pieces. :) */
620                 divbuf[divn] = '\0';
621                 *div = atoi(divbuf);
622         }
623
624         close(divfd);
625
626         return fd;
627 }
628
629 double get_i2c_info(int *fd, int div, char *devtype, char *type)
630 {
631         int val = 0;
632
633         if (*fd <= 0)
634                 return 0;
635
636         lseek(*fd, 0, SEEK_SET);
637
638         /* read integer */
639         {
640                 char buf[64];
641                 unsigned int n;
642                 n = read(*fd, buf, 63);
643                 /* should read until n == 0 but I doubt that kernel will give these
644                  * in multiple pieces. :) */
645                 buf[n] = '\0';
646                 val = atoi(buf);
647         }
648
649         close(*fd);
650         /* open file */
651         *fd = open(devtype, O_RDONLY);
652         if (*fd < 0)
653                 ERR("can't open '%s': %s", devtype, strerror(errno));
654
655         /* My dirty hack for computing CPU value 
656          * Filedil, from forums.gentoo.org
657          */
658 /*      if (strstr(devtype, "temp1_input") != NULL)
659         return -15.096+1.4893*(val / 1000.0); */
660
661
662         /* divide voltage and temperature by 1000 */
663         /* or if any other divisor is given, use that */
664         if (strcmp(type, "tempf") == 0) {
665                 if (div > 1)
666                         return ((val / div + 40) * 9.0 / 5) - 40;
667                 else if (div)
668                         return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
669                 else
670                         return ((val + 40) * 9.0 / 5) - 40;
671         } else {
672                 if (div > 1)
673                         return val / div;
674                 else if (div)
675                         return val / 1000.0;
676                 else
677                         return val;
678         }
679 }
680
681 #define ADT746X_FAN "/sys/devices/temperatures/cpu_fan_speed"
682
683 void get_adt746x_fan( char * p_client_buffer, size_t client_buffer_size )
684 {
685         static int rep;
686         char adt746x_fan_state[64];
687         FILE *fp;
688
689         if ( !p_client_buffer || client_buffer_size <= 0 )
690                 return;
691
692         fp = open_file(ADT746X_FAN, &rep);
693         if (!fp) 
694         {
695                 sprintf(adt746x_fan_state, "adt746x not found");
696         }
697         else
698         {
699                 fscanf(fp, "%s", adt746x_fan_state);
700                 fclose(fp);
701         }
702
703         snprintf( p_client_buffer, client_buffer_size, "%s", adt746x_fan_state );
704         return;
705 }
706
707 #define ADT746X_CPU "/sys/devices/temperatures/cpu_temperature"
708
709 void get_adt746x_cpu( char * p_client_buffer, size_t client_buffer_size )
710 {
711         static int rep;
712         char adt746x_cpu_state[64];
713         FILE *fp;
714
715         if ( !p_client_buffer || client_buffer_size <= 0 )
716                 return;
717         
718         fp = open_file(ADT746X_CPU, &rep);
719         if (!fp)
720         {
721                 sprintf(adt746x_cpu_state, "adt746x not found");
722         }
723         else
724         {
725                 fscanf(fp, "%2s", adt746x_cpu_state);
726                 fclose(fp);
727         }
728
729         snprintf( p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state ); 
730         return;
731 }
732
733 /* Thanks to "Walt Nelson" <wnelsonjr@comcast.net> */
734
735 /***********************************************************************/
736 /*
737  *  This file is part of x86info.
738  *  (C) 2001 Dave Jones.
739  *
740  *  Licensed under the terms of the GNU GPL License version 2.
741  *
742  * Estimate CPU MHz routine by Andrea Arcangeli <andrea@suse.de>
743  * Small changes by David Sterba <sterd9am@ss1000.ms.mff.cuni.cz>
744  *
745  */
746 #if  defined(__i386) || defined(__x86_64)
747 __inline__ unsigned long long int rdtsc()
748 {
749         unsigned long long int x;
750         __asm__ volatile (".byte 0x0f, 0x31":"=A" (x));
751         return x;
752 }
753 #endif
754
755 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
756 void get_freq_dynamic( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor )
757 {
758 #if  defined(__i386) || defined(__x86_64)
759         struct timezone tz;
760         struct timeval tvstart, tvstop;
761         unsigned long long cycles[2];   /* gotta be 64 bit */
762         unsigned int microseconds;      /* total time taken */
763
764         if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 )
765              return;
766
767         memset(&tz, 0, sizeof(tz));
768
769         /* get this function in cached memory */
770         gettimeofday(&tvstart, &tz);
771         cycles[0] = rdtsc();
772         gettimeofday(&tvstart, &tz);
773
774         /* we don't trust that this is any specific length of time */
775         usleep(100);
776         cycles[1] = rdtsc();
777         gettimeofday(&tvstop, &tz);
778         microseconds = ((tvstop.tv_sec - tvstart.tv_sec) * 1000000) +
779             (tvstop.tv_usec - tvstart.tv_usec);
780
781         snprintf( p_client_buffer, client_buffer_size, p_format, (float)((cycles[1] - cycles[0]) / microseconds) / divisor );
782         return;
783 #else
784         get_freq( p_client_buffer, client_buffer_size, p_format, divisor );
785         return;
786 #endif
787 }
788
789 #define CPUFREQ_CURRENT "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq"
790
791 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
792 void get_freq( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor )
793 {
794         FILE *f;
795         char frequency[32];
796         char s[256];
797         double freq = 0;
798
799         if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 )
800                 return;
801
802         f = fopen(CPUFREQ_CURRENT, "r");
803         if (f) {
804                 /* if there's a cpufreq /sys node, read the current frequency from this node;
805                  * divide by 1000 to get Mhz. */
806                 if (fgets(s, sizeof(s), f)) {
807                         s[strlen(s)-1] = '\0';
808                         freq = strtod(s, NULL);
809                 }
810                 fclose(f);
811                 snprintf( p_client_buffer, client_buffer_size, p_format, (freq/1000)/divisor );
812                 return;
813         }
814         
815         f = fopen("/proc/cpuinfo", "r");                //open the CPU information file
816         if (!f)
817             return;
818
819         while (fgets(s, sizeof(s), f) != NULL){         //read the file
820 #if defined(__i386) || defined(__x86_64)
821                 if (strncmp(s, "cpu MHz", 7) == 0) {    //and search for the cpu mhz
822 #else
823                 if (strncmp(s, "clock", 5) == 0) {      // this is different on ppc for some reason
824 #endif
825                 strcpy(frequency, strchr(s, ':') + 2);  //copy just the number
826                 frequency[strlen(frequency) - 1] = '\0'; // strip \n
827                 freq = strtod(frequency, NULL);
828                 break;
829                 }
830         }
831         
832         fclose(f);
833         snprintf( p_client_buffer, client_buffer_size, p_format, (float)freq/divisor );
834         return;
835 }
836
837
838 #define ACPI_FAN_DIR "/proc/acpi/fan/"
839
840 void get_acpi_fan( char * p_client_buffer, size_t client_buffer_size )
841 {
842         static int rep;
843         char buf[256];
844         char buf2[256];
845         FILE *fp;
846
847         if ( !p_client_buffer || client_buffer_size <= 0 )
848                 return;
849
850         /* yeah, slow... :/ */
851         if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep))
852         {
853                 snprintf( p_client_buffer, client_buffer_size, "no fans?" );
854                 return;
855         }
856
857         snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf );
858
859         fp = open_file(buf2, &rep);
860         if (!fp) {
861                 snprintf( p_client_buffer, client_buffer_size, "can't open fan's state file" );
862                 return;
863         }
864         memset(buf,0,sizeof(buf));
865         fscanf(fp, "%*s %99s", buf);
866         fclose(fp);
867
868         snprintf( p_client_buffer, client_buffer_size, "%s", buf );
869
870         return;
871 }
872
873 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
874
875 void get_acpi_ac_adapter( char * p_client_buffer, size_t client_buffer_size )
876 {
877         static int rep;
878         char buf[256];
879         char buf2[256];
880         FILE *fp;
881
882         if ( !p_client_buffer || client_buffer_size <= 0 )
883                 return;
884
885         /* yeah, slow... :/ */
886         if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep))
887         {
888                 snprintf( p_client_buffer, client_buffer_size, "no ac_adapters?" );
889                 return; 
890         }
891
892         snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf );
893          
894
895         fp = open_file(buf2, &rep);
896         if (!fp) {
897                 snprintf( p_client_buffer, client_buffer_size, "No ac adapter found.... where is it?" );
898                 return;
899         }
900         memset(buf,0,sizeof(buf));
901         fscanf(fp, "%*s %99s", buf );
902         fclose(fp);
903
904         snprintf( p_client_buffer, client_buffer_size, "%s", buf );
905
906         return;
907 }
908
909 /*
910 /proc/acpi/thermal_zone/THRM/cooling_mode
911 cooling mode:            active
912 /proc/acpi/thermal_zone/THRM/polling_frequency
913 <polling disabled>
914 /proc/acpi/thermal_zone/THRM/state
915 state:                   ok
916 /proc/acpi/thermal_zone/THRM/temperature
917 temperature:             45 C
918 /proc/acpi/thermal_zone/THRM/trip_points
919 critical (S5):           73 C
920 passive:                 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
921 */
922
923 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
924 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
925
926 int open_acpi_temperature(const char *name)
927 {
928         char path[256];
929         char buf[256];
930         int fd;
931
932         if (name == NULL || strcmp(name, "*") == 0) {
933                 static int rep;
934                 if (!get_first_file_in_a_directory
935                     (ACPI_THERMAL_DIR, buf, &rep))
936                         return -1;
937                 name = buf;
938         }
939
940         snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
941
942         fd = open(path, O_RDONLY);
943         if (fd < 0)
944                 ERR("can't open '%s': %s", path, strerror(errno));
945
946         return fd;
947 }
948
949 static double last_acpi_temp;
950 static double last_acpi_temp_time;
951
952 double get_acpi_temperature(int fd)
953 {
954         if (fd <= 0)
955                 return 0;
956
957         /* don't update acpi temperature too often */
958         if (current_update_time - last_acpi_temp_time < 11.32) {
959                 return last_acpi_temp;
960         }
961         last_acpi_temp_time = current_update_time;
962
963         /* seek to beginning */
964         lseek(fd, 0, SEEK_SET);
965
966         /* read */
967         {
968                 char buf[256];
969                 int n;
970                 n = read(fd, buf, 255);
971                 if (n < 0)
972                         ERR("can't read fd %d: %s", fd, strerror(errno));
973                 else {
974                         buf[n] = '\0';
975                         sscanf(buf, "temperature: %lf", &last_acpi_temp);
976                 }
977         }
978
979         return last_acpi_temp;
980 }
981
982 /*
983 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info 
984 present:                 yes
985 design capacity:         4400 mAh
986 last full capacity:      4064 mAh
987 battery technology:      rechargeable
988 design voltage:          14800 mV
989 design capacity warning: 300 mAh
990 design capacity low:     200 mAh
991 capacity granularity 1:  32 mAh
992 capacity granularity 2:  32 mAh
993 model number:            02KT
994 serial number:           16922
995 battery type:            LION
996 OEM info:                SANYO
997 */
998
999 /*
1000 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1001 present:                 yes
1002 capacity state:          ok
1003 charging state:          unknown
1004 present rate:            0 mA
1005 remaining capacity:      4064 mAh
1006 present voltage:         16608 mV
1007 */
1008
1009 /*
1010 2213<@jupet kellari ö> jupet@lagi-unstable:~$ cat /proc/apm 
1011 2213<@jupet kellari ö> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1012 2213<@jupet kellari ö> (-1 ollee ei akkua kiinni, koska akku on pöydällä)
1013 2214<@jupet kellari ö> jupet@lagi-unstable:~$ cat /proc/apm 
1014 2214<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1015
1016 2238<@jupet kellari ö> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1017 2239<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1018
1019 2240<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori päällä
1020 2241<@jupet kellari ö> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori päällä mutta ilman verkkovirtaa
1021 */
1022
1023 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1024 #define APM_PATH "/proc/apm"
1025
1026 static FILE *acpi_bat_fp;
1027 static FILE *apm_bat_fp;
1028
1029 static int acpi_last_full;
1030
1031 static char last_battery_str[64];
1032
1033 static double last_battery_time;
1034
1035 void get_battery_stuff(char *buf, unsigned int n, const char *bat)
1036 {
1037         static int rep, rep2;
1038         char acpi_path[128];
1039         snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1040
1041         /* don't update battery too often */
1042         if (current_update_time - last_battery_time < 29.5) {
1043                 snprintf(buf, n, "%s", last_battery_str);
1044                 return;
1045         }
1046         last_battery_time = current_update_time;
1047
1048         /* first try ACPI */
1049
1050         if (acpi_bat_fp == NULL && apm_bat_fp == NULL)
1051                 acpi_bat_fp = open_file(acpi_path, &rep);
1052
1053         if (acpi_bat_fp != NULL) {
1054                 int present_rate = -1;
1055                 int remaining_capacity = -1;
1056                 char charging_state[64];
1057
1058                 /* read last full capacity if it's zero */
1059                 if (acpi_last_full == 0) {
1060                         static int rep;
1061                         char path[128];
1062                         FILE *fp;
1063                         snprintf(path, 127,
1064                                  ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1065                         fp = open_file(path, &rep);
1066                         if (fp != NULL) {
1067                                 while (!feof(fp)) {
1068                                         char b[256];
1069                                         if (fgets(b, 256, fp) == NULL)
1070                                                 break;
1071
1072                                         if (sscanf
1073                                             (b, "last full capacity: %d",
1074                                              &acpi_last_full) != 0)
1075                                                 break;
1076                                 }
1077
1078                                 fclose(fp);
1079                         }
1080                 }
1081
1082                 fseek(acpi_bat_fp, 0, SEEK_SET);
1083
1084                 strcpy(charging_state, "unknown");
1085
1086                 while (!feof(acpi_bat_fp)) {
1087                         char buf[256];
1088                         if (fgets(buf, 256, acpi_bat_fp) == NULL)
1089                                 break;
1090
1091                         /* let's just hope units are ok */
1092                         if (buf[0] == 'c')
1093                                 sscanf(buf, "charging state: %63s",
1094                                        charging_state);
1095                         else if (buf[0] == 'p')
1096                                 sscanf(buf, "present rate: %d",
1097                                        &present_rate);
1098                         else if (buf[0] == 'r')
1099                                 sscanf(buf, "remaining capacity: %d",
1100                                        &remaining_capacity);
1101                 }
1102
1103                 /* charging */
1104                 if (strcmp(charging_state, "charging") == 0) {
1105                         if (acpi_last_full != 0 && present_rate > 0) {
1106                                 strcpy(last_battery_str, "charging ");
1107                                 format_seconds(last_battery_str + 9,
1108                                                63 - 9,
1109                                                (acpi_last_full -
1110                                                 remaining_capacity) * 60 *
1111                                                60 / present_rate);
1112                         } else if (acpi_last_full != 0
1113                                    && present_rate <= 0) {
1114                                 sprintf(last_battery_str, "charging %d%%",
1115                                         remaining_capacity * 100 /
1116                                         acpi_last_full);
1117                         } else {
1118                                 strcpy(last_battery_str, "charging");
1119                         }
1120                 }
1121                 /* discharging */
1122                 else if (strcmp(charging_state, "discharging") == 0) {
1123                         if (present_rate > 0)
1124                                 format_seconds(last_battery_str, 63,
1125                                                (remaining_capacity * 60 *
1126                                                 60) / present_rate);
1127                         else
1128                                 sprintf(last_battery_str,
1129                                         "discharging %d%%",
1130                                         remaining_capacity * 100 /
1131                                         acpi_last_full);
1132                 }
1133                 /* charged */
1134                 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1135                 else if (strcmp(charging_state, "charged") == 0) {
1136                         if (acpi_last_full != 0
1137                             && remaining_capacity != acpi_last_full)
1138                                 sprintf(last_battery_str, "charged %d%%",
1139                                         remaining_capacity * 100 /
1140                                         acpi_last_full);
1141                         else
1142                                 strcpy(last_battery_str, "charged");
1143                 }
1144                 /* unknown, probably full / AC */
1145                 else {
1146                         if (acpi_last_full != 0
1147                             && remaining_capacity != acpi_last_full)
1148                                 sprintf(last_battery_str, "unknown %d%%",
1149                                         remaining_capacity * 100 /
1150                                         acpi_last_full);
1151                         else
1152                                 strcpy(last_battery_str, "AC");
1153                 }
1154         } else {
1155                 /* APM */
1156                 if (apm_bat_fp == NULL)
1157                         apm_bat_fp = open_file(APM_PATH, &rep2);
1158
1159                 if (apm_bat_fp != NULL) {
1160                         int ac, status, flag, life;
1161
1162                         fscanf(apm_bat_fp,
1163                                "%*s %*s %*x %x   %x       %x     %d%%",
1164                                &ac, &status, &flag, &life);
1165
1166                         if (life == -1) {
1167                                 /* could check now that there is ac */
1168                                 snprintf(last_battery_str, 64, "AC");
1169                         } else if (ac && life != 100) { /* could check that status==3 here? */
1170                                 snprintf(last_battery_str, 64,
1171                                          "charging %d%%", life);
1172                         } else {
1173                                 snprintf(last_battery_str, 64, "%d%%",
1174                                          life);
1175                         }
1176
1177                         /* it seemed to buffer it so file must be closed (or could use syscalls
1178                          * directly but I don't feel like coding it now) */
1179                         fclose(apm_bat_fp);
1180                         apm_bat_fp = NULL;
1181                 }
1182         }
1183
1184         snprintf(buf, n, "%s", last_battery_str);
1185 }
1186
1187 void update_top()
1188 {
1189         show_nice_processes = 1;
1190         process_find_top(info.cpu, info.memu);
1191         info.first_process = get_first_process();
1192 }
1193
1194
1195 /*
1196  *  The following ifdefs were adapted from gkrellm
1197  */
1198 #include <linux/major.h>
1199
1200 #if ! defined (MD_MAJOR)
1201 #define MD_MAJOR 9
1202 #endif
1203
1204 #if !defined(LVM_BLK_MAJOR)
1205 #define LVM_BLK_MAJOR 58
1206 #endif
1207
1208 #if !defined(NBD_MAJOR)
1209 #define NBD_MAJOR 43
1210 #endif
1211
1212 void update_diskio()
1213 {
1214         static unsigned int last = UINT_MAX;
1215         static FILE* fp;
1216
1217         char buf[512];
1218         int major, minor;
1219         unsigned int current = 0;
1220         unsigned int reads, writes = 0;
1221         int col_count = 0;
1222
1223         if (!fp) {
1224                 fp = fopen("/proc/diskstats", "r");
1225         } else {
1226                 fseek(fp, 0, SEEK_SET);
1227         }
1228
1229         /* read reads and writes from all disks (minor = 0), including
1230          * cd-roms and floppies, and summ them up
1231          */
1232         current = 0;
1233         while (!feof(fp)) {
1234                 fgets(buf, 512, fp);
1235                 col_count = sscanf(buf, "%u %u %*s %*u %*u %u %*u %*u %*u %u",
1236                                    &major, &minor, &reads, &writes);
1237                 /* ignore subdevices (they have only 3 matching entries in their line)
1238                  * and virtual devices (LVM, network block devices, RAM disks, Loopback)
1239                  *
1240                  * XXX ignore devices which are part of a SW RAID (MD_MAJOR)
1241                  */
1242                 if (col_count > 3 &&
1243                     major != LVM_BLK_MAJOR && major != NBD_MAJOR &&
1244                     major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
1245                         current += reads + writes;
1246                 }
1247         }
1248
1249         /* since the values in /proc/diststats are absolute, we have
1250          * to substract our last reading. The numbers stand for
1251          * "sectors read", and we therefore have to divide by two to
1252          * get KB */
1253         int tot = ((double)(current-last)/2);
1254         if (last > current) {
1255                 /* we hit this either if it's the very first time we
1256                  * run this, or when /proc/diskstats overflows; while
1257                  * 0 is not correct, it's at least not way off */
1258                 tot = 0;
1259         }
1260         last = current;
1261
1262         diskio_value = tot;
1263 }
1264