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