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