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