Limit $battery_percent to 100 (bug 2145006)
[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[18];
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 + 1);
384                 if(NULL != ns->addrs)
385                         memset(ns->addrs, 0, 17 * 16 + 1); /* 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                         if (isdigit(buf[3])) {
679                                 idx = atoi(&buf[3]);
680                         } else {
681                                 idx = 0;
682                         }
683                         sscanf(buf, stat_template, &(cpu[idx].cpu_user),
684                                 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
685                                 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
686                                 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
687                                 &(cpu[idx].cpu_steal));
688
689                         cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
690                                 cpu[idx].cpu_system + cpu[idx].cpu_idle +
691                                 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
692                                 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
693
694                         cpu[idx].cpu_active_total = cpu[idx].cpu_total -
695                                 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
696                         info.mask |= (1 << INFO_CPU);
697
698                         delta = current_update_time - last_update_time;
699
700                         if (delta <= 0.001) {
701                                 break;
702                         }
703
704                         cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
705                                 cpu[idx].cpu_last_active_total) /
706                                 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
707                         curtmp = 0;
708                         for (i = 0; i < info.cpu_avg_samples; i++) {
709                                 curtmp += cpu[idx].cpu_val[i];
710                         }
711                         /* TESTING -- I've removed this, because I don't think it is right.
712                          * You shouldn't divide by the cpu count here ...
713                          * removing for testing */
714                         /* if (idx == 0) {
715                                 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
716                                         info.cpu_count;
717                         } else {
718                                 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
719                         } */
720                         /* TESTING -- this line replaces the prev. "suspect" if/else */
721                         info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
722
723                         cpu[idx].cpu_last_total = cpu[idx].cpu_total;
724                         cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
725                         for (i = info.cpu_avg_samples - 1; i > 0; i--) {
726                                 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
727                         }
728                 }
729         }
730         fclose(stat_fp);
731 }
732
733 void update_running_processes(void)
734 {
735         update_stat();
736 }
737
738 void update_cpu_usage(void)
739 {
740         update_stat();
741 }
742
743 void update_load_average(void)
744 {
745 #ifdef HAVE_GETLOADAVG
746         if (!prefer_proc) {
747                 double v[3];
748
749                 getloadavg(v, 3);
750                 info.loadavg[0] = (float) v[0];
751                 info.loadavg[1] = (float) v[1];
752                 info.loadavg[2] = (float) v[2];
753         } else
754 #endif
755         {
756                 static int rep = 0;
757                 FILE *fp;
758
759                 if (!(fp = open_file("/proc/loadavg", &rep))) {
760                         info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
761                         return;
762                 }
763                 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
764                         &info.loadavg[2]);
765                 fclose(fp);
766         }
767         info.mask |= (1 << INFO_LOADAVG);
768 }
769
770 #define PROC_I8K "/proc/i8k"
771 #define I8K_DELIM " "
772 static char *i8k_procbuf = NULL;
773 void update_i8k(void)
774 {
775         FILE *fp;
776
777         if (!i8k_procbuf) {
778                 i8k_procbuf = (char *) malloc(128 * sizeof(char));
779         }
780         if ((fp = fopen(PROC_I8K, "r")) == NULL) {
781                 CRIT_ERR("/proc/i8k doesn't exist! use insmod to make sure the kernel "
782                         "driver is loaded...");
783         }
784
785         memset(&i8k_procbuf[0], 0, 128);
786         if (fread(&i8k_procbuf[0], sizeof(char), 128, fp) == 0) {
787                 ERR("something wrong with /proc/i8k...");
788         }
789
790         fclose(fp);
791
792         i8k.version = strtok(&i8k_procbuf[0], I8K_DELIM);
793         i8k.bios = strtok(NULL, I8K_DELIM);
794         i8k.serial = strtok(NULL, I8K_DELIM);
795         i8k.cpu_temp = strtok(NULL, I8K_DELIM);
796         i8k.left_fan_status = strtok(NULL, I8K_DELIM);
797         i8k.right_fan_status = strtok(NULL, I8K_DELIM);
798         i8k.left_fan_rpm = strtok(NULL, I8K_DELIM);
799         i8k.right_fan_rpm = strtok(NULL, I8K_DELIM);
800         i8k.ac_status = strtok(NULL, I8K_DELIM);
801         i8k.buttons_status = strtok(NULL, I8K_DELIM);
802 }
803
804 /***********************************************************/
805 /***********************************************************/
806 /***********************************************************/
807
808 static int no_dots(const struct dirent *d)
809 {
810         if (d->d_name[0] == '.') {
811                 return 0;
812         }
813         return 1;
814 }
815
816 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
817 {
818         struct dirent **namelist;
819         int i, n;
820
821         n = scandir(dir, &namelist, no_dots, alphasort);
822         if (n < 0) {
823                 if (!rep || !*rep) {
824                         ERR("scandir for %s: %s", dir, strerror(errno));
825                         if (rep) {
826                                 *rep = 1;
827                         }
828                 }
829                 return 0;
830         } else {
831                 if (n == 0) {
832                         return 0;
833                 }
834
835                 strncpy(s, namelist[0]->d_name, 255);
836                 s[255] = '\0';
837
838                 for (i = 0; i < n; i++) {
839                         free(namelist[i]);
840                 }
841                 free(namelist);
842
843                 return 1;
844         }
845 }
846
847 int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
848                 int *divisor, char *devtype)
849 {
850         char path[256];
851         char buf[256];
852         int fd;
853         int divfd;
854
855         memset(buf, 0, sizeof(buf));
856
857         /* if device is NULL or *, get first */
858         if (dev == NULL || strcmp(dev, "*") == 0) {
859                 static int rep = 0;
860
861                 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
862                         return -1;
863                 }
864                 dev = buf;
865         }
866
867         if (strcmp(dir, "/sys/class/hwmon/") == 0) {
868                 if (*buf) {
869                         /* buf holds result from get_first_file_in_a_directory() above,
870                          * e.g. "hwmon0" -- append "/device" */
871                         strcat(buf, "/device");
872                 } else {
873                         /* dev holds device number N as a string,
874                          * e.g. "0", -- convert to "hwmon0/device" */
875                         sprintf(buf, "hwmon%s/device", dev);
876                         dev = buf;
877                 }
878         }
879
880         /* change vol to in */
881         if (strcmp(type, "vol") == 0) {
882                 type = "in";
883         }
884
885         if (strcmp(type, "tempf") == 0) {
886                 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, "temp", n);
887         } else {
888                 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
889         }
890         strncpy(devtype, path, 255);
891
892         /* open file */
893         fd = open(path, O_RDONLY);
894         if (fd < 0) {
895                 CRIT_ERR("can't open '%s': %s\nplease check your device or remove this "
896                         "var from "PACKAGE_NAME, path, strerror(errno));
897         }
898
899         if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
900                         || strcmp(type, "tempf") == 0) {
901                 *divisor = 1;
902         } else {
903                 *divisor = 0;
904         }
905         /* fan does not use *_div as a read divisor */
906         if (strcmp("fan", type) == 0) {
907                 return fd;
908         }
909
910         /* test if *_div file exist, open it and use it as divisor */
911         if (strcmp(type, "tempf") == 0) {
912                 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
913         } else {
914                 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
915         }
916
917         divfd = open(path, O_RDONLY);
918         if (divfd > 0) {
919                 /* read integer */
920                 char divbuf[64];
921                 int divn;
922
923                 divn = read(divfd, divbuf, 63);
924                 /* should read until n == 0 but I doubt that kernel will give these
925                  * in multiple pieces. :) */
926                 if (divn < 0) {
927                         ERR("open_sysfs_sensor(): can't read from sysfs");
928                 } else {
929                         divbuf[divn] = '\0';
930                         *divisor = atoi(divbuf);
931                 }
932         }
933
934         close(divfd);
935
936         return fd;
937 }
938
939 double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
940 {
941         int val = 0;
942
943         if (*fd <= 0) {
944                 return 0;
945         }
946
947         lseek(*fd, 0, SEEK_SET);
948
949         /* read integer */
950         {
951                 char buf[64];
952                 int n;
953                 n = read(*fd, buf, 63);
954                 /* should read until n == 0 but I doubt that kernel will give these
955                  * in multiple pieces. :) */
956                 if (n < 0) {
957                         ERR("get_sysfs_info(): read from %s failed\n", devtype);
958                 } else {
959                         buf[n] = '\0';
960                         val = atoi(buf);
961                 }
962         }
963
964         close(*fd);
965         /* open file */
966         *fd = open(devtype, O_RDONLY);
967         if (*fd < 0) {
968                 ERR("can't open '%s': %s", devtype, strerror(errno));
969         }
970
971         /* My dirty hack for computing CPU value
972          * Filedil, from forums.gentoo.org */
973         /* if (strstr(devtype, "temp1_input") != NULL) {
974                 return -15.096 + 1.4893 * (val / 1000.0);
975         } */
976
977         /* divide voltage and temperature by 1000 */
978         /* or if any other divisor is given, use that */
979         if (strcmp(type, "tempf") == 0) {
980                 if (divisor > 1) {
981                         return ((val / divisor + 40) * 9.0 / 5) - 40;
982                 } else if (divisor) {
983                         return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
984                 } else {
985                         return ((val + 40) * 9.0 / 5) - 40;
986                 }
987         } else {
988                 if (divisor > 1) {
989                         return val / divisor;
990                 } else if (divisor) {
991                         return val / 1000.0;
992                 } else {
993                         return val;
994                 }
995         }
996 }
997
998 /* Prior to kernel version 2.6.12, the CPU fan speed was available in
999  * ADT746X_FAN_OLD, whereas later kernel versions provide this information in
1000  * ADT746X_FAN. */
1001 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
1002 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
1003
1004 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
1005 {
1006         static int rep = 0;
1007         char adt746x_fan_state[64];
1008         FILE *fp;
1009
1010         if (!p_client_buffer || client_buffer_size <= 0) {
1011                 return;
1012         }
1013
1014         if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
1015                         && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL) {
1016                 sprintf(adt746x_fan_state, "adt746x not found");
1017         } else {
1018                 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
1019                 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
1020                 fclose(fp);
1021         }
1022
1023         snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_fan_state);
1024 }
1025
1026 /* Prior to kernel version 2.6.12, the CPU temperature was found in
1027  * ADT746X_CPU_OLD, whereas later kernel versions provide this information in
1028  * ADT746X_CPU. */
1029 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
1030 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
1031
1032 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
1033 {
1034         static int rep = 0;
1035         char adt746x_cpu_state[64];
1036         FILE *fp;
1037
1038         if (!p_client_buffer || client_buffer_size <= 0) {
1039                 return;
1040         }
1041
1042         if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
1043                         && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL) {
1044                 sprintf(adt746x_cpu_state, "adt746x not found");
1045         } else {
1046                 fscanf(fp, "%2s", adt746x_cpu_state);
1047                 fclose(fp);
1048         }
1049
1050         snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state);
1051 }
1052
1053 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1054 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1055
1056 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1057 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1058                 const char *p_format, int divisor, unsigned int cpu)
1059 {
1060         FILE *f;
1061         static int rep = 0;
1062         char frequency[32];
1063         char s[256];
1064         double freq = 0;
1065
1066         if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1067                         || divisor <= 0) {
1068                 return 0;
1069         }
1070
1071         if (!prefer_proc) {
1072                 char current_freq_file[128];
1073
1074                 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1075                         CPUFREQ_POSTFIX);
1076                 f = fopen(current_freq_file, "r");
1077                 if (f) {
1078                         /* if there's a cpufreq /sys node, read the current frequency from
1079                          * this node and divide by 1000 to get Mhz. */
1080                         if (fgets(s, sizeof(s), f)) {
1081                                 s[strlen(s) - 1] = '\0';
1082                                 freq = strtod(s, NULL);
1083                         }
1084                         fclose(f);
1085                         snprintf(p_client_buffer, client_buffer_size, p_format,
1086                                 (freq / 1000) / divisor);
1087                         return 1;
1088                 }
1089         }
1090
1091         // open the CPU information file
1092         f = open_file("/proc/cpuinfo", &rep);
1093         if (!f) {
1094                 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1095                 return 0;
1096         }
1097
1098         // read the file
1099         while (fgets(s, sizeof(s), f) != NULL) {
1100
1101 #if defined(__i386) || defined(__x86_64)
1102                 // and search for the cpu mhz
1103                 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1104 #else
1105 #if defined(__alpha)
1106                 // different on alpha
1107                 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1108 #else
1109                 // this is different on ppc for some reason
1110                 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1111 #endif // defined(__alpha)
1112 #endif // defined(__i386) || defined(__x86_64)
1113
1114                         // copy just the number
1115                         strcpy(frequency, strchr(s, ':') + 2);
1116 #if defined(__alpha)
1117                         // strip " est.\n"
1118                         frequency[strlen(frequency) - 6] = '\0';
1119                         // kernel reports in Hz
1120                         freq = strtod(frequency, NULL) / 1000000;
1121 #else
1122                         // strip \n
1123                         frequency[strlen(frequency) - 1] = '\0';
1124                         freq = strtod(frequency, NULL);
1125 #endif
1126                         break;
1127                 }
1128                 if (strncmp(s, "processor", 9) == 0) {
1129                         cpu--;
1130                         continue;
1131                 }
1132         }
1133
1134         fclose(f);
1135         snprintf(p_client_buffer, client_buffer_size, p_format,
1136                 (float) freq / divisor);
1137         return 1;
1138 }
1139
1140 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1141
1142 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1143  * like this:
1144 # frequency voltage
1145 1800000 1340
1146 1600000 1292
1147 1400000 1100
1148 1200000 988
1149 1000000 1116
1150 800000 1004
1151 600000 988
1152  * Peter Tarjan (ptarjan@citromail.hu) */
1153
1154 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1155 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1156                 const char *p_format, int divisor, unsigned int cpu)
1157 {
1158         FILE *f;
1159         char s[256];
1160         int freq = 0;
1161         int voltage = 0;
1162         char current_freq_file[128];
1163         int freq_comp = 0;
1164
1165         /* build the voltage file name */
1166         cpu--;
1167         snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1168                 CPUFREQ_POSTFIX);
1169
1170         if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1171                         || divisor <= 0) {
1172                 return 0;
1173         }
1174
1175         /* read the current cpu frequency from the /sys node */
1176         f = fopen(current_freq_file, "r");
1177         if (f) {
1178                 if (fgets(s, sizeof(s), f)) {
1179                         s[strlen(s) - 1] = '\0';
1180                         freq = strtod(s, NULL);
1181                 }
1182                 fclose(f);
1183         } else {
1184                 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1185                 perror("get_voltage()");
1186                 if (f) {
1187                         fclose(f);
1188                 }
1189                 return 0;
1190         }
1191
1192         snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1193                 CPUFREQ_VOLTAGE);
1194
1195         /* use the current cpu frequency to find the corresponding voltage */
1196         f = fopen(current_freq_file, "r");
1197
1198         if (f) {
1199                 while (!feof(f)) {
1200                         char line[256];
1201
1202                         if (fgets(line, 255, f) == NULL) {
1203                                 break;
1204                         }
1205                         sscanf(line, "%d %d", &freq_comp, &voltage);
1206                         if (freq_comp == freq) {
1207                                 break;
1208                         }
1209                 }
1210                 fclose(f);
1211         } else {
1212                 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1213                 perror("get_voltage()");
1214                 if (f) {
1215                         fclose(f);
1216                 }
1217                 return 0;
1218         }
1219         snprintf(p_client_buffer, client_buffer_size, p_format,
1220                 (float) voltage / divisor);
1221         return 1;
1222 }
1223
1224 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1225
1226 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1227 {
1228         static int rep = 0;
1229         char buf[256];
1230         char buf2[256];
1231         FILE *fp;
1232
1233         if (!p_client_buffer || client_buffer_size <= 0) {
1234                 return;
1235         }
1236
1237         /* yeah, slow... :/ */
1238         if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1239                 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1240                 return;
1241         }
1242
1243         snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1244
1245         fp = open_file(buf2, &rep);
1246         if (!fp) {
1247                 snprintf(p_client_buffer, client_buffer_size,
1248                         "can't open fan's state file");
1249                 return;
1250         }
1251         memset(buf, 0, sizeof(buf));
1252         fscanf(fp, "%*s %99s", buf);
1253         fclose(fp);
1254
1255         snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1256 }
1257
1258 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1259 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1260 /* Linux 2.6.25 onwards ac adapter info is in
1261    /sys/class/power_supply/AC/
1262    On my system I get the following.
1263      /sys/class/power_supply/AC/uevent:
1264      PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1265      PHYSDEVBUS=acpi
1266      PHYSDEVDRIVER=ac
1267      POWER_SUPPLY_NAME=AC
1268      POWER_SUPPLY_TYPE=Mains
1269      POWER_SUPPLY_ONLINE=1
1270 */
1271
1272 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1273 {
1274         static int rep = 0;
1275
1276         char buf[256];
1277         char buf2[256];
1278         FILE *fp;
1279
1280         if (!p_client_buffer || client_buffer_size <= 0) {
1281                 return;
1282         }
1283
1284         snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1285         fp = open_file(buf2, &rep);
1286         if (fp) {
1287                 /* sysfs processing */
1288                 while (!feof(fp)) {
1289                         if (fgets(buf, sizeof(buf), fp) == NULL)
1290                                 break;
1291
1292                         if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1293                                 int online = 0;
1294                                 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1295                                 snprintf(p_client_buffer, client_buffer_size,
1296                                          "%s-line", (online ? "on" : "off"));
1297                                 break;
1298                         }
1299                 }
1300                 fclose(fp);
1301         } else {
1302                 /* yeah, slow... :/ */
1303                 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1304                         snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1305                         return;
1306                 }
1307
1308                 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1309
1310                 fp = open_file(buf2, &rep);
1311                 if (!fp) {
1312                         snprintf(p_client_buffer, client_buffer_size,
1313                                  "No ac adapter found.... where is it?");
1314                         return;
1315                 }
1316                 memset(buf, 0, sizeof(buf));
1317                 fscanf(fp, "%*s %99s", buf);
1318                 fclose(fp);
1319
1320                 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1321         }
1322 }
1323
1324 /*
1325 /proc/acpi/thermal_zone/THRM/cooling_mode
1326 cooling mode:            active
1327 /proc/acpi/thermal_zone/THRM/polling_frequency
1328 <polling disabled>
1329 /proc/acpi/thermal_zone/THRM/state
1330 state:                   ok
1331 /proc/acpi/thermal_zone/THRM/temperature
1332 temperature:             45 C
1333 /proc/acpi/thermal_zone/THRM/trip_points
1334 critical (S5):           73 C
1335 passive:                 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1336 */
1337
1338 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1339 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1340
1341 int open_acpi_temperature(const char *name)
1342 {
1343         char path[256];
1344         char buf[256];
1345         int fd;
1346
1347         if (name == NULL || strcmp(name, "*") == 0) {
1348                 static int rep = 0;
1349
1350                 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1351                         return -1;
1352                 }
1353                 name = buf;
1354         }
1355
1356         snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1357
1358         fd = open(path, O_RDONLY);
1359         if (fd < 0) {
1360                 ERR("can't open '%s': %s", path, strerror(errno));
1361         }
1362
1363         return fd;
1364 }
1365
1366 static double last_acpi_temp;
1367 static double last_acpi_temp_time;
1368
1369 double get_acpi_temperature(int fd)
1370 {
1371         if (fd <= 0) {
1372                 return 0;
1373         }
1374
1375         /* don't update acpi temperature too often */
1376         if (current_update_time - last_acpi_temp_time < 11.32) {
1377                 return last_acpi_temp;
1378         }
1379         last_acpi_temp_time = current_update_time;
1380
1381         /* seek to beginning */
1382         lseek(fd, 0, SEEK_SET);
1383
1384         /* read */
1385         {
1386                 char buf[256];
1387                 int n;
1388
1389                 n = read(fd, buf, 255);
1390                 if (n < 0) {
1391                         ERR("can't read fd %d: %s", fd, strerror(errno));
1392                 } else {
1393                         buf[n] = '\0';
1394                         sscanf(buf, "temperature: %lf", &last_acpi_temp);
1395                 }
1396         }
1397
1398         return last_acpi_temp;
1399 }
1400
1401 /*
1402 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1403 present:                 yes
1404 design capacity:         4400 mAh
1405 last full capacity:      4064 mAh
1406 battery technology:      rechargeable
1407 design voltage:          14800 mV
1408 design capacity warning: 300 mAh
1409 design capacity low:     200 mAh
1410 capacity granularity 1:  32 mAh
1411 capacity granularity 2:  32 mAh
1412 model number:            02KT
1413 serial number:           16922
1414 battery type:            LION
1415 OEM info:                SANYO
1416 */
1417
1418 /*
1419 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1420 present:                 yes
1421 capacity state:          ok
1422 charging state:          unknown
1423 present rate:            0 mA
1424 remaining capacity:      4064 mAh
1425 present voltage:         16608 mV
1426 */
1427
1428 /*
1429 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1430 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1431 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1432 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1433 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1434
1435 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1436 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1437
1438 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1439 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1440 */
1441
1442 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1443   Linux 2.6.24 onwards battery info is in
1444   /sys/class/power_supply/BAT0/
1445   On my system I get the following.
1446         /sys/class/power_supply/BAT0/uevent:
1447         PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1448         PHYSDEVBUS=acpi
1449         PHYSDEVDRIVER=battery
1450         POWER_SUPPLY_NAME=BAT0
1451         POWER_SUPPLY_TYPE=Battery
1452         POWER_SUPPLY_STATUS=Discharging
1453         POWER_SUPPLY_PRESENT=1
1454         POWER_SUPPLY_TECHNOLOGY=Li-ion
1455         POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1456         POWER_SUPPLY_VOLTAGE_NOW=10780000
1457         POWER_SUPPLY_CURRENT_NOW=13970000
1458         POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1459         POWER_SUPPLY_ENERGY_FULL=27370000
1460         POWER_SUPPLY_ENERGY_NOW=11810000
1461         POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1462         POWER_SUPPLY_MANUFACTURER=Panasonic
1463   On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1464 */
1465
1466 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1467 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1468 #define APM_PATH "/proc/apm"
1469 #define MAX_BATTERY_COUNT 4
1470
1471 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1472 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1473 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1474
1475 static int batteries_initialized = 0;
1476 static char batteries[MAX_BATTERY_COUNT][32];
1477
1478 static int acpi_last_full[MAX_BATTERY_COUNT];
1479 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1480
1481 /* e.g. "charging 75%" */
1482 static char last_battery_str[MAX_BATTERY_COUNT][64];
1483 /* e.g. "3h 15m" */
1484 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1485
1486 static double last_battery_time[MAX_BATTERY_COUNT];
1487
1488 static int last_battery_perct[MAX_BATTERY_COUNT];
1489 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1490
1491 void init_batteries(void)
1492 {
1493         int idx;
1494
1495         if (batteries_initialized) {
1496                 return;
1497         }
1498         for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1499                 batteries[idx][0] = '\0';
1500         }
1501         batteries_initialized = 1;
1502 }
1503
1504 int get_battery_idx(const char *bat)
1505 {
1506         int idx;
1507
1508         for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1509                 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1510                         break;
1511                 }
1512         }
1513
1514         /* if not found, enter a new entry */
1515         if (!strlen(batteries[idx])) {
1516                 snprintf(batteries[idx], 31, "%s", bat);
1517         }
1518
1519         return idx;
1520 }
1521
1522 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1523
1524 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1525 {
1526         static int idx, rep = 0, rep1 = 0, rep2 = 0;
1527         char acpi_path[128];
1528         char sysfs_path[128];
1529
1530         snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1531         snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1532
1533         init_batteries();
1534
1535         idx = get_battery_idx(bat);
1536
1537         /* don't update battery too often */
1538         if (current_update_time - last_battery_time[idx] < 29.5) {
1539                 set_return_value(buffer, n, item, idx);
1540                 return;
1541         }
1542
1543         last_battery_time[idx] = current_update_time;
1544
1545         memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1546         memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1547
1548         /* first try SYSFS if that fails try ACPI */
1549
1550         if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1551                 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1552         }
1553
1554         if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1555                 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1556         }
1557
1558         if (sysfs_bat_fp[idx] != NULL) {
1559                 /* SYSFS */
1560                 int present_rate = -1;
1561                 int remaining_capacity = -1;
1562                 char charging_state[64];
1563                 char present[4];
1564
1565                 strcpy(charging_state, "unknown");
1566
1567                 while (!feof(sysfs_bat_fp[idx])) {
1568                         char buf[256];
1569                         if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1570                                 break;
1571
1572                         /* let's just hope units are ok */
1573                         if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1574                                 strcpy(present, "yes");
1575                         else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1576                                 strcpy(present, "no");
1577                         else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1578                                 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1579                         /* present_rate is not the same as the
1580                         current flowing now but it is the same value
1581                         which was used in the past. so we continue
1582                         the tradition! */
1583                         else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1584                                 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1585                         else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1586                                 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1587                         else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1588                                 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1589                         else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1590                                 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1591                         else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1592                                 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1593                 }
1594
1595                 fclose(sysfs_bat_fp[idx]);
1596                 sysfs_bat_fp[idx] = NULL;
1597
1598                 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1599                 if (remaining_capacity > acpi_last_full[idx])
1600                         acpi_last_full[idx] = remaining_capacity;  /* normalize to 100% */
1601
1602                 /* not present */
1603                 if (strcmp(present, "No") == 0) {
1604                         strncpy(last_battery_str[idx], "not present", 64);
1605                 }
1606                 /* charging */
1607                 else if (strcmp(charging_state, "Charging") == 0) {
1608                         if (acpi_last_full[idx] != 0 && present_rate > 0) {
1609                                 /* e.g. charging 75% */
1610                                 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1611                                         (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1612                                 /* e.g. 2h 37m */
1613                                 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1614                                               (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1615                         } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1616                                 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1617                                         (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1618                                 snprintf(last_battery_time_str[idx],
1619                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1620                         } else {
1621                                 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1622                                 snprintf(last_battery_time_str[idx],
1623                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1624                         }
1625                 }
1626                 /* discharging */
1627                 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1628                         if (present_rate > 0) {
1629                                 /* e.g. discharging 35% */
1630                                 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1631                                         (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1632                                 /* e.g. 1h 12m */
1633                                 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1634                                               (long) (((float) remaining_capacity / present_rate) * 3600));
1635                         } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1636                                 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1637                                 snprintf(last_battery_time_str[idx],
1638                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1639                         } else {
1640                                 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1641                                         "discharging %d%%",
1642                                         (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1643                                 snprintf(last_battery_time_str[idx],
1644                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1645                         }
1646                 }
1647                 /* charged */
1648                 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1649                 else if (strncmp(charging_state, "Charged", 64) == 0) {
1650                                 /* Below happens with the second battery on my X40,
1651                                  * when the second one is empty and the first one
1652                                  * being charged. */
1653                                 if (remaining_capacity == 0)
1654                                         strcpy(last_battery_str[idx], "empty");
1655                                 else
1656                                         strcpy(last_battery_str[idx], "charged");
1657                 }
1658                 /* unknown, probably full / AC */
1659                 else {
1660                         if (acpi_last_full[idx] != 0
1661                             && remaining_capacity != acpi_last_full[idx])
1662                                 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1663                                         (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1664                         else
1665                                 strncpy(last_battery_str[idx], "AC", 64);
1666                 }
1667         } else if (acpi_bat_fp[idx] != NULL) {
1668                 /* ACPI */
1669                 int present_rate = -1;
1670                 int remaining_capacity = -1;
1671                 char charging_state[64];
1672                 char present[4];
1673
1674                 /* read last full capacity if it's zero */
1675                 if (acpi_last_full[idx] == 0) {
1676                         static int rep3 = 0;
1677                         char path[128];
1678                         FILE *fp;
1679
1680                         snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1681                         fp = open_file(path, &rep3);
1682                         if (fp != NULL) {
1683                                 while (!feof(fp)) {
1684                                         char b[256];
1685
1686                                         if (fgets(b, 256, fp) == NULL) {
1687                                                 break;
1688                                         }
1689                                         if (sscanf(b, "last full capacity: %d",
1690                                                         &acpi_last_full[idx]) != 0) {
1691                                                 break;
1692                                         }
1693                                 }
1694
1695                                 fclose(fp);
1696                         }
1697                 }
1698
1699                 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1700
1701                 strcpy(charging_state, "unknown");
1702
1703                 while (!feof(acpi_bat_fp[idx])) {
1704                         char buf[256];
1705
1706                         if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1707                                 break;
1708                         }
1709
1710                         /* let's just hope units are ok */
1711                         if (strncmp(buf, "present:", 8) == 0) {
1712                                 sscanf(buf, "present: %4s", present);
1713                         } else if (strncmp(buf, "charging state:", 15) == 0) {
1714                                 sscanf(buf, "charging state: %63s", charging_state);
1715                         } else if (strncmp(buf, "present rate:", 13) == 0) {
1716                                 sscanf(buf, "present rate: %d", &present_rate);
1717                         } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1718                                 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1719                         }
1720                 }
1721                 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1722                 if (remaining_capacity > acpi_last_full[idx]) {
1723                         /* normalize to 100% */
1724                         acpi_last_full[idx] = remaining_capacity;
1725                 }
1726
1727                 /* not present */
1728                 if (strcmp(present, "no") == 0) {
1729                         strncpy(last_battery_str[idx], "not present", 64);
1730                 /* charging */
1731                 } else if (strcmp(charging_state, "charging") == 0) {
1732                         if (acpi_last_full[idx] != 0 && present_rate > 0) {
1733                                 /* e.g. charging 75% */
1734                                 snprintf(last_battery_str[idx],
1735                                         sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1736                                         (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1737                                 /* e.g. 2h 37m */
1738                                 format_seconds(last_battery_time_str[idx],
1739                                         sizeof(last_battery_time_str[idx]) - 1,
1740                                         (long) (((acpi_last_full[idx] - remaining_capacity) *
1741                                         3600) / present_rate));
1742                         } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1743                                 snprintf(last_battery_str[idx],
1744                                         sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1745                                         (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1746                                 snprintf(last_battery_time_str[idx],
1747                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1748                         } else {
1749                                 strncpy(last_battery_str[idx], "charging",
1750                                         sizeof(last_battery_str[idx]) - 1);
1751                                 snprintf(last_battery_time_str[idx],
1752                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1753                         }
1754                 /* discharging */
1755                 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1756                         if (present_rate > 0) {
1757                                 /* e.g. discharging 35% */
1758                                 snprintf(last_battery_str[idx],
1759                                         sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1760                                         (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1761                                 /* e.g. 1h 12m */
1762                                 format_seconds(last_battery_time_str[idx],
1763                                         sizeof(last_battery_time_str[idx]) - 1,
1764                                         (long) ((remaining_capacity * 3600) / present_rate));
1765                         } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1766                                 snprintf(last_battery_str[idx],
1767                                         sizeof(last_battery_str[idx]) - 1, "full");
1768                                 snprintf(last_battery_time_str[idx],
1769                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1770                         } else {
1771                                 snprintf(last_battery_str[idx],
1772                                         sizeof(last_battery_str[idx]) - 1, "discharging %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                         }
1777                 /* charged */
1778                 } else if (strncmp(charging_state, "charged", 64) == 0) {
1779                         /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1780                         /* Below happens with the second battery on my X40,
1781                          * when the second one is empty and the first one being charged. */
1782                         if (remaining_capacity == 0) {
1783                                 strcpy(last_battery_str[idx], "empty");
1784                         } else {
1785                                 strcpy(last_battery_str[idx], "charged");
1786                         }
1787                 /* unknown, probably full / AC */
1788                 } else {
1789                         if (acpi_last_full[idx] != 0
1790                                         && remaining_capacity != acpi_last_full[idx]) {
1791                                 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1792                                         (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1793                         } else {
1794                                 strncpy(last_battery_str[idx], "AC", 64);
1795                         }
1796                 }
1797         } else {
1798                 /* APM */
1799                 if (apm_bat_fp[idx] == NULL) {
1800                         apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1801                 }
1802
1803                 if (apm_bat_fp[idx] != NULL) {
1804                         unsigned int ac, status, flag;
1805                         int life;
1806
1807                         fscanf(apm_bat_fp[idx], "%*s %*s %*x %x   %x       %x     %d%%",
1808                                 &ac, &status, &flag, &life);
1809
1810                         if (life == -1) {
1811                                 /* could check now that there is ac */
1812                                 snprintf(last_battery_str[idx], 64, "AC");
1813
1814                         /* could check that status == 3 here? */
1815                         } else if (ac && life != 100) {
1816                                 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1817                         } else {
1818                                 snprintf(last_battery_str[idx], 64, "%d%%", life);
1819                         }
1820
1821                         /* it seemed to buffer it so file must be closed (or could use
1822                          * syscalls directly but I don't feel like coding it now) */
1823                         fclose(apm_bat_fp[idx]);
1824                         apm_bat_fp[idx] = NULL;
1825                 }
1826         }
1827         set_return_value(buffer, n, item, idx);
1828 }
1829
1830 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1831 {
1832         switch (item) {
1833                 case BATTERY_STATUS:
1834                         snprintf(buffer, n, "%s", last_battery_str[idx]);
1835                         break;
1836                 case BATTERY_TIME:
1837                         snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1838                         break;
1839                 default:
1840                         break;
1841         }
1842 }
1843
1844 int get_battery_perct(const char *bat)
1845 {
1846         static int rep = 0;
1847         int idx;
1848         char acpi_path[128];
1849         char sysfs_path[128];
1850         int remaining_capacity = -1;
1851
1852         snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1853         snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1854
1855         init_batteries();
1856
1857         idx = get_battery_idx(bat);
1858
1859         /* don't update battery too often */
1860         if (current_update_time - last_battery_perct_time[idx] < 30) {
1861                 return last_battery_perct[idx];
1862         }
1863         last_battery_perct_time[idx] = current_update_time;
1864
1865         /* Only check for SYSFS or ACPI */
1866
1867         if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1868                 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1869                 rep = 0;
1870         }
1871
1872         if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1873                 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1874         }
1875
1876         if (sysfs_bat_fp[idx] != NULL) {
1877                 /* SYSFS */
1878                 while (!feof(sysfs_bat_fp[idx])) {
1879                         char buf[256];
1880                         if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1881                                 break;
1882
1883                         if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1884                                 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1885                         } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1886                                 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1887                         } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1888                                 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1889                         } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1890                                 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1891                         }
1892                 }
1893
1894                 fclose(sysfs_bat_fp[idx]);
1895                 sysfs_bat_fp[idx] = NULL;
1896
1897         } else if (acpi_bat_fp[idx] != NULL) {
1898                 /* ACPI */
1899                 /* read last full capacity if it's zero */
1900                 if (acpi_design_capacity[idx] == 0) {
1901                         static int rep2;
1902                         char path[128];
1903                         FILE *fp;
1904
1905                         snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1906                         fp = open_file(path, &rep2);
1907                         if (fp != NULL) {
1908                                 while (!feof(fp)) {
1909                                         char b[256];
1910
1911                                         if (fgets(b, 256, fp) == NULL) {
1912                                                 break;
1913                                         }
1914                                         if (sscanf(b, "last full capacity: %d",
1915                                                                 &acpi_design_capacity[idx]) != 0) {
1916                                                 break;
1917                                         }
1918                                 }
1919                                 fclose(fp);
1920                         }
1921                 }
1922
1923                 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1924
1925                 while (!feof(acpi_bat_fp[idx])) {
1926                         char buf[256];
1927
1928                         if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1929                                 break;
1930                         }
1931
1932                         if (buf[0] == 'r') {
1933                                 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1934                         }
1935                 }
1936         }
1937         if (remaining_capacity < 0) {
1938                 return 0;
1939         }
1940         /* compute the battery percentage */
1941         last_battery_perct[idx] =
1942                 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
1943         if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
1944         return last_battery_perct[idx];
1945 }
1946
1947 int get_battery_perct_bar(const char *bar)
1948 {
1949         int idx;
1950
1951         get_battery_perct(bar);
1952         idx = get_battery_idx(bar);
1953         return (int) (last_battery_perct[idx] * 2.56 - 1);
1954 }
1955
1956 /* On Apple powerbook and ibook:
1957 $ cat /proc/pmu/battery_0
1958 flags      : 00000013
1959 charge     : 3623
1960 max_charge : 3720
1961 current    : 388
1962 voltage    : 16787
1963 time rem.  : 900
1964 $ cat /proc/pmu/info
1965 PMU driver version     : 2
1966 PMU firmware version   : 0c
1967 AC Power               : 1
1968 Battery count          : 1
1969 */
1970
1971 /* defines as in <linux/pmu.h> */
1972 #define PMU_BATT_PRESENT                0x00000001
1973 #define PMU_BATT_CHARGING               0x00000002
1974
1975 static FILE *pmu_battery_fp;
1976 static FILE *pmu_info_fp;
1977 static char pb_battery_info[3][32];
1978 static double pb_battery_info_update;
1979
1980 #define PMU_PATH "/proc/pmu"
1981 void get_powerbook_batt_info(char *buffer, size_t n, int i)
1982 {
1983         static int rep = 0;
1984         const char *batt_path = PMU_PATH "/battery_0";
1985         const char *info_path = PMU_PATH "/info";
1986         unsigned int flags;
1987         int charge, max_charge, ac = -1;
1988         long timeval = -1;
1989
1990         /* don't update battery too often */
1991         if (current_update_time - pb_battery_info_update < 29.5) {
1992                 snprintf(buffer, n, "%s", pb_battery_info[i]);
1993                 return;
1994         }
1995         pb_battery_info_update = current_update_time;
1996
1997         if (pmu_battery_fp == NULL) {
1998                 pmu_battery_fp = open_file(batt_path, &rep);
1999         }
2000
2001         if (pmu_battery_fp != NULL) {
2002                 rewind(pmu_battery_fp);
2003                 while (!feof(pmu_battery_fp)) {
2004                         char buf[32];
2005
2006                         if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2007                                 break;
2008                         }
2009
2010                         if (buf[0] == 'f') {
2011                                 sscanf(buf, "flags      : %8x", &flags);
2012                         } else if (buf[0] == 'c' && buf[1] == 'h') {
2013                                 sscanf(buf, "charge     : %d", &charge);
2014                         } else if (buf[0] == 'm') {
2015                                 sscanf(buf, "max_charge : %d", &max_charge);
2016                         } else if (buf[0] == 't') {
2017                                 sscanf(buf, "time rem.  : %ld", &timeval);
2018                         }
2019                 }
2020         }
2021         if (pmu_info_fp == NULL) {
2022                 pmu_info_fp = open_file(info_path, &rep);
2023         }
2024
2025         if (pmu_info_fp != NULL) {
2026                 rewind(pmu_info_fp);
2027                 while (!feof(pmu_info_fp)) {
2028                         char buf[32];
2029
2030                         if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2031                                 break;
2032                         }
2033                         if (buf[0] == 'A') {
2034                                 sscanf(buf, "AC Power               : %d", &ac);
2035                         }
2036                 }
2037         }
2038         /* update status string */
2039         if ((ac && !(flags & PMU_BATT_PRESENT))) {
2040                 strcpy(pb_battery_info[PB_BATT_STATUS], "AC");
2041         } else if (ac && (flags & PMU_BATT_PRESENT)
2042                         && !(flags & PMU_BATT_CHARGING)) {
2043                 strcpy(pb_battery_info[PB_BATT_STATUS], "charged");
2044         } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2045                 strcpy(pb_battery_info[PB_BATT_STATUS], "charging");
2046         } else {
2047                 strcpy(pb_battery_info[PB_BATT_STATUS], "discharging");
2048         }
2049
2050         /* update percentage string */
2051         if (timeval == 0) {
2052                 pb_battery_info[PB_BATT_PERCENT][0] = 0;
2053         } else {
2054                 snprintf(pb_battery_info[PB_BATT_PERCENT],
2055                         sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2056                         (charge * 100) / max_charge);
2057         }
2058
2059         /* update time string */
2060         if (timeval == 0) {                     /* fully charged or battery not present */
2061                 pb_battery_info[PB_BATT_TIME][0] = 0;
2062         } else if (timeval < 60 * 60) { /* don't show secs */
2063                 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2064                         sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2065         } else {
2066                 format_seconds(pb_battery_info[PB_BATT_TIME],
2067                         sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2068         }
2069
2070         snprintf(buffer, n, "%s", pb_battery_info[i]);
2071 }
2072
2073 void update_top(void)
2074 {
2075         show_nice_processes = 1;
2076         process_find_top(info.cpu, info.memu);
2077         info.first_process = get_first_process();
2078 }
2079
2080 /* Here come the IBM ACPI-specific things. For reference, see
2081  * http://ibm-acpi.sourceforge.net/README
2082  * If IBM ACPI is installed, /proc/acpi/ibm contains the following files:
2083 bay
2084 beep
2085 bluetooth
2086 brightness
2087 cmos
2088 dock
2089 driver
2090 ecdump
2091 fan
2092 hotkey
2093 led
2094 light
2095 thermal
2096 video
2097 volume
2098  * The content of these files is described in detail in the aforementioned
2099  * README - some of them also in the following functions accessing them.
2100  * Peter Tarjan (ptarjan@citromail.hu) */
2101
2102 #define IBM_ACPI_DIR "/proc/acpi/ibm"
2103
2104 /* get fan speed on IBM/Lenovo laptops running the ibm acpi.
2105  * /proc/acpi/ibm/fan looks like this (3 lines):
2106 status:         disabled
2107 speed:          2944
2108 commands:       enable, disable
2109  * Peter Tarjan (ptarjan@citromail.hu) */
2110
2111 void get_ibm_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
2112 {
2113         FILE *fp;
2114         unsigned int speed = 0;
2115         char fan[128];
2116
2117         if (!p_client_buffer || client_buffer_size <= 0) {
2118                 return;
2119         }
2120
2121         snprintf(fan, 127, "%s/fan", IBM_ACPI_DIR);
2122
2123         fp = fopen(fan, "r");
2124         if (fp != NULL) {
2125                 while (!feof(fp)) {
2126                         char line[256];
2127
2128                         if (fgets(line, 255, fp) == NULL) {
2129                                 break;
2130                         }
2131                         if (sscanf(line, "speed: %u", &speed)) {
2132                                 break;
2133                         }
2134                 }
2135         } else {
2136                 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2137                         "ibm* from your "PACKAGE_NAME" config file.", fan, strerror(errno));
2138         }
2139
2140         fclose(fp);
2141         snprintf(p_client_buffer, client_buffer_size, "%d", speed);
2142 }
2143
2144 /* get the measured temperatures from the temperature sensors
2145  * on IBM/Lenovo laptops running the ibm acpi.
2146  * There are 8 values in /proc/acpi/ibm/thermal, and according to
2147  * http://ibm-acpi.sourceforge.net/README
2148  * these mean the following (at least on an IBM R51...)
2149  * 0:  CPU (also on the T series laptops)
2150  * 1:  Mini PCI Module (?)
2151  * 2:  HDD (?)
2152  * 3:  GPU (also on the T series laptops)
2153  * 4:  Battery (?)
2154  * 5:  N/A
2155  * 6:  Battery (?)
2156  * 7:  N/A
2157  * I'm not too sure about those with the question mark, but the values I'm
2158  * reading from *my* thermal file (on a T42p) look realistic for the
2159  * hdd and the battery.
2160  * #5 and #7 are always -128.
2161  * /proc/acpi/ibm/thermal looks like this (1 line):
2162 temperatures:   41 43 31 46 33 -128 29 -128
2163  * Peter Tarjan (ptarjan@citromail.hu) */
2164
2165 static double last_ibm_acpi_temp_time;
2166 void get_ibm_acpi_temps(void)
2167 {
2168
2169         FILE *fp;
2170         char thermal[128];
2171
2172         /* don't update too often */
2173         if (current_update_time - last_ibm_acpi_temp_time < 10.00) {
2174                 return;
2175         }
2176         last_ibm_acpi_temp_time = current_update_time;
2177
2178         /* if (!p_client_buffer || client_buffer_size <= 0) {
2179                 return;
2180         } */
2181
2182         snprintf(thermal, 127, "%s/thermal", IBM_ACPI_DIR);
2183         fp = fopen(thermal, "r");
2184
2185         if (fp != NULL) {
2186                 while (!feof(fp)) {
2187                         char line[256];
2188
2189                         if (fgets(line, 255, fp) == NULL) {
2190                                 break;
2191                         }
2192                         if (sscanf(line, "temperatures: %d %d %d %d %d %d %d %d",
2193                                         &ibm_acpi.temps[0], &ibm_acpi.temps[1], &ibm_acpi.temps[2],
2194                                         &ibm_acpi.temps[3], &ibm_acpi.temps[4], &ibm_acpi.temps[5],
2195                                         &ibm_acpi.temps[6], &ibm_acpi.temps[7])) {
2196                                 break;
2197                         }
2198                 }
2199         } else {
2200                 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2201                         "ibm* from your "PACKAGE_NAME" config file.", thermal, strerror(errno));
2202         }
2203
2204         fclose(fp);
2205 }
2206
2207 /* get volume (0-14) on IBM/Lenovo laptops running the ibm acpi.
2208  * "Volume" here is none of the mixer volumes, but a "master of masters"
2209  * volume adjusted by the IBM volume keys.
2210  * /proc/acpi/ibm/fan looks like this (4 lines):
2211 level:          4
2212 mute:           off
2213 commands:       up, down, mute
2214 commands:       level <level> (<level> is 0-15)
2215  * Peter Tarjan (ptarjan@citromail.hu) */
2216
2217 void get_ibm_acpi_volume(char *p_client_buffer, size_t client_buffer_size)
2218 {
2219         FILE *fp;
2220         char volume[128];
2221         unsigned int vol = -1;
2222         char mute[3] = "";
2223
2224         if (!p_client_buffer || client_buffer_size <= 0) {
2225                 return;
2226         }
2227
2228         snprintf(volume, 127, "%s/volume", IBM_ACPI_DIR);
2229
2230         fp = fopen(volume, "r");
2231         if (fp != NULL) {
2232                 while (!feof(fp)) {
2233                         char line[256];
2234                         unsigned int read_vol = -1;
2235
2236                         if (fgets(line, 255, fp) == NULL) {
2237                                 break;
2238                         }
2239                         if (sscanf(line, "level: %u", &read_vol)) {
2240                                 vol = read_vol;
2241                                 continue;
2242                         }
2243                         if (sscanf(line, "mute: %s", mute)) {
2244                                 break;
2245                         }
2246                 }
2247         } else {
2248                 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2249                         "ibm* from your "PACKAGE_NAME" config file.", volume, strerror(errno));
2250         }
2251
2252         fclose(fp);
2253
2254         if (strcmp(mute, "on") == 0) {
2255                 snprintf(p_client_buffer, client_buffer_size, "%s", "mute");
2256                 return;
2257         } else {
2258                 snprintf(p_client_buffer, client_buffer_size, "%d", vol);
2259                 return;
2260         }
2261 }
2262
2263 /* static FILE *fp = NULL; */
2264
2265 /* get LCD brightness on IBM/Lenovo laptops running the ibm acpi.
2266  * /proc/acpi/ibm/brightness looks like this (3 lines):
2267 level:          7
2268 commands:       up, down
2269 commands:       level <level> (<level> is 0-7)
2270  * Peter Tarjan (ptarjan@citromail.hu) */
2271
2272 void get_ibm_acpi_brightness(char *p_client_buffer, size_t client_buffer_size)
2273 {
2274         FILE *fp;
2275         unsigned int brightness = 0;
2276         char filename[128];
2277
2278         if (!p_client_buffer || client_buffer_size <= 0) {
2279                 return;
2280         }
2281
2282         snprintf(filename, 127, "%s/brightness", IBM_ACPI_DIR);
2283
2284         fp = fopen(filename, "r");
2285         if (fp != NULL) {
2286                 while (!feof(fp)) {
2287                         char line[256];
2288
2289                         if (fgets(line, 255, fp) == NULL) {
2290                                 break;
2291                         }
2292                         if (sscanf(line, "level: %u", &brightness)) {
2293                                 break;
2294                         }
2295                 }
2296         } else {
2297                 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2298                         "ibm* from your "PACKAGE_NAME" config file.", filename, strerror(errno));
2299         }
2300
2301         fclose(fp);
2302
2303         snprintf(p_client_buffer, client_buffer_size, "%d", brightness);
2304 }
2305
2306 void update_entropy(void)
2307 {
2308         static int rep = 0;
2309         const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2310         const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2311         FILE *fp1, *fp2;
2312
2313         info.entropy.entropy_avail = 0;
2314         info.entropy.poolsize = 0;
2315
2316         if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2317                 return;
2318         }
2319
2320         if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2321                 fclose(fp1);
2322                 return;
2323         }
2324
2325         fscanf(fp1, "%u", &info.entropy.entropy_avail);
2326         fscanf(fp2, "%u", &info.entropy.poolsize);
2327
2328         fclose(fp1);
2329         fclose(fp2);
2330
2331         info.mask |= (1 << INFO_ENTROPY);
2332 }
2333
2334 const char *get_disk_protect_queue(const char *disk)
2335 {
2336         FILE *fp;
2337         char path[128];
2338         int state;
2339
2340         snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2341         if ((fp = fopen(path, "r")) == NULL)
2342                 return "n/a   ";
2343         if (fscanf(fp, "%d\n", &state) != 1) {
2344                 fclose(fp);
2345                 return "failed";
2346         }
2347         fclose(fp);
2348         return state ? "frozen" : "free  ";
2349 }
2350