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