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