7920ca2140ce8d32c5458bda207718c2f742a0fd
[monky] / linux.c
1 #include "conky.h"
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <dirent.h>
6 #include <ctype.h>
7 #include <errno.h>
8 #include <limits.h>
9 #include <sys/types.h>
10 #include <sys/sysinfo.h>
11 #include <sys/stat.h>
12 #include <fcntl.h>
13 #include <unistd.h>
14 // #include <assert.h>
15 #include <time.h>
16 #include <proc/procps.h>
17 #include <proc/readproc.h>
18 #include "top.h"
19
20 #include <sys/ioctl.h>
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 #include <linux/sockios.h>
24 #include <net/if.h>
25 #include <math.h>
26
27
28 static struct sysinfo s_info;
29
30 #define TEXT_BUFFER_SIZE (1024*4)
31
32 static int show_nice_processes;
33
34 void prepare_update()
35 {
36 }
37
38 static void update_sysinfo()
39 {
40         sysinfo(&s_info);
41
42         info.uptime = (double) s_info.uptime;
43
44         /* there was some problem with these */
45 #if 0
46 //      info.loadavg[0] = s_info.loads[0] / 100000.0f;
47         info.loadavg[1] = s_info.loads[1] / 100000.0f;
48         info.loadavg[2] = s_info.loads[2] / 100000.0f;
49         gkrelltop_process_find_top_three info.mask |= 1 << INFO_LOADAVG;
50 #endif
51
52         info.procs = s_info.procs;
53
54         /* these aren't nice, no cache and should check kernel version for mem_unit */
55 #if 0
56         info.memmax = s_info.totalram;
57         info.mem = s_info.totalram - s_info.freeram;
58         info.swapmax = s_info.totalswap;
59         info.swap = s_info.totalswap - s_info.swap;
60         info.mask |= 1 << INFO_MEM;
61 #endif
62
63         info.mask |= (1 << INFO_UPTIME) | (1 << INFO_PROCS);
64 }
65
66 void update_uptime()
67 {
68         /* prefers sysinfo() for uptime, I don't really know which one is better
69         * (=faster?) */
70 #ifdef USE_PROC_UPTIME
71         static int rep;
72         FILE *fp = open_file("/proc/uptime", &rep);
73         if (!fp)
74                 return 0;
75         fscanf(fp, "%lf", &info.uptime);
76         fclose(fp);
77
78         info.mask |= (1 << INFO_UPTIME);
79 #else
80         update_sysinfo();
81 #endif
82 }
83
84 /* these things are also in sysinfo except Buffers:, that's why I'm reading
85 * them from proc */
86
87 static FILE *meminfo_fp;
88
89 void update_meminfo()
90 {
91         static int rep;
92         /*  unsigned int a; */
93         char buf[256];
94
95         info.mem = info.memmax = info.swap = info.swapmax = info.bufmem =
96                         info.buffers = info.cached = 0;
97
98         if (meminfo_fp == NULL)
99                 meminfo_fp = open_file("/proc/meminfo", &rep);
100         else
101                 fseek(meminfo_fp, 0, SEEK_SET);
102         if (meminfo_fp == NULL)
103                 return;
104
105         while (!feof(meminfo_fp)) {
106                 if (fgets(buf, 255, meminfo_fp) == NULL)
107                         break;
108
109                 if (strncmp(buf, "MemTotal:", 9) == 0) {
110                         sscanf(buf, "%*s %u", &info.memmax);
111                 } else if (strncmp(buf, "MemFree:", 8) == 0) {
112                         sscanf(buf, "%*s %u", &info.mem);
113                 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
114                         sscanf(buf, "%*s %u", &info.swapmax);
115                 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
116                         sscanf(buf, "%*s %u", &info.swap);
117                 } else if (strncmp(buf, "Buffers:", 8) == 0) {
118                         sscanf(buf, "%*s %u", &info.buffers);
119                 } else if (strncmp(buf, "Cached:", 7) == 0) {
120                         sscanf(buf, "%*s %u", &info.cached);
121                 }
122         }
123
124         info.mem = info.memmax - info.mem;
125         info.swap = info.swapmax - info.swap;
126
127         info.bufmem = info.cached + info.buffers;
128
129         info.mask |= (1 << INFO_MEM) | (1 << INFO_BUFFERS);
130 }
131
132 static FILE *net_dev_fp;
133 static FILE *net_wireless_fp;
134
135 inline void update_net_stats()
136 {
137         static int rep;
138         // FIXME: arbitrary size chosen to keep code simple.
139         int i, i2;
140         unsigned int curtmp1, curtmp2;
141         unsigned int k;
142         struct ifconf conf;
143
144
145         char buf[256];
146         double delta;
147
148         /* get delta */
149         delta = current_update_time - last_update_time;
150         if (delta <= 0.0001)
151                 return;
152
153         /* open file and ignore first two lines */
154         if (net_dev_fp == NULL)
155                 net_dev_fp = open_file("/proc/net/dev", &rep);
156         else
157                 fseek(net_dev_fp, 0, SEEK_SET);
158         if (!net_dev_fp)
159                 return;
160
161         fgets(buf, 255, net_dev_fp);    /* garbage */
162         fgets(buf, 255, net_dev_fp);    /* garbage (field names) */
163
164         /* read each interface */
165         for (i2 = 0; i2 < 16; i2++) {
166                 struct net_stat *ns;
167                 char *s, *p;
168                 long long r, t, last_recv, last_trans;
169
170                 if (fgets(buf, 255, net_dev_fp) == NULL)
171                         break;
172                 p = buf;
173                 while (isspace((int) *p))
174                         p++;
175
176                 s = p;
177
178                 while (*p && *p != ':')
179                         p++;
180                 if (*p == '\0')
181                         continue;
182                 *p = '\0';
183                 p++;
184
185                 ns = get_net_stat(s);
186                 ns->up = 1;
187                 last_recv = ns->recv;
188                 last_trans = ns->trans;
189
190                 sscanf(p,
191                        /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
192                        "%Ld  %*d     %*d  %*d  %*d  %*d   %*d        %*d       %Ld",
193                        &r, &t);
194
195                 /* if recv or trans is less than last time, an overflow happened */
196
197                 if (r < ns->last_read_recv)
198                         ns->recv +=
199                                         ((long long) 4294967295U -
200                                         ns->last_read_recv) + r;
201                 else
202                         ns->recv += (r - ns->last_read_recv);
203                 ns->last_read_recv = r;
204
205                 if (t < ns->last_read_trans)
206                         ns->trans +=
207                                         ((long long) 4294967295U -
208                                         ns->last_read_trans) + t;
209                 else
210                         ns->trans += (t - ns->last_read_trans);
211                 ns->last_read_trans = t;
212
213                 /*** ip addr patch ***/
214
215                 s = (char*)socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
216                 
217                 conf.ifc_buf = malloc(sizeof(struct ifreq) * 16);
218                 
219                 conf.ifc_len = sizeof(struct ifreq) * 16;
220
221                 ioctl((long)s, SIOCGIFCONF, &conf);
222
223                 for (k=0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
224                         struct net_stat *ns;    
225                         ns = get_net_stat(((struct ifreq*)conf.ifc_buf)[k].ifr_ifrn.ifrn_name);
226                         ns->addr = ((struct ifreq*)conf.ifc_buf)[k].ifr_ifru.ifru_addr;
227                 }
228
229                 close((long)s);
230                 
231                 free(conf.ifc_buf);
232
233
234                 /*** end ip addr patch ***/
235
236
237                 /* calculate speeds */
238                 ns->net_rec[0] = (ns->recv - last_recv) / delta;
239                 ns->net_trans[0] = (ns->trans - last_trans) / delta;
240                 curtmp1 = 0;
241                 curtmp2 = 0;
242                 // get an average
243                 for (i = 0; (unsigned) i < info.net_avg_samples; i++) {
244                         curtmp1 += ns->net_rec[i];
245                         curtmp2 += ns->net_trans[i];
246                 }
247                 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
248                 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
249                 if (info.net_avg_samples > 1) {
250                         for (i = info.net_avg_samples; i > 1; i--) {
251                                 ns->net_rec[i - 1] = ns->net_rec[i - 2];
252                                 ns->net_trans[i - 1] =
253                                                 ns->net_trans[i - 2];
254                         }
255                 }
256
257
258
259         }
260
261         /* fclose(net_dev_fp); net_dev_fp = NULL; */
262 }
263
264 inline void update_wifi_stats() {
265         /** wireless stats patch by Bobby Beckmann **/
266         static int rep;
267         int i;
268         char buf[256];
269         /*open file and ignore first two lines       sorry, this code sucks ass right now, i'll clean it up later*/
270         if (net_wireless_fp == NULL)
271                 net_wireless_fp = open_file("/proc/net/wireless", &rep);
272         else
273                 fseek(net_wireless_fp, 0, SEEK_SET);
274         if (!net_wireless_fp) return;
275
276         fgets(buf, 255, net_wireless_fp); /* garbage */
277         fgets(buf, 255, net_wireless_fp); /* garbage (field names) */
278
279         /* read each interface */
280         for (i=0; i<16; i++) {
281                 struct net_stat *ns;
282                 char *s, *p;
283                 int l, m, n;
284
285                 if (fgets(buf, 255, net_wireless_fp) == NULL) break;
286                 p = buf;
287                 while (isspace((int) *p)) p++;
288
289                 s = p;
290
291                 while (*p && *p != ':') p++;
292                 if (*p == '\0') continue;
293                 *p = '\0';
294                 p++;
295
296                 ns = get_net_stat(s);
297
298                 sscanf(p,
299                        "%*d   %d.  %d.  %d",
300                        &l,  &m,  &n);
301
302                 ns->linkstatus = (int)(log(l) / log(92) * 100);
303         }
304
305         /*** end wireless patch ***/
306 }
307
308 int result;
309
310 void update_total_processes()
311 {
312         update_sysinfo();
313 }
314
315 static unsigned int cpu_user, cpu_system, cpu_nice;
316 static double last_cpu_sum;
317 static int clock_ticks;
318
319 static FILE *stat_fp;
320
321 inline static void update_stat()
322 {
323         // FIXME: arbitrary size?
324         static double cpu_val[15];
325         static int rep;
326         char buf[256];
327         unsigned int i;
328         double curtmp;
329
330         if (stat_fp == NULL)
331                 stat_fp = open_file("/proc/stat", &rep);
332         else
333                 fseek(stat_fp, 0, SEEK_SET);
334         if (stat_fp == NULL)
335                 return;
336
337         info.cpu_count = 0;
338
339         while (!feof(stat_fp)) {
340                 if (fgets(buf, 255, stat_fp) == NULL)
341                         break;
342
343                 if (strncmp(buf, "procs_running ", 14) == 0) {
344                         sscanf(buf, "%*s %d", &info.run_procs);
345                         info.mask |= (1 << INFO_RUN_PROCS);
346                 } else if (strncmp(buf, "cpu ", 4) == 0) {
347                         sscanf(buf, "%*s %u %u %u", &cpu_user, &cpu_nice,
348                                &cpu_system);
349                         info.mask |= (1 << INFO_CPU);
350                 } else if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
351                         info.cpu_count++;
352                 }
353         }
354
355         {
356                 double delta;
357                 delta = current_update_time - last_update_time;
358                 if (delta <= 0.001)
359                         return;
360
361                 if (clock_ticks == 0)
362                         clock_ticks = sysconf(_SC_CLK_TCK);
363                 curtmp = 0;
364                 cpu_val[0] =
365                                 (cpu_user + cpu_nice + cpu_system -
366                                 last_cpu_sum) / delta / (double) clock_ticks /
367                                 info.cpu_count;
368                 for (i = 0; i < info.cpu_avg_samples; i++)
369                         curtmp += cpu_val[i];
370                 info.cpu_usage = curtmp / info.cpu_avg_samples;
371                 last_cpu_sum = cpu_user + cpu_nice + cpu_system;
372                 for (i = info.cpu_avg_samples; i > 1; i--)
373                         cpu_val[i - 1] = cpu_val[i - 2];
374
375         }
376
377 // test code
378 // this is for getting proc shit
379 // pee pee
380 // poo
381         //
382
383
384
385
386
387
388 }
389
390 void update_running_processes()
391 {
392         update_stat();
393 }
394
395 void update_cpu_usage()
396 {
397         update_stat();
398 }
399
400 void update_load_average()
401 {
402 #ifdef HAVE_GETLOADAVG
403         double v[3];
404         getloadavg(v, 3);
405         info.loadavg[0] = (float) v[0];
406         info.loadavg[1] = (float) v[1];
407         info.loadavg[2] = (float) v[2];
408 #else
409         static int rep;
410         FILE *fp;
411
412         fp = open_file("/proc/loadavg", &rep);
413         if (!fp) {
414                 v[0] = v[1] = v[2] = 0.0;
415                 return;
416         }
417
418         fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
419                &info.loadavg[2]);
420
421         fclose(fp);
422 #endif
423 }
424
425
426 static int no_dots(const struct dirent *d)
427 {
428         if (d->d_name[0] == '.')
429                 return 0;
430         return 1;
431 }
432
433 static int
434                 get_first_file_in_a_directory(const char *dir, char *s, int *rep)
435 {
436         struct dirent **namelist;
437         int i, n;
438
439         n = scandir(dir, &namelist, no_dots, alphasort);
440         if (n < 0) {
441                 if (!rep || !*rep) {
442                         ERR("scandir for %s: %s", dir, strerror(errno));
443                         if (rep)
444                                 *rep = 1;
445                 }
446                 return 0;
447         } else {
448                 if (n == 0)
449                         return 0;
450
451                 strncpy(s, namelist[0]->d_name, 255);
452                 s[255] = '\0';
453
454                 for (i = 0; i < n; i++)
455                         free(namelist[i]);
456                 free(namelist);
457
458                 return 1;
459         }
460 }
461
462 #define I2C_DIR "/sys/bus/i2c/devices/"
463
464 int
465 open_i2c_sensor(const char *dev, const char *type, int n, int *div,
466                 char *devtype)
467 {
468         char path[256];
469         char buf[64];
470         int fd;
471         int divfd;
472
473         /* if i2c device is NULL or *, get first */
474         if (dev == NULL || strcmp(dev, "*") == 0) {
475                 static int rep;
476                 if (!get_first_file_in_a_directory(I2C_DIR, buf, &rep))
477                         return -1;
478                 dev = buf;
479         }
480
481         /* change vol to in */
482         if (strcmp(type, "vol") == 0)
483                 type = "in";
484
485         if (strcmp(type, "tempf") == 0) {
486                 snprintf(path, 255, I2C_DIR "%s/%s%d_input", dev, "temp", n);
487         }
488         else {
489                 snprintf(path, 255, I2C_DIR "%s/%s%d_input", dev, type, n);
490         }
491         strcpy(devtype, path);
492
493         /* open file */
494         fd = open(path, O_RDONLY);
495         if (fd < 0)
496                 ERR("can't open '%s': %s", path, strerror(errno));
497
498         if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0 || strcmp(type, "tempf") == 0)
499                 *div = 1;
500         else
501                 *div = 0;
502         /* fan does not use *_div as a read divisor */
503         if (strcmp("fan", type) == 0)
504                 return fd;
505
506         /* test if *_div file exist, open it and use it as divisor */
507         if (strcmp(type, "tempf") == 0) {
508                 snprintf(path, 255, I2C_DIR "%s/%s%d_div", "one", "two", n);
509         }
510         else {
511                 snprintf(path, 255, I2C_DIR "%s/%s%d_div", dev, type, n);
512         }
513
514         divfd = open(path, O_RDONLY);
515         if (divfd > 0) {
516                 /* read integer */
517                 char divbuf[64];
518                 unsigned int divn;
519                 divn = read(divfd, divbuf, 63);
520                 /* should read until n == 0 but I doubt that kernel will give these
521                 * in multiple pieces. :) */
522                 divbuf[divn] = '\0';
523                 *div = atoi(divbuf);
524         }
525
526         close(divfd);
527
528         return fd;
529 }
530
531 double get_i2c_info(int *fd, int div, char *devtype, char *type)
532 {
533         int val = 0;
534
535         if (*fd <= 0)
536                 return 0;
537
538         lseek(*fd, 0, SEEK_SET);
539
540         /* read integer */
541         {
542                 char buf[64];
543                 unsigned int n;
544                 n = read(*fd, buf, 63);
545                 /* should read until n == 0 but I doubt that kernel will give these
546                 * in multiple pieces. :) */
547                 buf[n] = '\0';
548                 val = atoi(buf);
549         }
550
551         close(*fd);
552         /* open file */
553         *fd = open(devtype, O_RDONLY);
554         if (*fd < 0)
555                 ERR("can't open '%s': %s", devtype, strerror(errno));  
556         
557         /* My dirty hack for computing CPU value 
558         * Filedil, from forums.gentoo.org
559         */
560 /*      if (strstr(devtype, "temp1_input") != NULL)
561         return -15.096+1.4893*(val / 1000.0); */
562         
563         
564         /* divide voltage and temperature by 1000 */
565         /* or if any other divisor is given, use that */
566         if (strcmp(type, "tempf") == 0) {
567                 if (div > 1)
568                         return ((val / div + 40)*9.0/5)-40;
569                 else if (div)
570                         return ((val / 1000.0 + 40)*9.0/5)-40;
571                 else
572                         return ((val + 40)*9.0/5)-40;
573         }
574         else {
575                 if (div > 1)
576                         return val / div;
577                 else if (div)
578                         return val / 1000.0;
579                 else
580                         return val;
581         }
582 }
583
584 #define ADT746X_FAN "/sys/devices/temperatures/cpu_fan_speed"
585
586 static char *adt746x_fan_state;
587
588 char *get_adt746x_fan()
589 {
590         static int rep;
591         FILE *fp;
592
593         if (adt746x_fan_state == NULL) {
594                 adt746x_fan_state = (char *) malloc(100);
595                 assert(adt746x_fan_state != NULL);
596         }
597
598         fp = open_file(ADT746X_FAN, &rep);
599         if (!fp) {
600                 strcpy(adt746x_fan_state,
601                        "No fan found! Hey, you don't have one?");
602                 return adt746x_fan_state;
603         }
604         fscanf(fp, "%s", adt746x_fan_state);
605         fclose(fp);
606
607         return adt746x_fan_state;
608 }
609
610 #define ADT746X_CPU "/sys/devices/temperatures/cpu_temperature"
611
612 static char *adt746x_cpu_state;
613
614 char *get_adt746x_cpu()
615 {
616         static int rep;
617         FILE *fp;
618
619         if (adt746x_cpu_state == NULL) {
620                 adt746x_cpu_state = (char *) malloc(100);
621                 assert(adt746x_cpu_state != NULL);
622         }
623
624         fp = open_file(ADT746X_CPU, &rep);
625         fscanf(fp, "%2s", adt746x_cpu_state);
626         fclose(fp);
627
628         return adt746x_cpu_state;
629 }
630
631 /* Thanks to "Walt Nelson" <wnelsonjr@comcast.net> */
632
633 /***********************************************************************/
634 /*
635  *  $Id$
636  *  This file is part of x86info.
637  *  (C) 2001 Dave Jones.
638  *
639  *  Licensed under the terms of the GNU GPL License version 2.
640  *
641  * Estimate CPU MHz routine by Andrea Arcangeli <andrea@suse.de>
642  * Small changes by David Sterba <sterd9am@ss1000.ms.mff.cuni.cz>
643  *
644  */
645
646 __inline__ unsigned long long int rdtsc()
647 {
648         unsigned long long int x;
649         __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
650         return x;
651 }
652
653 static char *buffer = NULL;
654
655 char *get_freq()
656 {
657         if (buffer == NULL)
658                 buffer = malloc(64);
659         struct timezone tz;
660         struct timeval tvstart, tvstop;
661         unsigned long long cycles[2]; /* gotta be 64 bit */
662         unsigned int microseconds; /* total time taken */
663         
664         memset(&tz, 0, sizeof(tz));
665
666         /* get this function in cached memory */
667         gettimeofday(&tvstart, &tz);
668         cycles[0] = rdtsc();
669         gettimeofday(&tvstart, &tz);
670
671         /* we don't trust that this is any specific length of time */
672         usleep(100);
673         
674         cycles[1] = rdtsc();
675         gettimeofday(&tvstop, &tz);
676         microseconds = ((tvstop.tv_sec-tvstart.tv_sec)*1000000) +
677                         (tvstop.tv_usec-tvstart.tv_usec);
678
679         sprintf(buffer, "%lldMHz",
680                 (cycles[1]-cycles[0])/microseconds);
681
682         return buffer;
683 }
684
685
686 #define ACPI_FAN_DIR "/proc/acpi/fan/"
687
688 static char *acpi_fan_state;
689
690 char *get_acpi_fan()
691 {
692         static int rep;
693         char buf[256];
694         char buf2[256];
695         FILE *fp;
696
697         if (acpi_fan_state == NULL) {
698                 acpi_fan_state = (char *) malloc(100);
699                 assert(acpi_fan_state != NULL);
700         }
701
702         /* yeah, slow... :/ */
703         if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep))
704                 return "no fans?";
705
706         snprintf(buf2, 256, "%s%s/state", ACPI_FAN_DIR, buf);
707
708         fp = open_file(buf2, &rep);
709         if (!fp) {
710                 strcpy(acpi_fan_state, "can't open fan's state file");
711                 return acpi_fan_state;
712         }
713         fscanf(fp, "%*s %99s", acpi_fan_state);
714
715         return acpi_fan_state;
716 }
717
718 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
719
720 static char *acpi_ac_adapter_state;
721
722 char *get_acpi_ac_adapter()
723 {
724         static int rep;
725         char buf[256];
726         char buf2[256];
727         FILE *fp;
728
729         if (acpi_ac_adapter_state == NULL) {
730                 acpi_ac_adapter_state = (char *) malloc(100);
731                 assert(acpi_ac_adapter_state != NULL);
732         }
733
734         /* yeah, slow... :/ */
735         if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep))
736                 return "no ac_adapters?";
737
738         snprintf(buf2, 256, "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
739
740         fp = open_file(buf2, &rep);
741         if (!fp) {
742                 strcpy(acpi_ac_adapter_state,
743                        "No ac adapter found.... where is it?");
744                 return acpi_ac_adapter_state;
745         }
746         fscanf(fp, "%*s %99s", acpi_ac_adapter_state);
747         fclose(fp);
748
749         return acpi_ac_adapter_state;
750 }
751
752 /*
753 /proc/acpi/thermal_zone/THRM/cooling_mode
754 cooling mode:            active
755 /proc/acpi/thermal_zone/THRM/polling_frequency
756 <polling disabled>
757 /proc/acpi/thermal_zone/THRM/state
758 state:                   ok
759 /proc/acpi/thermal_zone/THRM/temperature
760 temperature:             45 C
761 /proc/acpi/thermal_zone/THRM/trip_points
762 critical (S5):           73 C
763 passive:                 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
764 */
765
766 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
767 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
768
769 int open_acpi_temperature(const char *name)
770 {
771         char path[256];
772         char buf[64];
773         int fd;
774
775         if (name == NULL || strcmp(name, "*") == 0) {
776                 static int rep;
777                 if (!get_first_file_in_a_directory
778                                    (ACPI_THERMAL_DIR, buf, &rep))
779                         return -1;
780                 name = buf;
781         }
782
783         snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
784
785         fd = open(path, O_RDONLY);
786         if (fd < 0)
787                 ERR("can't open '%s': %s", path, strerror(errno));
788
789         return fd;
790 }
791
792 static double last_acpi_temp;
793 static double last_acpi_temp_time;
794
795 double get_acpi_temperature(int fd)
796 {
797         if (fd <= 0)
798                 return 0;
799
800         /* don't update acpi temperature too often */
801         if (current_update_time - last_acpi_temp_time < 11.32) {
802                 return last_acpi_temp;
803         }
804         last_acpi_temp_time = current_update_time;
805
806         /* seek to beginning */
807         lseek(fd, 0, SEEK_SET);
808
809         /* read */
810         {
811                 char buf[256];
812                 int n;
813                 n = read(fd, buf, 255);
814                 if (n < 0)
815                         ERR("can't read fd %d: %s", fd, strerror(errno));
816                 else {
817                         buf[n] = '\0';
818                         sscanf(buf, "temperature: %lf", &last_acpi_temp);
819                 }
820         }
821
822         return last_acpi_temp;
823 }
824
825 /*
826 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info 
827 present:                 yes
828 design capacity:         4400 mAh
829 last full capacity:      4064 mAh
830 battery technology:      rechargeable
831 design voltage:          14800 mV
832 design capacity warning: 300 mAh
833 design capacity low:     200 mAh
834 capacity granularity 1:  32 mAh
835 capacity granularity 2:  32 mAh
836 model number:            02KT
837 serial number:           16922
838 battery type:            LION
839 OEM info:                SANYO
840 */
841
842 /*
843 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
844 present:                 yes
845 capacity state:          ok
846 charging state:          unknown
847 present rate:            0 mA
848 remaining capacity:      4064 mAh
849 present voltage:         16608 mV
850 */
851
852 /*
853 2213<@jupet kellari ö> jupet@lagi-unstable:~$ cat /proc/apm 
854 2213<@jupet kellari ö> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
855 2213<@jupet kellari ö> (-1 ollee ei akkua kiinni, koska akku on pöydällä)
856 2214<@jupet kellari ö> jupet@lagi-unstable:~$ cat /proc/apm 
857 2214<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
858
859 2238<@jupet kellari ö> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
860 2239<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
861
862 2240<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori päällä
863 2241<@jupet kellari ö> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori päällä mutta ilman verkkovirtaa
864 */
865
866 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
867 #define APM_PATH "/proc/apm"
868
869 static FILE *acpi_bat_fp;
870 static FILE *apm_bat_fp;
871
872 static int acpi_last_full;
873
874 static char last_battery_str[64];
875
876 static double last_battery_time;
877
878 void get_battery_stuff(char *buf, unsigned int n, const char *bat)
879 {
880         static int rep, rep2;
881         char acpi_path[128];
882         snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
883
884         /* don't update battery too often */
885         if (current_update_time - last_battery_time < 29.5) {
886                 snprintf(buf, n, "%s", last_battery_str);
887                 return;
888         }
889         last_battery_time = current_update_time;
890
891         /* first try ACPI */
892
893         if (acpi_bat_fp == NULL && apm_bat_fp == NULL)
894                 acpi_bat_fp = open_file(acpi_path, &rep);
895
896         if (acpi_bat_fp != NULL) {
897                 int present_rate = -1;
898                 int remaining_capacity = -1;
899                 char charging_state[64];
900
901                 /* read last full capacity if it's zero */
902                 if (acpi_last_full == 0) {
903                         static int rep;
904                         char path[128];
905                         FILE *fp;
906                         snprintf(path, 127,
907                                  ACPI_BATTERY_BASE_PATH "/%s/info", bat);
908                         fp = open_file(path, &rep);
909                         if (fp != NULL) {
910                                 while (!feof(fp)) {
911                                         char b[256];
912                                         if (fgets(b, 256, fp) == NULL)
913                                                 break;
914
915                                         if (sscanf
916                                                                                (b, "last full capacity: %d",
917                                                         &acpi_last_full) != 0)
918                                                 break;
919                                 }
920
921                                 fclose(fp);
922                         }
923                 }
924
925                 fseek(acpi_bat_fp, 0, SEEK_SET);
926
927                 strcpy(charging_state, "unknown");
928
929                 while (!feof(acpi_bat_fp)) {
930                         char buf[256];
931                         if (fgets(buf, 256, acpi_bat_fp) == NULL)
932                                 break;
933
934                         /* let's just hope units are ok */
935                         if (buf[0] == 'c')
936                                 sscanf(buf, "charging state: %63s",
937                                        charging_state);
938                         else if (buf[0] == 'p')
939                                 sscanf(buf, "present rate: %d",
940                                        &present_rate);
941                         else if (buf[0] == 'r')
942                                 sscanf(buf, "remaining capacity: %d",
943                                        &remaining_capacity);
944                 }
945
946                 /* charging */
947                 if (strcmp(charging_state, "charging") == 0) {
948                         if (acpi_last_full != 0 && present_rate > 0) {
949                                 strcpy(last_battery_str, "charging ");
950                                 format_seconds(last_battery_str + 9,
951                                                63 - 9,
952                                                (acpi_last_full -
953                                                                remaining_capacity) * 60 *
954                                                                60 / present_rate);
955                         } else if (acpi_last_full != 0
956                                                         && present_rate <= 0) {
957                                                                 sprintf(last_battery_str, "charging %d%%",
958                                                                                 remaining_capacity * 100 /
959                                                                                 acpi_last_full);
960                                                         } else {
961                                                                 strcpy(last_battery_str, "charging");
962                                                         }
963                 }
964                 /* discharging */
965                 else if (strcmp(charging_state, "discharging") == 0) {
966                         if (present_rate > 0)
967                                 format_seconds(last_battery_str, 63,
968                                                (remaining_capacity * 60 *
969                                                                60) / present_rate);
970                         else
971                                 sprintf(last_battery_str,
972                                         "discharging %d%%",
973                                         remaining_capacity * 100 /
974                                                         acpi_last_full);
975                 }
976                 /* charged */
977                 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
978                 else if (strcmp(charging_state, "charged") == 0) {
979                         if (acpi_last_full != 0 && remaining_capacity != acpi_last_full)
980                                 sprintf(last_battery_str, "charged %d%%",
981                                         remaining_capacity * 100 / acpi_last_full);
982                         else
983                                 strcpy(last_battery_str, "charged");
984                 }
985                 /* unknown, probably full / AC */
986                 else {
987                         if (acpi_last_full != 0
988                                                  && remaining_capacity != acpi_last_full)
989                                 sprintf(last_battery_str, "unknown %d%%",
990                                         remaining_capacity * 100 /
991                                                         acpi_last_full);
992                         else
993                                 strcpy(last_battery_str, "AC");
994                 }
995         } else {
996                 /* APM */
997                 if (apm_bat_fp == NULL)
998                         apm_bat_fp = open_file(APM_PATH, &rep2);
999
1000                 if (apm_bat_fp != NULL) {
1001                         int ac, status, flag, life;
1002
1003                         fscanf(apm_bat_fp,
1004                                "%*s %*s %*x %x   %x       %x     %d%%",
1005                                &ac, &status, &flag, &life);
1006
1007                         if (life == -1) {
1008                                 /* could check now that there is ac */
1009                                 snprintf(last_battery_str, 64, "AC");
1010                         } else if (ac && life != 100) { /* could check that status==3 here? */
1011                                 snprintf(last_battery_str, 64,
1012                                          "charging %d%%", life);
1013                         } else {
1014                                 snprintf(last_battery_str, 64, "%d%%",
1015                                          life);
1016                         }
1017
1018                         /* it seemed to buffer it so file must be closed (or could use syscalls
1019                         * directly but I don't feel like coding it now) */
1020                         fclose(apm_bat_fp);
1021                         apm_bat_fp = NULL;
1022                 }
1023         }
1024
1025         snprintf(buf, n, "%s", last_battery_str);
1026 }
1027
1028 void update_top()
1029 {
1030         show_nice_processes = 1;
1031         process_find_top(info.tops);
1032 }