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