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