Merge with conky1-extra, Changelog contains the changes
[monky] / src / linux.c
1 /* Conky, a system monitor, based on torsmo
2  *
3  * Any original torsmo code is licensed under the BSD license
4  *
5  * All code written since the fork of torsmo is licensed under the GPL
6  *
7  * Please see COPYING for details
8  *
9  * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
10  * Copyright (c) 2007 Toni Spets
11  * Copyright (c) 2005-2008 Brenden Matthews, Philip Kovacs, et. al.
12  *      (see AUTHORS)
13  * All rights reserved.
14  *
15  * This program is free software: you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation, either version 3 of the License, or
18  * (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  * You should have received a copy of the GNU General Public License
25  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
26  *
27  * $Id$ */
28
29 #include "conky.h"
30 #include <dirent.h>
31 #include <ctype.h>
32 #include <errno.h>
33 #include <limits.h>
34 #include <sys/types.h>
35 #include <sys/sysinfo.h>
36 #include <sys/stat.h>
37 #ifndef HAVE_CLOCK_GETTIME
38 #include <sys/time.h>
39 #endif
40 #include <fcntl.h>
41 #include <unistd.h>
42 // #include <assert.h>
43 #include <time.h>
44 #include "top.h"
45
46 #include <sys/ioctl.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <linux/sockios.h>
50 #include <net/if.h>
51 #include <arpa/inet.h>
52 #ifdef _NET_IF_H
53 #define _LINUX_IF_H
54 #endif
55 #include <linux/route.h>
56 #include <math.h>
57
58 #ifdef HAVE_IWLIB
59 #include <iwlib.h>
60 #endif
61
62 #define SHORTSTAT_TEMPL "%*s %llu %llu %llu"
63 #define LONGSTAT_TEMPL "%*s %llu %llu %llu "
64
65 static int show_nice_processes;
66
67 /* This flag tells the linux routines to use the /proc system where possible,
68  * even if other api's are available, e.g. sysinfo() or getloadavg().
69  * the reason for this is to allow for /proc-based distributed monitoring.
70  * using a flag in this manner creates less confusing code. */
71 static int prefer_proc = 0;
72
73 void prepare_update(void)
74 {
75 }
76
77 void update_uptime(void)
78 {
79 #ifdef HAVE_SYSINFO
80         if (!prefer_proc) {
81                 struct sysinfo s_info;
82
83                 sysinfo(&s_info);
84                 info.uptime = (double) s_info.uptime;
85         } else
86 #endif
87         {
88                 static int rep = 0;
89                 FILE *fp;
90
91                 if (!(fp = open_file("/proc/uptime", &rep))) {
92                         info.uptime = 0.0;
93                         return;
94                 }
95                 fscanf(fp, "%lf", &info.uptime);
96                 fclose(fp);
97         }
98         info.mask |= (1 << INFO_UPTIME);
99 }
100
101 int check_mount(char *s)
102 {
103         int ret = 0;
104         FILE *mtab = fopen("/etc/mtab", "r");
105
106         if (mtab) {
107                 char buf1[256], buf2[128];
108
109                 while (fgets(buf1, 256, mtab)) {
110                         sscanf(buf1, "%*s %128s", buf2);
111                         if (!strcmp(s, buf2)) {
112                                 ret = 1;
113                                 break;
114                         }
115                 }
116                 fclose(mtab);
117         } else {
118                 ERR("Could not open mtab");
119         }
120         return ret;
121 }
122
123 /* these things are also in sysinfo except Buffers:
124  * (that's why I'm reading them from proc) */
125
126 void update_meminfo(void)
127 {
128         FILE *meminfo_fp;
129         static int rep = 0;
130
131         /* unsigned int a; */
132         char buf[256];
133
134         info.mem = info.memmax = info.swap = info.swapmax = info.bufmem =
135                 info.buffers = info.cached = info.memfree = info.memeasyfree = 0;
136
137         if (!(meminfo_fp = open_file("/proc/meminfo", &rep))) {
138                 return;
139         }
140
141         while (!feof(meminfo_fp)) {
142                 if (fgets(buf, 255, meminfo_fp) == NULL) {
143                         break;
144                 }
145
146                 if (strncmp(buf, "MemTotal:", 9) == 0) {
147                         sscanf(buf, "%*s %llu", &info.memmax);
148                 } else if (strncmp(buf, "MemFree:", 8) == 0) {
149                         sscanf(buf, "%*s %llu", &info.memfree);
150                 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
151                         sscanf(buf, "%*s %llu", &info.swapmax);
152                 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
153                         sscanf(buf, "%*s %llu", &info.swap);
154                 } else if (strncmp(buf, "Buffers:", 8) == 0) {
155                         sscanf(buf, "%*s %llu", &info.buffers);
156                 } else if (strncmp(buf, "Cached:", 7) == 0) {
157                         sscanf(buf, "%*s %llu", &info.cached);
158                 }
159         }
160
161         info.mem = info.memmax - info.memfree;
162         info.memeasyfree = info.memfree;
163         info.swap = info.swapmax - info.swap;
164
165         info.bufmem = info.cached + info.buffers;
166
167         info.mask |= (1 << INFO_MEM) | (1 << INFO_BUFFERS);
168
169         fclose(meminfo_fp);
170 }
171
172 int get_laptop_mode(void)
173 {
174         FILE *fp;
175         int val = -1;
176
177         if ((fp = fopen("/proc/sys/vm/laptop_mode", "r")) != NULL)
178                 fscanf(fp, "%d\n", &val);
179         fclose(fp);
180         return val;
181 }
182
183 /* my system says:
184  * # cat /sys/block/sda/queue/scheduler
185  * noop [anticipatory] cfq
186  */
187 char *get_ioscheduler(char *disk)
188 {
189         FILE *fp;
190         char buf[128];
191
192         if (!disk)
193                 return strndup("n/a", text_buffer_size);
194
195         snprintf(buf, 127, "/sys/block/%s/queue/scheduler", disk);
196         if ((fp = fopen(buf, "r")) == NULL) {
197                 return strndup("n/a", text_buffer_size);
198         }
199         while (!feof(fp)) {
200                 fscanf(fp, "%127s", buf);
201                 if (buf[0] == '[') {
202                         buf[strlen(buf) - 1] = '\0';
203                         fclose(fp);
204                         return strndup(buf + 1, text_buffer_size);
205                 }
206         }
207         fclose(fp);
208         return strndup("n/a", text_buffer_size);
209 }
210
211 int interface_up(const char *dev)
212 {
213         int fd;
214         struct ifreq ifr;
215
216         if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
217                 CRIT_ERR("could not create sockfd");
218                 return 0;
219         }
220         strncpy(ifr.ifr_name, dev, IFNAMSIZ);
221         if (ioctl(fd, SIOCGIFFLAGS, &ifr)) {
222                 /* if device does not exist, treat like not up */
223                 if (errno != ENODEV)
224                         perror("SIOCGIFFLAGS");
225                 goto END_FALSE;
226         }
227
228         if (!(ifr.ifr_flags & IFF_UP)) /* iface is not up */
229                 goto END_FALSE;
230         if (ifup_strictness == IFUP_UP)
231                 goto END_TRUE;
232
233         if (!(ifr.ifr_flags & IFF_RUNNING))
234                 goto END_FALSE;
235         if (ifup_strictness == IFUP_LINK)
236                 goto END_TRUE;
237
238         if (ioctl(fd, SIOCGIFADDR, &ifr)) {
239                 perror("SIOCGIFADDR");
240                 goto END_FALSE;
241         }
242         if (((struct sockaddr_in *)&(ifr.ifr_ifru.ifru_addr))->sin_addr.s_addr)
243                 goto END_TRUE;
244
245 END_FALSE:
246         close(fd);
247         return 0;
248 END_TRUE:
249         close(fd);
250         return 1;
251 }
252
253 #define COND_FREE(x) if(x) free(x); x = 0
254 #define SAVE_SET_STRING(x, y) \
255         if (x && strcmp((char *)x, (char *)y)) { \
256                 free(x); \
257                 x = strndup("multiple", text_buffer_size); \
258         } else if (!x) { \
259                 x = strndup(y, text_buffer_size); \
260         }
261
262 void update_gateway_info_failure(const char *reason)
263 {
264         if(reason != NULL) {
265                 perror(reason);
266         }
267         //2 pointers to 1 location causes a crash when we try to free them both
268         info.gw_info.iface = strndup("failed", text_buffer_size);
269         info.gw_info.ip = strndup("failed", text_buffer_size);
270 }
271
272 void update_gateway_info(void)
273 {
274         FILE *fp;
275         struct in_addr ina;
276         char iface[64];
277         unsigned long dest, gate, mask;
278         unsigned int flags;
279         short ref, use, metric, mtu, win, irtt;
280
281         struct gateway_info *gw_info = &info.gw_info;
282
283         COND_FREE(gw_info->iface);
284         COND_FREE(gw_info->ip);
285         gw_info->count = 0;
286
287         if ((fp = fopen("/proc/net/route", "r")) == NULL) {
288                 update_gateway_info_failure("fopen()");
289                 return;
290         }
291         if (fscanf(fp, "%*[^\n]\n") == EOF) {
292                 //NULL because a empty table is not a error
293                 update_gateway_info_failure(NULL);
294                 fclose(fp);
295                 return;
296         }
297         while (!feof(fp)) {
298                 // Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
299                 if(fscanf(fp, "%63s %lx %lx %x %hd %hd %hd %lx %hd %hd %hd\n",
300                                         iface, &dest, &gate, &flags, &ref, &use,
301                                         &metric, &mask, &mtu, &win, &irtt) != 11) {
302                         update_gateway_info_failure("fscanf()");
303                         fclose(fp);
304                         return;
305                 }
306                 if (flags & RTF_GATEWAY && dest == 0 && mask == 0) {
307                         gw_info->count++;
308                         SAVE_SET_STRING(gw_info->iface, iface)
309                                 ina.s_addr = gate;
310                         SAVE_SET_STRING(gw_info->ip, inet_ntoa(ina))
311                 }
312         }
313         fclose(fp);
314         return;
315 }
316
317 void update_net_stats(void)
318 {
319         FILE *net_dev_fp;
320         static int rep = 0;
321
322         // FIXME: arbitrary size chosen to keep code simple.
323         int i, i2;
324         unsigned int curtmp1, curtmp2;
325         unsigned int k;
326         struct ifconf conf;
327         char buf[256];
328         double delta;
329
330 #ifdef HAVE_IWLIB
331         // wireless info variables
332         int skfd, has_bitrate = 0;
333         struct wireless_info *winfo;
334         struct iwreq wrq;
335 #endif
336
337         /* get delta */
338         delta = current_update_time - last_update_time;
339         if (delta <= 0.0001) {
340                 return;
341         }
342
343         /* open file and ignore first two lines */
344         if (!(net_dev_fp = open_file("/proc/net/dev", &rep))) {
345                 clear_net_stats();
346                 return;
347         }
348
349         fgets(buf, 255, net_dev_fp);    /* garbage */
350         fgets(buf, 255, net_dev_fp);    /* garbage (field names) */
351
352         /* read each interface */
353         for (i2 = 0; i2 < 16; i2++) {
354                 struct net_stat *ns;
355                 char *s, *p;
356                 char temp_addr[17];
357                 long long r, t, last_recv, last_trans;
358
359                 if (fgets(buf, 255, net_dev_fp) == NULL) {
360                         break;
361                 }
362                 p = buf;
363                 while (isspace((int) *p)) {
364                         p++;
365                 }
366
367                 s = p;
368
369                 while (*p && *p != ':') {
370                         p++;
371                 }
372                 if (*p == '\0') {
373                         continue;
374                 }
375                 *p = '\0';
376                 p++;
377
378                 ns = get_net_stat(s);
379                 ns->up = 1;
380                 memset(&(ns->addr.sa_data), 0, 14);
381
382                 if(NULL == ns->addrs)
383                         ns->addrs = (char*) malloc(17 * 16);
384                 if(NULL != ns->addrs)
385                         memset(ns->addrs, 0, 17 * 16); /* Up to 17 chars per ip, max 16 interfaces. Nasty memory usage... */
386
387                 last_recv = ns->recv;
388                 last_trans = ns->trans;
389
390                 /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
391                 sscanf(p, "%lld  %*d     %*d  %*d  %*d  %*d   %*d        %*d       %lld",
392                         &r, &t);
393
394                 /* if recv or trans is less than last time, an overflow happened */
395                 if (r < ns->last_read_recv) {
396                         last_recv = 0;
397                 } else {
398                         ns->recv += (r - ns->last_read_recv);
399                 }
400                 ns->last_read_recv = r;
401
402                 if (t < ns->last_read_trans) {
403                         last_trans = 0;
404                 } else {
405                         ns->trans += (t - ns->last_read_trans);
406                 }
407                 ns->last_read_trans = t;
408
409                 /*** ip addr patch ***/
410                 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
411
412                 conf.ifc_buf = malloc(sizeof(struct ifreq) * 16);
413                 conf.ifc_len = sizeof(struct ifreq) * 16;
414                 memset(conf.ifc_buf, 0, conf.ifc_len);
415
416                 ioctl((long) i, SIOCGIFCONF, &conf);
417
418                 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
419                         struct net_stat *ns2;
420
421                         if (!(((struct ifreq *) conf.ifc_buf) + k))
422                                 break;
423
424                         ns2 = get_net_stat(
425                                 ((struct ifreq *) conf.ifc_buf)[k].ifr_ifrn.ifrn_name);
426                         ns2->addr = ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.ifru_addr;
427                         if(NULL != ns2->addrs) {
428                                 sprintf(temp_addr, "%u.%u.%u.%u, ",
429                                         ns2->addr.sa_data[2] & 255,
430                                         ns2->addr.sa_data[3] & 255,
431                                         ns2->addr.sa_data[4] & 255,
432                                         ns2->addr.sa_data[5] & 255);
433                                 if(NULL == strstr(ns2->addrs, temp_addr))
434                                         strncpy(ns2->addrs + strlen(ns2->addrs), temp_addr, 17);
435                         }
436                 }
437
438                 close((long) i);
439
440                 free(conf.ifc_buf);
441
442                 /*** end ip addr patch ***/
443
444                 /* calculate speeds */
445                 ns->net_rec[0] = (ns->recv - last_recv) / delta;
446                 ns->net_trans[0] = (ns->trans - last_trans) / delta;
447                 curtmp1 = 0;
448                 curtmp2 = 0;
449                 // get an average
450                 for (i = 0; (unsigned) i < info.net_avg_samples; i++) {
451                         curtmp1 += ns->net_rec[i];
452                         curtmp2 += ns->net_trans[i];
453                 }
454                 if (curtmp1 == 0) {
455                         curtmp1 = 1;
456                 }
457                 if (curtmp2 == 0) {
458                         curtmp2 = 1;
459                 }
460                 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
461                 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
462                 if (info.net_avg_samples > 1) {
463                         for (i = info.net_avg_samples; i > 1; i--) {
464                                 ns->net_rec[i - 1] = ns->net_rec[i - 2];
465                                 ns->net_trans[i - 1] = ns->net_trans[i - 2];
466                         }
467                 }
468
469 #ifdef HAVE_IWLIB
470                 /* update wireless info */
471                 winfo = malloc(sizeof(struct wireless_info));
472                 memset(winfo, 0, sizeof(struct wireless_info));
473
474                 skfd = iw_sockets_open();
475                 if (iw_get_basic_config(skfd, s, &(winfo->b)) > -1) {
476
477                         // set present winfo variables
478                         if (iw_get_stats(skfd, s, &(winfo->stats),
479                                         &winfo->range, winfo->has_range) >= 0) {
480                                 winfo->has_stats = 1;
481                         }
482                         if (iw_get_range_info(skfd, s, &(winfo->range)) >= 0) {
483                                 winfo->has_range = 1;
484                         }
485                         if (iw_get_ext(skfd, s, SIOCGIWAP, &wrq) >= 0) {
486                                 winfo->has_ap_addr = 1;
487                                 memcpy(&(winfo->ap_addr), &(wrq.u.ap_addr), sizeof(sockaddr));
488                         }
489
490                         // get bitrate
491                         if (iw_get_ext(skfd, s, SIOCGIWRATE, &wrq) >= 0) {
492                                 memcpy(&(winfo->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
493                                 iw_print_bitrate(ns->bitrate, 16, winfo->bitrate.value);
494                                 has_bitrate = 1;
495                         }
496
497                         // get link quality
498                         if (winfo->has_range && winfo->has_stats
499                                         && ((winfo->stats.qual.level != 0)
500                                         || (winfo->stats.qual.updated & IW_QUAL_DBM))) {
501                                 if (!(winfo->stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
502                                         ns->link_qual = winfo->stats.qual.qual;
503                                         ns->link_qual_max = winfo->range.max_qual.qual;
504                                 }
505                         }
506
507                         // get ap mac
508                         if (winfo->has_ap_addr) {
509                                 iw_sawap_ntop(&winfo->ap_addr, ns->ap);
510                         }
511
512                         // get essid
513                         if (winfo->b.has_essid) {
514                                 if (winfo->b.essid_on) {
515                                         snprintf(ns->essid, 32, "%s", winfo->b.essid);
516                                 } else {
517                                         snprintf(ns->essid, 32, "off/any");
518                                 }
519                         }
520
521                         snprintf(ns->mode, 16, "%s", iw_operation_mode[winfo->b.mode]);
522                 }
523                 iw_sockets_close(skfd);
524                 free(winfo);
525 #endif
526         }
527
528         fclose(net_dev_fp);
529
530         info.mask |= (1 << INFO_NET);
531 }
532
533 int result;
534
535 void update_total_processes(void)
536 {
537 #ifdef HAVE_SYSINFO
538         if (!prefer_proc) {
539                 struct sysinfo s_info;
540
541                 sysinfo(&s_info);
542                 info.procs = s_info.procs;
543         } else
544 #endif
545         {
546                 static int rep = 0;
547                 FILE *fp;
548
549                 if (!(fp = open_file("/proc/loadavg", &rep))) {
550                         info.procs = 0;
551                         return;
552                 }
553                 fscanf(fp, "%*f %*f %*f %*d/%hu", &info.procs);
554                 fclose(fp);
555         }
556         info.mask |= (1 << INFO_PROCS);
557 }
558
559 #define CPU_SAMPLE_COUNT 15
560 struct cpu_info {
561         unsigned long long cpu_user;
562         unsigned long long cpu_system;
563         unsigned long long cpu_nice;
564         unsigned long long cpu_idle;
565         unsigned long long cpu_iowait;
566         unsigned long long cpu_irq;
567         unsigned long long cpu_softirq;
568         unsigned long long cpu_steal;
569         unsigned long long cpu_total;
570         unsigned long long cpu_active_total;
571         unsigned long long cpu_last_total;
572         unsigned long long cpu_last_active_total;
573         double cpu_val[CPU_SAMPLE_COUNT];
574 };
575 static short cpu_setup = 0;
576
577 /* Determine if this kernel gives us "extended" statistics information in
578  * /proc/stat.
579  * Kernels around 2.5 and earlier only reported user, system, nice, and
580  * idle values in proc stat.
581  * Kernels around 2.6 and greater report these PLUS iowait, irq, softirq,
582  * and steal */
583 void determine_longstat(char *buf)
584 {
585         unsigned long long iowait = 0;
586
587         KFLAG_SETOFF(KFLAG_IS_LONGSTAT);
588         /* scanf will either return -1 or 1 because there is only 1 assignment */
589         if (sscanf(buf, "%*s %*d %*d %*d %*d %llu", &iowait) > 0) {
590                 KFLAG_SETON(KFLAG_IS_LONGSTAT);
591         }
592 }
593
594 void get_cpu_count(void)
595 {
596         FILE *stat_fp;
597         static int rep = 0;
598         char buf[256];
599
600         if (info.cpu_usage) {
601                 return;
602         }
603
604         if (!(stat_fp = open_file("/proc/stat", &rep))) {
605                 return;
606         }
607
608         info.cpu_count = 0;
609
610         while (!feof(stat_fp)) {
611                 if (fgets(buf, 255, stat_fp) == NULL) {
612                         break;
613                 }
614
615                 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
616                         if (info.cpu_count == 0) {
617                                 determine_longstat(buf);
618                         }
619                         info.cpu_count++;
620                 }
621         }
622         info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
623
624         fclose(stat_fp);
625 }
626
627 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
628 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
629
630 inline static void update_stat(void)
631 {
632         FILE *stat_fp;
633         static int rep = 0;
634         static struct cpu_info *cpu = NULL;
635         char buf[256];
636         unsigned int i;
637         unsigned int idx;
638         double curtmp;
639         const char *stat_template = NULL;
640         unsigned int malloc_cpu_size = 0;
641
642         /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
643         if (!cpu_setup || !info.cpu_usage) {
644                 get_cpu_count();
645                 cpu_setup = 1;
646         }
647
648         if (!stat_template) {
649                 stat_template =
650                         KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
651         }
652
653         if (!cpu) {
654                 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
655                 cpu = malloc(malloc_cpu_size);
656                 memset(cpu, 0, malloc_cpu_size);
657         }
658
659         if (!(stat_fp = open_file("/proc/stat", &rep))) {
660                 info.run_procs = 0;
661                 if (info.cpu_usage) {
662                         memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
663                 }
664                 return;
665         }
666
667         idx = 0;
668         while (!feof(stat_fp)) {
669                 if (fgets(buf, 255, stat_fp) == NULL) {
670                         break;
671                 }
672
673                 if (strncmp(buf, "procs_running ", 14) == 0) {
674                         sscanf(buf, "%*s %hu", &info.run_procs);
675                         info.mask |= (1 << INFO_RUN_PROCS);
676                 } else if (strncmp(buf, "cpu", 3) == 0) {
677                         double delta;
678                         idx = isdigit(buf[3]) ? ((int) buf[3]) - 0x2F : 0;
679                         sscanf(buf, stat_template, &(cpu[idx].cpu_user),
680                                 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
681                                 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
682                                 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
683                                 &(cpu[idx].cpu_steal));
684
685                         cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
686                                 cpu[idx].cpu_system + cpu[idx].cpu_idle +
687                                 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
688                                 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
689
690                         cpu[idx].cpu_active_total = cpu[idx].cpu_total -
691                                 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
692                         info.mask |= (1 << INFO_CPU);
693
694                         delta = current_update_time - last_update_time;
695
696                         if (delta <= 0.001) {
697                                 break;
698                         }
699
700                         cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
701                                 cpu[idx].cpu_last_active_total) /
702                                 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
703                         curtmp = 0;
704                         for (i = 0; i < info.cpu_avg_samples; i++) {
705                                 curtmp += cpu[idx].cpu_val[i];
706                         }
707                         /* TESTING -- I've removed this, because I don't think it is right.
708                          * You shouldn't divide by the cpu count here ...
709                          * removing for testing */
710                         /* if (idx == 0) {
711                                 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
712                                         info.cpu_count;
713                         } else {
714                                 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
715                         } */
716                         /* TESTING -- this line replaces the prev. "suspect" if/else */
717                         info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
718
719                         cpu[idx].cpu_last_total = cpu[idx].cpu_total;
720                         cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
721                         for (i = info.cpu_avg_samples - 1; i > 0; i--) {
722                                 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
723                         }
724                 }
725         }
726         fclose(stat_fp);
727 }
728
729 void update_running_processes(void)
730 {
731         update_stat();
732 }
733
734 void update_cpu_usage(void)
735 {
736         update_stat();
737 }
738
739 void update_load_average(void)
740 {
741 #ifdef HAVE_GETLOADAVG
742         if (!prefer_proc) {
743                 double v[3];
744
745                 getloadavg(v, 3);
746                 info.loadavg[0] = (float) v[0];
747                 info.loadavg[1] = (float) v[1];
748                 info.loadavg[2] = (float) v[2];
749         } else
750 #endif
751         {
752                 static int rep = 0;
753                 FILE *fp;
754
755                 if (!(fp = open_file("/proc/loadavg", &rep))) {
756                         info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
757                         return;
758                 }
759                 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
760                         &info.loadavg[2]);
761                 fclose(fp);
762         }
763         info.mask |= (1 << INFO_LOADAVG);
764 }
765
766 #define PROC_I8K "/proc/i8k"
767 #define I8K_DELIM " "
768 static char *i8k_procbuf = NULL;
769 void update_i8k(void)
770 {
771         FILE *fp;
772
773         if (!i8k_procbuf) {
774                 i8k_procbuf = (char *) malloc(128 * sizeof(char));
775         }
776         if ((fp = fopen(PROC_I8K, "r")) == NULL) {
777                 CRIT_ERR("/proc/i8k doesn't exist! use insmod to make sure the kernel "
778                         "driver is loaded...");
779         }
780
781         memset(&i8k_procbuf[0], 0, 128);
782         if (fread(&i8k_procbuf[0], sizeof(char), 128, fp) == 0) {
783                 ERR("something wrong with /proc/i8k...");
784         }
785
786         fclose(fp);
787
788         i8k.version = strtok(&i8k_procbuf[0], I8K_DELIM);
789         i8k.bios = strtok(NULL, I8K_DELIM);
790         i8k.serial = strtok(NULL, I8K_DELIM);
791         i8k.cpu_temp = strtok(NULL, I8K_DELIM);
792         i8k.left_fan_status = strtok(NULL, I8K_DELIM);
793         i8k.right_fan_status = strtok(NULL, I8K_DELIM);
794         i8k.left_fan_rpm = strtok(NULL, I8K_DELIM);
795         i8k.right_fan_rpm = strtok(NULL, I8K_DELIM);
796         i8k.ac_status = strtok(NULL, I8K_DELIM);
797         i8k.buttons_status = strtok(NULL, I8K_DELIM);
798 }
799
800 /***********************************************************/
801 /***********************************************************/
802 /***********************************************************/
803
804 static int no_dots(const struct dirent *d)
805 {
806         if (d->d_name[0] == '.') {
807                 return 0;
808         }
809         return 1;
810 }
811
812 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
813 {
814         struct dirent **namelist;
815         int i, n;
816
817         n = scandir(dir, &namelist, no_dots, alphasort);
818         if (n < 0) {
819                 if (!rep || !*rep) {
820                         ERR("scandir for %s: %s", dir, strerror(errno));
821                         if (rep) {
822                                 *rep = 1;
823                         }
824                 }
825                 return 0;
826         } else {
827                 if (n == 0) {
828                         return 0;
829                 }
830
831                 strncpy(s, namelist[0]->d_name, 255);
832                 s[255] = '\0';
833
834                 for (i = 0; i < n; i++) {
835                         free(namelist[i]);
836                 }
837                 free(namelist);
838
839                 return 1;
840         }
841 }
842
843 int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
844                 int *divisor, char *devtype)
845 {
846         char path[256];
847         char buf[256];
848         int fd;
849         int divfd;
850
851         memset(buf, 0, sizeof(buf));
852
853         /* if device is NULL or *, get first */
854         if (dev == NULL || strcmp(dev, "*") == 0) {
855                 static int rep = 0;
856
857                 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
858                         return -1;
859                 }
860                 dev = buf;
861         }
862
863         if (strcmp(dir, "/sys/class/hwmon/") == 0) {
864                 if (*buf) {
865                         /* buf holds result from get_first_file_in_a_directory() above,
866                          * e.g. "hwmon0" -- append "/device" */
867                         strcat(buf, "/device");
868                 } else {
869                         /* dev holds device number N as a string,
870                          * e.g. "0", -- convert to "hwmon0/device" */
871                         sprintf(buf, "hwmon%s/device", dev);
872                         dev = buf;
873                 }
874         }
875
876         /* change vol to in */
877         if (strcmp(type, "vol") == 0) {
878                 type = "in";
879         }
880
881         if (strcmp(type, "tempf") == 0) {
882                 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, "temp", n);
883         } else {
884                 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
885         }
886         strncpy(devtype, path, 255);
887
888         /* open file */
889         fd = open(path, O_RDONLY);
890         if (fd < 0) {
891                 CRIT_ERR("can't open '%s': %s\nplease check your device or remove this "
892                         "var from Conky", path, strerror(errno));
893         }
894
895         if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
896                         || strcmp(type, "tempf") == 0) {
897                 *divisor = 1;
898         } else {
899                 *divisor = 0;
900         }
901         /* fan does not use *_div as a read divisor */
902         if (strcmp("fan", type) == 0) {
903                 return fd;
904         }
905
906         /* test if *_div file exist, open it and use it as divisor */
907         if (strcmp(type, "tempf") == 0) {
908                 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
909         } else {
910                 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
911         }
912
913         divfd = open(path, O_RDONLY);
914         if (divfd > 0) {
915                 /* read integer */
916                 char divbuf[64];
917                 int divn;
918
919                 divn = read(divfd, divbuf, 63);
920                 /* should read until n == 0 but I doubt that kernel will give these
921                  * in multiple pieces. :) */
922                 if (divn < 0) {
923                         ERR("open_sysfs_sensor(): can't read from sysfs");
924                 } else {
925                         divbuf[divn] = '\0';
926                         *divisor = atoi(divbuf);
927                 }
928         }
929
930         close(divfd);
931
932         return fd;
933 }
934
935 double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
936 {
937         int val = 0;
938
939         if (*fd <= 0) {
940                 return 0;
941         }
942
943         lseek(*fd, 0, SEEK_SET);
944
945         /* read integer */
946         {
947                 char buf[64];
948                 int n;
949                 n = read(*fd, buf, 63);
950                 /* should read until n == 0 but I doubt that kernel will give these
951                  * in multiple pieces. :) */
952                 if (n < 0) {
953                         ERR("get_sysfs_info(): read from %s failed\n", devtype);
954                 } else {
955                         buf[n] = '\0';
956                         val = atoi(buf);
957                 }
958         }
959
960         close(*fd);
961         /* open file */
962         *fd = open(devtype, O_RDONLY);
963         if (*fd < 0) {
964                 ERR("can't open '%s': %s", devtype, strerror(errno));
965         }
966
967         /* My dirty hack for computing CPU value
968          * Filedil, from forums.gentoo.org */
969         /* if (strstr(devtype, "temp1_input") != NULL) {
970                 return -15.096 + 1.4893 * (val / 1000.0);
971         } */
972
973         /* divide voltage and temperature by 1000 */
974         /* or if any other divisor is given, use that */
975         if (strcmp(type, "tempf") == 0) {
976                 if (divisor > 1) {
977                         return ((val / divisor + 40) * 9.0 / 5) - 40;
978                 } else if (divisor) {
979                         return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
980                 } else {
981                         return ((val + 40) * 9.0 / 5) - 40;
982                 }
983         } else {
984                 if (divisor > 1) {
985                         return val / divisor;
986                 } else if (divisor) {
987                         return val / 1000.0;
988                 } else {
989                         return val;
990                 }
991         }
992 }
993
994 /* Prior to kernel version 2.6.12, the CPU fan speed was available in
995  * ADT746X_FAN_OLD, whereas later kernel versions provide this information in
996  * ADT746X_FAN. */
997 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
998 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
999
1000 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
1001 {
1002         static int rep = 0;
1003         char adt746x_fan_state[64];
1004         FILE *fp;
1005
1006         if (!p_client_buffer || client_buffer_size <= 0) {
1007                 return;
1008         }
1009
1010         if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
1011                         && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL) {
1012                 sprintf(adt746x_fan_state, "adt746x not found");
1013         } else {
1014                 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
1015                 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
1016                 fclose(fp);
1017         }
1018
1019         snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_fan_state);
1020 }
1021
1022 /* Prior to kernel version 2.6.12, the CPU temperature was found in
1023  * ADT746X_CPU_OLD, whereas later kernel versions provide this information in
1024  * ADT746X_CPU. */
1025 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
1026 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
1027
1028 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
1029 {
1030         static int rep = 0;
1031         char adt746x_cpu_state[64];
1032         FILE *fp;
1033
1034         if (!p_client_buffer || client_buffer_size <= 0) {
1035                 return;
1036         }
1037
1038         if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
1039                         && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL) {
1040                 sprintf(adt746x_cpu_state, "adt746x not found");
1041         } else {
1042                 fscanf(fp, "%2s", adt746x_cpu_state);
1043                 fclose(fp);
1044         }
1045
1046         snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state);
1047 }
1048
1049 /* Thanks to "Walt Nelson" <wnelsonjr@comcast.net> */
1050
1051 /***********************************************************************/
1052 /* This file is part of x86info.
1053  * (C) 2001 Dave Jones.
1054  *
1055  * Licensed under the terms of the GNU GPL License version 2.
1056  *
1057  * Estimate CPU MHz routine by Andrea Arcangeli <andrea@suse.de>
1058  * Small changes by David Sterba <sterd9am@ss1000.ms.mff.cuni.cz> */
1059
1060 #if  defined(__i386) || defined(__x86_64)
1061 unsigned long long int rdtsc(void)
1062 {
1063         unsigned long long int x;
1064
1065         __asm__ volatile(".byte 0x0f, 0x31":"=A" (x));
1066         return x;
1067 }
1068 #endif
1069
1070 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1071 void get_freq_dynamic(char *p_client_buffer, size_t client_buffer_size,
1072                 const char *p_format, int divisor)
1073 {
1074 #if  defined(__i386) || defined(__x86_64)
1075         struct timezone tz;
1076         struct timeval tvstart, tvstop;
1077         unsigned long long cycles[2];   /* gotta be 64 bit */
1078         unsigned int microseconds;      /* total time taken */
1079
1080         if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1081                         || divisor <= 0) {
1082                 return;
1083         }
1084
1085         memset(&tz, 0, sizeof(tz));
1086
1087         /* get this function in cached memory */
1088         gettimeofday(&tvstart, &tz);
1089         cycles[0] = rdtsc();
1090         gettimeofday(&tvstart, &tz);
1091
1092         /* we don't trust that this is any specific length of time */
1093         usleep(100);
1094         cycles[1] = rdtsc();
1095         gettimeofday(&tvstop, &tz);
1096         microseconds = ((tvstop.tv_sec - tvstart.tv_sec) * 1000000) +
1097                 (tvstop.tv_usec - tvstart.tv_usec);
1098
1099         snprintf(p_client_buffer, client_buffer_size, p_format,
1100                 (float) ((cycles[1] - cycles[0]) / microseconds) / divisor);
1101         return;
1102 #else
1103         /* FIXME: hardwired: get freq for first cpu!
1104          * this whole function needs to be rethought and redone for
1105          * multi-cpu/multi-core/multi-threaded environments and
1106          * arbitrary combinations thereof */
1107         get_freq(p_client_buffer, client_buffer_size, p_format, divisor, 1);
1108         return;
1109 #endif
1110 }
1111
1112 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1113 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1114
1115 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1116 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1117                 const char *p_format, int divisor, unsigned int cpu)
1118 {
1119         FILE *f;
1120         static int rep = 0;
1121         char frequency[32];
1122         char s[256];
1123         double freq = 0;
1124
1125         if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1126                         || divisor <= 0) {
1127                 return 0;
1128         }
1129
1130         if (!prefer_proc) {
1131                 char current_freq_file[128];
1132
1133                 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1134                         CPUFREQ_POSTFIX);
1135                 f = fopen(current_freq_file, "r");
1136                 if (f) {
1137                         /* if there's a cpufreq /sys node, read the current frequency from
1138                          * this node and divide by 1000 to get Mhz. */
1139                         if (fgets(s, sizeof(s), f)) {
1140                                 s[strlen(s) - 1] = '\0';
1141                                 freq = strtod(s, NULL);
1142                         }
1143                         fclose(f);
1144                         snprintf(p_client_buffer, client_buffer_size, p_format,
1145                                 (freq / 1000) / divisor);
1146                         return 1;
1147                 }
1148         }
1149
1150         // open the CPU information file
1151         f = open_file("/proc/cpuinfo", &rep);
1152         if (!f) {
1153                 perror("Conky: Failed to access '/proc/cpuinfo' at get_freq()");
1154                 return 0;
1155         }
1156
1157         // read the file
1158         while (fgets(s, sizeof(s), f) != NULL) {
1159
1160 #if defined(__i386) || defined(__x86_64)
1161                 // and search for the cpu mhz
1162                 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1163 #else
1164 #if defined(__alpha)
1165                 // different on alpha
1166                 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1167 #else
1168                 // this is different on ppc for some reason
1169                 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1170 #endif // defined(__alpha)
1171 #endif // defined(__i386) || defined(__x86_64)
1172
1173                         // copy just the number
1174                         strcpy(frequency, strchr(s, ':') + 2);
1175 #if defined(__alpha)
1176                         // strip " est.\n"
1177                         frequency[strlen(frequency) - 6] = '\0';
1178                         // kernel reports in Hz
1179                         freq = strtod(frequency, NULL) / 1000000;
1180 #else
1181                         // strip \n
1182                         frequency[strlen(frequency) - 1] = '\0';
1183                         freq = strtod(frequency, NULL);
1184 #endif
1185                         break;
1186                 }
1187                 if (strncmp(s, "processor", 9) == 0) {
1188                         cpu--;
1189                         continue;
1190                 }
1191         }
1192
1193         fclose(f);
1194         snprintf(p_client_buffer, client_buffer_size, p_format,
1195                 (float) freq / divisor);
1196         return 1;
1197 }
1198
1199 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1200
1201 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1202  * like this:
1203 # frequency voltage
1204 1800000 1340
1205 1600000 1292
1206 1400000 1100
1207 1200000 988
1208 1000000 1116
1209 800000 1004
1210 600000 988
1211  * Peter Tarjan (ptarjan@citromail.hu) */
1212
1213 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1214 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1215                 const char *p_format, int divisor, unsigned int cpu)
1216 {
1217         FILE *f;
1218         char s[256];
1219         int freq = 0;
1220         int voltage = 0;
1221         char current_freq_file[128];
1222         int freq_comp = 0;
1223
1224         /* build the voltage file name */
1225         cpu--;
1226         snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1227                 CPUFREQ_POSTFIX);
1228
1229         if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1230                         || divisor <= 0) {
1231                 return 0;
1232         }
1233
1234         /* read the current cpu frequency from the /sys node */
1235         f = fopen(current_freq_file, "r");
1236         if (f) {
1237                 if (fgets(s, sizeof(s), f)) {
1238                         s[strlen(s) - 1] = '\0';
1239                         freq = strtod(s, NULL);
1240                 }
1241                 fclose(f);
1242         } else {
1243                 fprintf(stderr, "Conky: Failed to access '%s' at ", current_freq_file);
1244                 perror("get_voltage()");
1245                 if (f) {
1246                         fclose(f);
1247                 }
1248                 return 0;
1249         }
1250
1251         snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1252                 CPUFREQ_VOLTAGE);
1253
1254         /* use the current cpu frequency to find the corresponding voltage */
1255         f = fopen(current_freq_file, "r");
1256
1257         if (f) {
1258                 while (!feof(f)) {
1259                         char line[256];
1260
1261                         if (fgets(line, 255, f) == NULL) {
1262                                 break;
1263                         }
1264                         sscanf(line, "%d %d", &freq_comp, &voltage);
1265                         if (freq_comp == freq) {
1266                                 break;
1267                         }
1268                 }
1269                 fclose(f);
1270         } else {
1271                 fprintf(stderr, "Conky: Failed to access '%s' at ", current_freq_file);
1272                 perror("get_voltage()");
1273                 if (f) {
1274                         fclose(f);
1275                 }
1276                 return 0;
1277         }
1278         snprintf(p_client_buffer, client_buffer_size, p_format,
1279                 (float) voltage / divisor);
1280         return 1;
1281 }
1282
1283 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1284
1285 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1286 {
1287         static int rep = 0;
1288         char buf[256];
1289         char buf2[256];
1290         FILE *fp;
1291
1292         if (!p_client_buffer || client_buffer_size <= 0) {
1293                 return;
1294         }
1295
1296         /* yeah, slow... :/ */
1297         if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1298                 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1299                 return;
1300         }
1301
1302         snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1303
1304         fp = open_file(buf2, &rep);
1305         if (!fp) {
1306                 snprintf(p_client_buffer, client_buffer_size,
1307                         "can't open fan's state file");
1308                 return;
1309         }
1310         memset(buf, 0, sizeof(buf));
1311         fscanf(fp, "%*s %99s", buf);
1312         fclose(fp);
1313
1314         snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1315 }
1316
1317 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1318
1319 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1320 {
1321         static int rep = 0;
1322         char buf[256];
1323         char buf2[256];
1324         FILE *fp;
1325
1326         if (!p_client_buffer || client_buffer_size <= 0) {
1327                 return;
1328         }
1329
1330         /* yeah, slow... :/ */
1331         if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1332                 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1333                 return;
1334         }
1335
1336         snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1337
1338         fp = open_file(buf2, &rep);
1339         if (!fp) {
1340                 snprintf(p_client_buffer, client_buffer_size,
1341                         "No ac adapter found.... where is it?");
1342                 return;
1343         }
1344         memset(buf, 0, sizeof(buf));
1345         fscanf(fp, "%*s %99s", buf);
1346         fclose(fp);
1347
1348         snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1349 }
1350
1351 /*
1352 /proc/acpi/thermal_zone/THRM/cooling_mode
1353 cooling mode:            active
1354 /proc/acpi/thermal_zone/THRM/polling_frequency
1355 <polling disabled>
1356 /proc/acpi/thermal_zone/THRM/state
1357 state:                   ok
1358 /proc/acpi/thermal_zone/THRM/temperature
1359 temperature:             45 C
1360 /proc/acpi/thermal_zone/THRM/trip_points
1361 critical (S5):           73 C
1362 passive:                 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1363 */
1364
1365 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1366 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1367
1368 int open_acpi_temperature(const char *name)
1369 {
1370         char path[256];
1371         char buf[256];
1372         int fd;
1373
1374         if (name == NULL || strcmp(name, "*") == 0) {
1375                 static int rep = 0;
1376
1377                 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1378                         return -1;
1379                 }
1380                 name = buf;
1381         }
1382
1383         snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1384
1385         fd = open(path, O_RDONLY);
1386         if (fd < 0) {
1387                 ERR("can't open '%s': %s", path, strerror(errno));
1388         }
1389
1390         return fd;
1391 }
1392
1393 static double last_acpi_temp;
1394 static double last_acpi_temp_time;
1395
1396 double get_acpi_temperature(int fd)
1397 {
1398         if (fd <= 0) {
1399                 return 0;
1400         }
1401
1402         /* don't update acpi temperature too often */
1403         if (current_update_time - last_acpi_temp_time < 11.32) {
1404                 return last_acpi_temp;
1405         }
1406         last_acpi_temp_time = current_update_time;
1407
1408         /* seek to beginning */
1409         lseek(fd, 0, SEEK_SET);
1410
1411         /* read */
1412         {
1413                 char buf[256];
1414                 int n;
1415
1416                 n = read(fd, buf, 255);
1417                 if (n < 0) {
1418                         ERR("can't read fd %d: %s", fd, strerror(errno));
1419                 } else {
1420                         buf[n] = '\0';
1421                         sscanf(buf, "temperature: %lf", &last_acpi_temp);
1422                 }
1423         }
1424
1425         return last_acpi_temp;
1426 }
1427
1428 /*
1429 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1430 present:                 yes
1431 design capacity:         4400 mAh
1432 last full capacity:      4064 mAh
1433 battery technology:      rechargeable
1434 design voltage:          14800 mV
1435 design capacity warning: 300 mAh
1436 design capacity low:     200 mAh
1437 capacity granularity 1:  32 mAh
1438 capacity granularity 2:  32 mAh
1439 model number:            02KT
1440 serial number:           16922
1441 battery type:            LION
1442 OEM info:                SANYO
1443 */
1444
1445 /*
1446 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1447 present:                 yes
1448 capacity state:          ok
1449 charging state:          unknown
1450 present rate:            0 mA
1451 remaining capacity:      4064 mAh
1452 present voltage:         16608 mV
1453 */
1454
1455 /*
1456 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1457 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1458 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1459 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1460 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1461
1462 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1463 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1464
1465 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1466 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1467 */
1468
1469 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1470   Linux 2.6.24 onwards battery info is in
1471   /sys/class/power_supply/BAT0/
1472   On my system I get the following.
1473         /sys/class/power_supply/BAT0/uevent:
1474         PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1475         PHYSDEVBUS=acpi
1476         PHYSDEVDRIVER=battery
1477         POWER_SUPPLY_NAME=BAT0
1478         POWER_SUPPLY_TYPE=Battery
1479         POWER_SUPPLY_STATUS=Discharging
1480         POWER_SUPPLY_PRESENT=1
1481         POWER_SUPPLY_TECHNOLOGY=Li-ion
1482         POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1483         POWER_SUPPLY_VOLTAGE_NOW=10780000
1484         POWER_SUPPLY_CURRENT_NOW=13970000
1485         POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1486         POWER_SUPPLY_ENERGY_FULL=27370000
1487         POWER_SUPPLY_ENERGY_NOW=11810000
1488         POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1489         POWER_SUPPLY_MANUFACTURER=Panasonic
1490   On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1491 */
1492
1493 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1494 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1495 #define APM_PATH "/proc/apm"
1496 #define MAX_BATTERY_COUNT 4
1497
1498 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1499 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1500 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1501
1502 static int batteries_initialized = 0;
1503 static char batteries[MAX_BATTERY_COUNT][32];
1504
1505 static int acpi_last_full[MAX_BATTERY_COUNT];
1506 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1507
1508 /* e.g. "charging 75%" */
1509 static char last_battery_str[MAX_BATTERY_COUNT][64];
1510 /* e.g. "3h 15m" */
1511 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1512
1513 static double last_battery_time[MAX_BATTERY_COUNT];
1514
1515 static int last_battery_perct[MAX_BATTERY_COUNT];
1516 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1517
1518 void init_batteries(void)
1519 {
1520         int idx;
1521
1522         if (batteries_initialized) {
1523                 return;
1524         }
1525         for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1526                 batteries[idx][0] = '\0';
1527         }
1528         batteries_initialized = 1;
1529 }
1530
1531 int get_battery_idx(const char *bat)
1532 {
1533         int idx;
1534
1535         for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1536                 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1537                         break;
1538                 }
1539         }
1540
1541         /* if not found, enter a new entry */
1542         if (!strlen(batteries[idx])) {
1543                 snprintf(batteries[idx], 31, "%s", bat);
1544         }
1545
1546         return idx;
1547 }
1548
1549 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1550
1551 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1552 {
1553         static int idx, rep = 0, rep2 = 0;
1554         char acpi_path[128];
1555         char sysfs_path[128];
1556
1557         snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1558         snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1559
1560         init_batteries();
1561
1562         idx = get_battery_idx(bat);
1563
1564         /* don't update battery too often */
1565         if (current_update_time - last_battery_time[idx] < 29.5) {
1566                 set_return_value(buffer, n, item, idx);
1567                 return;
1568         }
1569
1570         last_battery_time[idx] = current_update_time;
1571
1572         memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1573         memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1574
1575         /* first try SYSFS if that fails try ACPI */
1576
1577         if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1578                 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1579                 rep = 0;
1580         }
1581
1582         if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1583                 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1584         }
1585
1586         if (sysfs_bat_fp[idx] != NULL) {
1587                 /* SYSFS */
1588                 int present_rate = -1;
1589                 int remaining_capacity = -1;
1590                 char charging_state[64];
1591                 char present[4];
1592
1593                 strcpy(charging_state, "unknown");
1594
1595                 while (!feof(sysfs_bat_fp[idx])) {
1596                         char buf[256];
1597                         if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1598                                 break;
1599
1600                         /* let's just hope units are ok */
1601                         if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1602                                 strcpy(present, "yes");
1603                         else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1604                                 strcpy(present, "no");
1605                         else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1606                                 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1607                         /* present_rate is not the same as the
1608                         current flowing now but it is the same value
1609                         which was used in the past. so we continue
1610                         the tradition! */
1611                         else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1612                                 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1613                         else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1614                                 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1615                         else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1616                                 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1617                         else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1618                                 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1619                         else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1620                                 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1621                 }
1622
1623                 fclose(sysfs_bat_fp[idx]);
1624                 sysfs_bat_fp[idx] = NULL;
1625
1626                 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1627                 if (remaining_capacity > acpi_last_full[idx])
1628                         acpi_last_full[idx] = remaining_capacity;  /* normalize to 100% */
1629
1630                 /* not present */
1631                 if (strcmp(present, "No") == 0) {
1632                         strncpy(last_battery_str[idx], "not present", 64);
1633                 }
1634                 /* charging */
1635                 else if (strcmp(charging_state, "Charging") == 0) {
1636                         if (acpi_last_full[idx] != 0 && present_rate > 0) {
1637                                 /* e.g. charging 75% */
1638                                 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1639                                         (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1640                                 /* e.g. 2h 37m */
1641                                 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1642                                               (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1643                         } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1644                                 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1645                                         (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1646                                 snprintf(last_battery_time_str[idx],
1647                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1648                         } else {
1649                                 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1650                                 snprintf(last_battery_time_str[idx],
1651                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1652                         }
1653                 }
1654                 /* discharging */
1655                 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1656                         if (present_rate > 0) {
1657                                 /* e.g. discharging 35% */
1658                                 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1659                                         (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1660                                 /* e.g. 1h 12m */
1661                                 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1662                                               (long) (((float) remaining_capacity / present_rate) * 3600));
1663                         } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1664                                 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1665                                 snprintf(last_battery_time_str[idx],
1666                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1667                         } else {
1668                                 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1669                                         "discharging %d%%",
1670                                         (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1671                                 snprintf(last_battery_time_str[idx],
1672                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1673                         }
1674                 }
1675                 /* charged */
1676                 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1677                 else if (strncmp(charging_state, "Charged", 64) == 0) {
1678                                 /* Below happens with the second battery on my X40,
1679                                  * when the second one is empty and the first one
1680                                  * being charged. */
1681                                 if (remaining_capacity == 0)
1682                                         strcpy(last_battery_str[idx], "empty");
1683                                 else
1684                                         strcpy(last_battery_str[idx], "charged");
1685                 }
1686                 /* unknown, probably full / AC */
1687                 else {
1688                         if (acpi_last_full[idx] != 0
1689                             && remaining_capacity != acpi_last_full[idx])
1690                                 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1691                                         (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1692                         else
1693                                 strncpy(last_battery_str[idx], "AC", 64);
1694                 }
1695         } else if (acpi_bat_fp[idx] != NULL) {
1696                 /* ACPI */
1697                 int present_rate = -1;
1698                 int remaining_capacity = -1;
1699                 char charging_state[64];
1700                 char present[4];
1701
1702                 /* read last full capacity if it's zero */
1703                 if (acpi_last_full[idx] == 0) {
1704                         static int rep3 = 0;
1705                         char path[128];
1706                         FILE *fp;
1707
1708                         snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1709                         fp = open_file(path, &rep3);
1710                         if (fp != NULL) {
1711                                 while (!feof(fp)) {
1712                                         char b[256];
1713
1714                                         if (fgets(b, 256, fp) == NULL) {
1715                                                 break;
1716                                         }
1717                                         if (sscanf(b, "last full capacity: %d",
1718                                                         &acpi_last_full[idx]) != 0) {
1719                                                 break;
1720                                         }
1721                                 }
1722
1723                                 fclose(fp);
1724                         }
1725                 }
1726
1727                 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1728
1729                 strcpy(charging_state, "unknown");
1730
1731                 while (!feof(acpi_bat_fp[idx])) {
1732                         char buf[256];
1733
1734                         if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1735                                 break;
1736                         }
1737
1738                         /* let's just hope units are ok */
1739                         if (strncmp(buf, "present:", 8) == 0) {
1740                                 sscanf(buf, "present: %4s", present);
1741                         } else if (strncmp(buf, "charging state:", 15) == 0) {
1742                                 sscanf(buf, "charging state: %63s", charging_state);
1743                         } else if (strncmp(buf, "present rate:", 13) == 0) {
1744                                 sscanf(buf, "present rate: %d", &present_rate);
1745                         } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1746                                 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1747                         }
1748                 }
1749                 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1750                 if (remaining_capacity > acpi_last_full[idx]) {
1751                         /* normalize to 100% */
1752                         acpi_last_full[idx] = remaining_capacity;
1753                 }
1754
1755                 /* not present */
1756                 if (strcmp(present, "no") == 0) {
1757                         strncpy(last_battery_str[idx], "not present", 64);
1758                 /* charging */
1759                 } else if (strcmp(charging_state, "charging") == 0) {
1760                         if (acpi_last_full[idx] != 0 && present_rate > 0) {
1761                                 /* e.g. charging 75% */
1762                                 snprintf(last_battery_str[idx],
1763                                         sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1764                                         (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1765                                 /* e.g. 2h 37m */
1766                                 format_seconds(last_battery_time_str[idx],
1767                                         sizeof(last_battery_time_str[idx]) - 1,
1768                                         (long) (((acpi_last_full[idx] - remaining_capacity) *
1769                                         3600) / present_rate));
1770                         } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1771                                 snprintf(last_battery_str[idx],
1772                                         sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1773                                         (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1774                                 snprintf(last_battery_time_str[idx],
1775                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1776                         } else {
1777                                 strncpy(last_battery_str[idx], "charging",
1778                                         sizeof(last_battery_str[idx]) - 1);
1779                                 snprintf(last_battery_time_str[idx],
1780                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1781                         }
1782                 /* discharging */
1783                 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1784                         if (present_rate > 0) {
1785                                 /* e.g. discharging 35% */
1786                                 snprintf(last_battery_str[idx],
1787                                         sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1788                                         (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1789                                 /* e.g. 1h 12m */
1790                                 format_seconds(last_battery_time_str[idx],
1791                                         sizeof(last_battery_time_str[idx]) - 1,
1792                                         (long) ((remaining_capacity * 3600) / present_rate));
1793                         } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1794                                 snprintf(last_battery_str[idx],
1795                                         sizeof(last_battery_str[idx]) - 1, "full");
1796                                 snprintf(last_battery_time_str[idx],
1797                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1798                         } else {
1799                                 snprintf(last_battery_str[idx],
1800                                         sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1801                                         (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1802                                 snprintf(last_battery_time_str[idx],
1803                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1804                         }
1805                 /* charged */
1806                 } else if (strncmp(charging_state, "charged", 64) == 0) {
1807                         /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1808                         /* Below happens with the second battery on my X40,
1809                          * when the second one is empty and the first one being charged. */
1810                         if (remaining_capacity == 0) {
1811                                 strcpy(last_battery_str[idx], "empty");
1812                         } else {
1813                                 strcpy(last_battery_str[idx], "charged");
1814                         }
1815                 /* unknown, probably full / AC */
1816                 } else {
1817                         if (acpi_last_full[idx] != 0
1818                                         && remaining_capacity != acpi_last_full[idx]) {
1819                                 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1820                                         (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1821                         } else {
1822                                 strncpy(last_battery_str[idx], "AC", 64);
1823                         }
1824                 }
1825         } else {
1826                 /* APM */
1827                 if (apm_bat_fp[idx] == NULL) {
1828                         apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1829                 }
1830
1831                 if (apm_bat_fp[idx] != NULL) {
1832                         unsigned int ac, status, flag;
1833                         int life;
1834
1835                         fscanf(apm_bat_fp[idx], "%*s %*s %*x %x   %x       %x     %d%%",
1836                                 &ac, &status, &flag, &life);
1837
1838                         if (life == -1) {
1839                                 /* could check now that there is ac */
1840                                 snprintf(last_battery_str[idx], 64, "AC");
1841
1842                         /* could check that status == 3 here? */
1843                         } else if (ac && life != 100) {
1844                                 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1845                         } else {
1846                                 snprintf(last_battery_str[idx], 64, "%d%%", life);
1847                         }
1848
1849                         /* it seemed to buffer it so file must be closed (or could use
1850                          * syscalls directly but I don't feel like coding it now) */
1851                         fclose(apm_bat_fp[idx]);
1852                         apm_bat_fp[idx] = NULL;
1853                 }
1854         }
1855         set_return_value(buffer, n, item, idx);
1856 }
1857
1858 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1859 {
1860         switch (item) {
1861                 case BATTERY_STATUS:
1862                         snprintf(buffer, n, "%s", last_battery_str[idx]);
1863                         break;
1864                 case BATTERY_TIME:
1865                         snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1866                         break;
1867                 default:
1868                         break;
1869         }
1870 }
1871
1872 int get_battery_perct(const char *bat)
1873 {
1874         static int rep = 0;
1875         int idx;
1876         char acpi_path[128];
1877         char sysfs_path[128];
1878         int remaining_capacity = -1;
1879
1880         snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1881         snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1882
1883         init_batteries();
1884
1885         idx = get_battery_idx(bat);
1886
1887         /* don't update battery too often */
1888         if (current_update_time - last_battery_perct_time[idx] < 30) {
1889                 return last_battery_perct[idx];
1890         }
1891         last_battery_perct_time[idx] = current_update_time;
1892
1893         /* Only check for SYSFS or ACPI */
1894
1895         if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1896                 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1897                 rep = 0;
1898         }
1899
1900         if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1901                 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1902         }
1903
1904         if (sysfs_bat_fp[idx] != NULL) {
1905                 /* SYSFS */
1906                 while (!feof(sysfs_bat_fp[idx])) {
1907                         char buf[256];
1908                         if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1909                                 break;
1910
1911                         if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1912                                 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1913                         } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1914                                 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1915                         } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1916                                 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1917                         } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1918                                 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1919                         }
1920                 }
1921
1922                 fclose(sysfs_bat_fp[idx]);
1923                 sysfs_bat_fp[idx] = NULL;
1924
1925         } else if (acpi_bat_fp[idx] != NULL) {
1926                 /* ACPI */
1927                 /* read last full capacity if it's zero */
1928                 if (acpi_design_capacity[idx] == 0) {
1929                         static int rep2;
1930                         char path[128];
1931                         FILE *fp;
1932
1933                         snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1934                         fp = open_file(path, &rep2);
1935                         if (fp != NULL) {
1936                                 while (!feof(fp)) {
1937                                         char b[256];
1938
1939                                         if (fgets(b, 256, fp) == NULL) {
1940                                                 break;
1941                                         }
1942                                         if (sscanf(b, "last full capacity: %d",
1943                                                                 &acpi_design_capacity[idx]) != 0) {
1944                                                 break;
1945                                         }
1946                                 }
1947                                 fclose(fp);
1948                         }
1949                 }
1950
1951                 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1952
1953                 while (!feof(acpi_bat_fp[idx])) {
1954                         char buf[256];
1955
1956                         if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1957                                 break;
1958                         }
1959
1960                         if (buf[0] == 'r') {
1961                                 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1962                         }
1963                 }
1964         }
1965         if (remaining_capacity < 0) {
1966                 return 0;
1967         }
1968         /* compute the battery percentage */
1969         last_battery_perct[idx] =
1970                 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
1971         return last_battery_perct[idx];
1972 }
1973
1974 int get_battery_perct_bar(const char *bar)
1975 {
1976         int idx;
1977
1978         get_battery_perct(bar);
1979         idx = get_battery_idx(bar);
1980         return (int) (last_battery_perct[idx] * 2.56 - 1);
1981 }
1982
1983 /* On Apple powerbook and ibook:
1984 $ cat /proc/pmu/battery_0
1985 flags      : 00000013
1986 charge     : 3623
1987 max_charge : 3720
1988 current    : 388
1989 voltage    : 16787
1990 time rem.  : 900
1991 $ cat /proc/pmu/info
1992 PMU driver version     : 2
1993 PMU firmware version   : 0c
1994 AC Power               : 1
1995 Battery count          : 1
1996 */
1997
1998 /* defines as in <linux/pmu.h> */
1999 #define PMU_BATT_PRESENT                0x00000001
2000 #define PMU_BATT_CHARGING               0x00000002
2001
2002 static FILE *pmu_battery_fp;
2003 static FILE *pmu_info_fp;
2004 static char pb_battery_info[3][32];
2005 static double pb_battery_info_update;
2006
2007 #define PMU_PATH "/proc/pmu"
2008 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2009 {
2010         static int rep = 0;
2011         const char *batt_path = PMU_PATH "/battery_0";
2012         const char *info_path = PMU_PATH "/info";
2013         unsigned int flags;
2014         int charge, max_charge, ac = -1;
2015         long timeval = -1;
2016
2017         /* don't update battery too often */
2018         if (current_update_time - pb_battery_info_update < 29.5) {
2019                 snprintf(buffer, n, "%s", pb_battery_info[i]);
2020                 return;
2021         }
2022         pb_battery_info_update = current_update_time;
2023
2024         if (pmu_battery_fp == NULL) {
2025                 pmu_battery_fp = open_file(batt_path, &rep);
2026         }
2027
2028         if (pmu_battery_fp != NULL) {
2029                 rewind(pmu_battery_fp);
2030                 while (!feof(pmu_battery_fp)) {
2031                         char buf[32];
2032
2033                         if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2034                                 break;
2035                         }
2036
2037                         if (buf[0] == 'f') {
2038                                 sscanf(buf, "flags      : %8x", &flags);
2039                         } else if (buf[0] == 'c' && buf[1] == 'h') {
2040                                 sscanf(buf, "charge     : %d", &charge);
2041                         } else if (buf[0] == 'm') {
2042                                 sscanf(buf, "max_charge : %d", &max_charge);
2043                         } else if (buf[0] == 't') {
2044                                 sscanf(buf, "time rem.  : %ld", &timeval);
2045                         }
2046                 }
2047         }
2048         if (pmu_info_fp == NULL) {
2049                 pmu_info_fp = open_file(info_path, &rep);
2050         }
2051
2052         if (pmu_info_fp != NULL) {
2053                 rewind(pmu_info_fp);
2054                 while (!feof(pmu_info_fp)) {
2055                         char buf[32];
2056
2057                         if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2058                                 break;
2059                         }
2060                         if (buf[0] == 'A') {
2061                                 sscanf(buf, "AC Power               : %d", &ac);
2062                         }
2063                 }
2064         }
2065         /* update status string */
2066         if ((ac && !(flags & PMU_BATT_PRESENT))) {
2067                 strcpy(pb_battery_info[PB_BATT_STATUS], "AC");
2068         } else if (ac && (flags & PMU_BATT_PRESENT)
2069                         && !(flags & PMU_BATT_CHARGING)) {
2070                 strcpy(pb_battery_info[PB_BATT_STATUS], "charged");
2071         } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2072                 strcpy(pb_battery_info[PB_BATT_STATUS], "charging");
2073         } else {
2074                 strcpy(pb_battery_info[PB_BATT_STATUS], "discharging");
2075         }
2076
2077         /* update percentage string */
2078         if (timeval == 0) {
2079                 pb_battery_info[PB_BATT_PERCENT][0] = 0;
2080         } else {
2081                 snprintf(pb_battery_info[PB_BATT_PERCENT],
2082                         sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2083                         (charge * 100) / max_charge);
2084         }
2085
2086         /* update time string */
2087         if (timeval == 0) {                     /* fully charged or battery not present */
2088                 pb_battery_info[PB_BATT_TIME][0] = 0;
2089         } else if (timeval < 60 * 60) { /* don't show secs */
2090                 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2091                         sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2092         } else {
2093                 format_seconds(pb_battery_info[PB_BATT_TIME],
2094                         sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2095         }
2096
2097         snprintf(buffer, n, "%s", pb_battery_info[i]);
2098 }
2099
2100 void update_top(void)
2101 {
2102         show_nice_processes = 1;
2103         process_find_top(info.cpu, info.memu);
2104         info.first_process = get_first_process();
2105 }
2106
2107 /* Here come the IBM ACPI-specific things. For reference, see
2108  * http://ibm-acpi.sourceforge.net/README
2109  * If IBM ACPI is installed, /proc/acpi/ibm contains the following files:
2110 bay
2111 beep
2112 bluetooth
2113 brightness
2114 cmos
2115 dock
2116 driver
2117 ecdump
2118 fan
2119 hotkey
2120 led
2121 light
2122 thermal
2123 video
2124 volume
2125  * The content of these files is described in detail in the aforementioned
2126  * README - some of them also in the following functions accessing them.
2127  * Peter Tarjan (ptarjan@citromail.hu) */
2128
2129 #define IBM_ACPI_DIR "/proc/acpi/ibm"
2130
2131 /* get fan speed on IBM/Lenovo laptops running the ibm acpi.
2132  * /proc/acpi/ibm/fan looks like this (3 lines):
2133 status:         disabled
2134 speed:          2944
2135 commands:       enable, disable
2136  * Peter Tarjan (ptarjan@citromail.hu) */
2137
2138 void get_ibm_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
2139 {
2140         FILE *fp;
2141         unsigned int speed = 0;
2142         char fan[128];
2143
2144         if (!p_client_buffer || client_buffer_size <= 0) {
2145                 return;
2146         }
2147
2148         snprintf(fan, 127, "%s/fan", IBM_ACPI_DIR);
2149
2150         fp = fopen(fan, "r");
2151         if (fp != NULL) {
2152                 while (!feof(fp)) {
2153                         char line[256];
2154
2155                         if (fgets(line, 255, fp) == NULL) {
2156                                 break;
2157                         }
2158                         if (sscanf(line, "speed: %u", &speed)) {
2159                                 break;
2160                         }
2161                 }
2162         } else {
2163                 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2164                         "ibm* from your Conky config file.", fan, strerror(errno));
2165         }
2166
2167         fclose(fp);
2168         snprintf(p_client_buffer, client_buffer_size, "%d", speed);
2169 }
2170
2171 /* get the measured temperatures from the temperature sensors
2172  * on IBM/Lenovo laptops running the ibm acpi.
2173  * There are 8 values in /proc/acpi/ibm/thermal, and according to
2174  * http://ibm-acpi.sourceforge.net/README
2175  * these mean the following (at least on an IBM R51...)
2176  * 0:  CPU (also on the T series laptops)
2177  * 1:  Mini PCI Module (?)
2178  * 2:  HDD (?)
2179  * 3:  GPU (also on the T series laptops)
2180  * 4:  Battery (?)
2181  * 5:  N/A
2182  * 6:  Battery (?)
2183  * 7:  N/A
2184  * I'm not too sure about those with the question mark, but the values I'm
2185  * reading from *my* thermal file (on a T42p) look realistic for the
2186  * hdd and the battery.
2187  * #5 and #7 are always -128.
2188  * /proc/acpi/ibm/thermal looks like this (1 line):
2189 temperatures:   41 43 31 46 33 -128 29 -128
2190  * Peter Tarjan (ptarjan@citromail.hu) */
2191
2192 static double last_ibm_acpi_temp_time;
2193 void get_ibm_acpi_temps(void)
2194 {
2195
2196         FILE *fp;
2197         char thermal[128];
2198
2199         /* don't update too often */
2200         if (current_update_time - last_ibm_acpi_temp_time < 10.00) {
2201                 return;
2202         }
2203         last_ibm_acpi_temp_time = current_update_time;
2204
2205         /* if (!p_client_buffer || client_buffer_size <= 0) {
2206                 return;
2207         } */
2208
2209         snprintf(thermal, 127, "%s/thermal", IBM_ACPI_DIR);
2210         fp = fopen(thermal, "r");
2211
2212         if (fp != NULL) {
2213                 while (!feof(fp)) {
2214                         char line[256];
2215
2216                         if (fgets(line, 255, fp) == NULL) {
2217                                 break;
2218                         }
2219                         if (sscanf(line, "temperatures: %d %d %d %d %d %d %d %d",
2220                                         &ibm_acpi.temps[0], &ibm_acpi.temps[1], &ibm_acpi.temps[2],
2221                                         &ibm_acpi.temps[3], &ibm_acpi.temps[4], &ibm_acpi.temps[5],
2222                                         &ibm_acpi.temps[6], &ibm_acpi.temps[7])) {
2223                                 break;
2224                         }
2225                 }
2226         } else {
2227                 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2228                         "ibm* from your Conky config file.", thermal, strerror(errno));
2229         }
2230
2231         fclose(fp);
2232 }
2233
2234 /* get volume (0-14) on IBM/Lenovo laptops running the ibm acpi.
2235  * "Volume" here is none of the mixer volumes, but a "master of masters"
2236  * volume adjusted by the IBM volume keys.
2237  * /proc/acpi/ibm/fan looks like this (4 lines):
2238 level:          4
2239 mute:           off
2240 commands:       up, down, mute
2241 commands:       level <level> (<level> is 0-15)
2242  * Peter Tarjan (ptarjan@citromail.hu) */
2243
2244 void get_ibm_acpi_volume(char *p_client_buffer, size_t client_buffer_size)
2245 {
2246         FILE *fp;
2247         char volume[128];
2248         unsigned int vol = -1;
2249         char mute[3] = "";
2250
2251         if (!p_client_buffer || client_buffer_size <= 0) {
2252                 return;
2253         }
2254
2255         snprintf(volume, 127, "%s/volume", IBM_ACPI_DIR);
2256
2257         fp = fopen(volume, "r");
2258         if (fp != NULL) {
2259                 while (!feof(fp)) {
2260                         char line[256];
2261                         unsigned int read_vol = -1;
2262
2263                         if (fgets(line, 255, fp) == NULL) {
2264                                 break;
2265                         }
2266                         if (sscanf(line, "level: %u", &read_vol)) {
2267                                 vol = read_vol;
2268                                 continue;
2269                         }
2270                         if (sscanf(line, "mute: %s", mute)) {
2271                                 break;
2272                         }
2273                 }
2274         } else {
2275                 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2276                         "ibm* from your Conky config file.", volume, strerror(errno));
2277         }
2278
2279         fclose(fp);
2280
2281         if (strcmp(mute, "on") == 0) {
2282                 snprintf(p_client_buffer, client_buffer_size, "%s", "mute");
2283                 return;
2284         } else {
2285                 snprintf(p_client_buffer, client_buffer_size, "%d", vol);
2286                 return;
2287         }
2288 }
2289
2290 /* static FILE *fp = NULL; */
2291
2292 /* get LCD brightness on IBM/Lenovo laptops running the ibm acpi.
2293  * /proc/acpi/ibm/brightness looks like this (3 lines):
2294 level:          7
2295 commands:       up, down
2296 commands:       level <level> (<level> is 0-7)
2297  * Peter Tarjan (ptarjan@citromail.hu) */
2298
2299 void get_ibm_acpi_brightness(char *p_client_buffer, size_t client_buffer_size)
2300 {
2301         FILE *fp;
2302         unsigned int brightness = 0;
2303         char filename[128];
2304
2305         if (!p_client_buffer || client_buffer_size <= 0) {
2306                 return;
2307         }
2308
2309         snprintf(filename, 127, "%s/brightness", IBM_ACPI_DIR);
2310
2311         fp = fopen(filename, "r");
2312         if (fp != NULL) {
2313                 while (!feof(fp)) {
2314                         char line[256];
2315
2316                         if (fgets(line, 255, fp) == NULL) {
2317                                 break;
2318                         }
2319                         if (sscanf(line, "level: %u", &brightness)) {
2320                                 break;
2321                         }
2322                 }
2323         } else {
2324                 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2325                         "ibm* from your Conky config file.", filename, strerror(errno));
2326         }
2327
2328         fclose(fp);
2329
2330         snprintf(p_client_buffer, client_buffer_size, "%d", brightness);
2331 }
2332
2333 void update_entropy(void)
2334 {
2335         static int rep = 0;
2336         const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2337         const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2338         FILE *fp1, *fp2;
2339
2340         info.entropy.entropy_avail = 0;
2341         info.entropy.poolsize = 0;
2342
2343         if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2344                 return;
2345         }
2346
2347         if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2348                 fclose(fp1);
2349                 return;
2350         }
2351
2352         fscanf(fp1, "%u", &info.entropy.entropy_avail);
2353         fscanf(fp2, "%u", &info.entropy.poolsize);
2354
2355         fclose(fp1);
2356         fclose(fp2);
2357
2358         info.mask |= (1 << INFO_ENTROPY);
2359 }
2360
2361 const char *get_disk_protect_queue(const char *disk)
2362 {
2363         FILE *fp;
2364         char path[128];
2365         int state;
2366
2367         snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2368         if ((fp = fopen(path, "r")) == NULL)
2369                 return "n/a   ";
2370         if (fscanf(fp, "%d\n", &state) != 1) {
2371                 fclose(fp);
2372                 return "failed";
2373         }
2374         fclose(fp);
2375         return state ? "frozen" : "free  ";
2376 }
2377