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