capitalization consistency
[monky] / src / linux.c
1 /* Conky, a system monitor, based on torsmo
2  *
3  * Any original torsmo code is licensed under the BSD license
4  *
5  * All code written since the fork of torsmo is licensed under the GPL
6  *
7  * Please see COPYING for details
8  *
9  * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
10  * Copyright (c) 2007 Toni Spets
11  * Copyright (c) 2005-2008 Brenden Matthews, Philip Kovacs, et. al.
12  *      (see AUTHORS)
13  * All rights reserved.
14  *
15  * This program is free software: you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation, either version 3 of the License, or
18  * (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  * You should have received a copy of the GNU General Public License
25  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
26  *
27  * $Id$ */
28
29 #include "conky.h"
30 #include <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 *ns2;
394
395                         if (!(((struct ifreq *) conf.ifc_buf) + k))
396                                 break;
397
398                         ns2 = get_net_stat(
399                                 ((struct ifreq *) conf.ifc_buf)[k].ifr_ifrn.ifrn_name);
400                         ns2->addr = ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.ifru_addr;
401                         if(NULL != ns2->addrs) {
402                                 sprintf(temp_addr, "%u.%u.%u.%u, ",
403                                         ns2->addr.sa_data[2] & 255,
404                                         ns2->addr.sa_data[3] & 255,
405                                         ns2->addr.sa_data[4] & 255,
406                                         ns2->addr.sa_data[5] & 255);
407                                 if(NULL == strstr(ns2->addrs, temp_addr))
408                                         strncpy(ns2->addrs + strlen(ns2->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         char buf[256];
573
574         if (info.cpu_usage) {
575                 return;
576         }
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 idx;
612         double curtmp;
613         const 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         idx = 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                         double delta;
652                         idx = isdigit(buf[3]) ? ((int) buf[3]) - 0x2F : 0;
653                         sscanf(buf, stat_template, &(cpu[idx].cpu_user),
654                                 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
655                                 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
656                                 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
657                                 &(cpu[idx].cpu_steal));
658
659                         cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
660                                 cpu[idx].cpu_system + cpu[idx].cpu_idle +
661                                 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
662                                 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
663
664                         cpu[idx].cpu_active_total = cpu[idx].cpu_total -
665                                 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
666                         info.mask |= (1 << INFO_CPU);
667
668                         delta = current_update_time - last_update_time;
669
670                         if (delta <= 0.001) {
671                                 break;
672                         }
673
674                         cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
675                                 cpu[idx].cpu_last_active_total) /
676                                 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
677                         curtmp = 0;
678                         for (i = 0; i < info.cpu_avg_samples; i++) {
679                                 curtmp += cpu[idx].cpu_val[i];
680                         }
681                         /* TESTING -- I've removed this, because I don't think it is right.
682                          * You shouldn't divide by the cpu count here ...
683                          * removing for testing */
684                         /* if (idx == 0) {
685                                 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
686                                         info.cpu_count;
687                         } else {
688                                 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
689                         } */
690                         /* TESTING -- this line replaces the prev. "suspect" if/else */
691                         info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
692
693                         cpu[idx].cpu_last_total = cpu[idx].cpu_total;
694                         cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
695                         for (i = info.cpu_avg_samples - 1; i > 0; i--) {
696                                 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
697                         }
698                 }
699         }
700         fclose(stat_fp);
701 }
702
703 void update_running_processes(void)
704 {
705         update_stat();
706 }
707
708 void update_cpu_usage(void)
709 {
710         update_stat();
711 }
712
713 void update_load_average(void)
714 {
715 #ifdef HAVE_GETLOADAVG
716         if (!prefer_proc) {
717                 double v[3];
718
719                 getloadavg(v, 3);
720                 info.loadavg[0] = (float) v[0];
721                 info.loadavg[1] = (float) v[1];
722                 info.loadavg[2] = (float) v[2];
723         } else
724 #endif
725         {
726                 static int rep = 0;
727                 FILE *fp;
728
729                 if (!(fp = open_file("/proc/loadavg", &rep))) {
730                         info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
731                         return;
732                 }
733                 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
734                         &info.loadavg[2]);
735                 fclose(fp);
736         }
737         info.mask |= (1 << INFO_LOADAVG);
738 }
739
740 #define PROC_I8K "/proc/i8k"
741 #define I8K_DELIM " "
742 static char *i8k_procbuf = NULL;
743 void update_i8k(void)
744 {
745         FILE *fp;
746
747         if (!i8k_procbuf) {
748                 i8k_procbuf = (char *) malloc(128 * sizeof(char));
749         }
750         if ((fp = fopen(PROC_I8K, "r")) == NULL) {
751                 CRIT_ERR("/proc/i8k doesn't exist! use insmod to make sure the kernel "
752                         "driver is loaded...");
753         }
754
755         memset(&i8k_procbuf[0], 0, 128);
756         if (fread(&i8k_procbuf[0], sizeof(char), 128, fp) == 0) {
757                 ERR("something wrong with /proc/i8k...");
758         }
759
760         fclose(fp);
761
762         i8k.version = strtok(&i8k_procbuf[0], I8K_DELIM);
763         i8k.bios = strtok(NULL, I8K_DELIM);
764         i8k.serial = strtok(NULL, I8K_DELIM);
765         i8k.cpu_temp = strtok(NULL, I8K_DELIM);
766         i8k.left_fan_status = strtok(NULL, I8K_DELIM);
767         i8k.right_fan_status = strtok(NULL, I8K_DELIM);
768         i8k.left_fan_rpm = strtok(NULL, I8K_DELIM);
769         i8k.right_fan_rpm = strtok(NULL, I8K_DELIM);
770         i8k.ac_status = strtok(NULL, I8K_DELIM);
771         i8k.buttons_status = strtok(NULL, I8K_DELIM);
772 }
773
774 /***********************************************************/
775 /***********************************************************/
776 /***********************************************************/
777
778 static int no_dots(const struct dirent *d)
779 {
780         if (d->d_name[0] == '.') {
781                 return 0;
782         }
783         return 1;
784 }
785
786 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
787 {
788         struct dirent **namelist;
789         int i, n;
790
791         n = scandir(dir, &namelist, no_dots, alphasort);
792         if (n < 0) {
793                 if (!rep || !*rep) {
794                         ERR("scandir for %s: %s", dir, strerror(errno));
795                         if (rep) {
796                                 *rep = 1;
797                         }
798                 }
799                 return 0;
800         } else {
801                 if (n == 0) {
802                         return 0;
803                 }
804
805                 strncpy(s, namelist[0]->d_name, 255);
806                 s[255] = '\0';
807
808                 for (i = 0; i < n; i++) {
809                         free(namelist[i]);
810                 }
811                 free(namelist);
812
813                 return 1;
814         }
815 }
816
817 int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
818                 int *divisor, char *devtype)
819 {
820         char path[256];
821         char buf[256];
822         int fd;
823         int divfd;
824
825         memset(buf, 0, sizeof(buf));
826
827         /* if device is NULL or *, get first */
828         if (dev == NULL || strcmp(dev, "*") == 0) {
829                 static int rep = 0;
830
831                 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
832                         return -1;
833                 }
834                 dev = buf;
835         }
836
837         if (strcmp(dir, "/sys/class/hwmon/") == 0) {
838                 if (*buf) {
839                         /* buf holds result from get_first_file_in_a_directory() above,
840                          * e.g. "hwmon0" -- append "/device" */
841                         strcat(buf, "/device");
842                 } else {
843                         /* dev holds device number N as a string,
844                          * e.g. "0", -- convert to "hwmon0/device" */
845                         sprintf(buf, "hwmon%s/device", dev);
846                         dev = buf;
847                 }
848         }
849
850         /* change vol to in */
851         if (strcmp(type, "vol") == 0) {
852                 type = "in";
853         }
854
855         if (strcmp(type, "tempf") == 0) {
856                 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, "temp", n);
857         } else {
858                 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
859         }
860         strncpy(devtype, path, 255);
861
862         /* open file */
863         fd = open(path, O_RDONLY);
864         if (fd < 0) {
865                 CRIT_ERR("can't open '%s': %s\nplease check your device or remove this "
866                         "var from Conky", path, strerror(errno));
867         }
868
869         if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
870                         || strcmp(type, "tempf") == 0) {
871                 *divisor = 1;
872         } else {
873                 *divisor = 0;
874         }
875         /* fan does not use *_div as a read divisor */
876         if (strcmp("fan", type) == 0) {
877                 return fd;
878         }
879
880         /* test if *_div file exist, open it and use it as divisor */
881         if (strcmp(type, "tempf") == 0) {
882                 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
883         } else {
884                 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
885         }
886
887         divfd = open(path, O_RDONLY);
888         if (divfd > 0) {
889                 /* read integer */
890                 char divbuf[64];
891                 int divn;
892
893                 divn = read(divfd, divbuf, 63);
894                 /* should read until n == 0 but I doubt that kernel will give these
895                  * in multiple pieces. :) */
896                 if (divn < 0) {
897                         ERR("open_sysfs_sensor(): can't read from sysfs");
898                 } else {
899                         divbuf[divn] = '\0';
900                         *divisor = atoi(divbuf);
901                 }
902         }
903
904         close(divfd);
905
906         return fd;
907 }
908
909 double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
910 {
911         int val = 0;
912
913         if (*fd <= 0) {
914                 return 0;
915         }
916
917         lseek(*fd, 0, SEEK_SET);
918
919         /* read integer */
920         {
921                 char buf[64];
922                 int n;
923                 n = read(*fd, buf, 63);
924                 /* should read until n == 0 but I doubt that kernel will give these
925                  * in multiple pieces. :) */
926                 if (n < 0) {
927                         ERR("get_sysfs_info(): read from %s failed\n", devtype);
928                 } else {
929                         buf[n] = '\0';
930                         val = atoi(buf);
931                 }
932         }
933
934         close(*fd);
935         /* open file */
936         *fd = open(devtype, O_RDONLY);
937         if (*fd < 0) {
938                 ERR("can't open '%s': %s", devtype, strerror(errno));
939         }
940
941         /* My dirty hack for computing CPU value
942          * Filedil, from forums.gentoo.org */
943         /* if (strstr(devtype, "temp1_input") != NULL) {
944                 return -15.096 + 1.4893 * (val / 1000.0);
945         } */
946
947         /* divide voltage and temperature by 1000 */
948         /* or if any other divisor is given, use that */
949         if (strcmp(type, "tempf") == 0) {
950                 if (divisor > 1) {
951                         return ((val / divisor + 40) * 9.0 / 5) - 40;
952                 } else if (divisor) {
953                         return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
954                 } else {
955                         return ((val + 40) * 9.0 / 5) - 40;
956                 }
957         } else {
958                 if (divisor > 1) {
959                         return val / divisor;
960                 } else if (divisor) {
961                         return val / 1000.0;
962                 } else {
963                         return val;
964                 }
965         }
966 }
967
968 /* Prior to kernel version 2.6.12, the CPU fan speed was available in
969  * ADT746X_FAN_OLD, whereas later kernel versions provide this information in
970  * ADT746X_FAN. */
971 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
972 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
973
974 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
975 {
976         static int rep = 0;
977         char adt746x_fan_state[64];
978         FILE *fp;
979
980         if (!p_client_buffer || client_buffer_size <= 0) {
981                 return;
982         }
983
984         if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
985                         && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL) {
986                 sprintf(adt746x_fan_state, "adt746x not found");
987         } else {
988                 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
989                 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
990                 fclose(fp);
991         }
992
993         snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_fan_state);
994 }
995
996 /* Prior to kernel version 2.6.12, the CPU temperature was found in
997  * ADT746X_CPU_OLD, whereas later kernel versions provide this information in
998  * ADT746X_CPU. */
999 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
1000 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
1001
1002 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
1003 {
1004         static int rep = 0;
1005         char adt746x_cpu_state[64];
1006         FILE *fp;
1007
1008         if (!p_client_buffer || client_buffer_size <= 0) {
1009                 return;
1010         }
1011
1012         if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
1013                         && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL) {
1014                 sprintf(adt746x_cpu_state, "adt746x not found");
1015         } else {
1016                 fscanf(fp, "%2s", adt746x_cpu_state);
1017                 fclose(fp);
1018         }
1019
1020         snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state);
1021 }
1022
1023 /* Thanks to "Walt Nelson" <wnelsonjr@comcast.net> */
1024
1025 /***********************************************************************/
1026 /* This file is part of x86info.
1027  * (C) 2001 Dave Jones.
1028  *
1029  * Licensed under the terms of the GNU GPL License version 2.
1030  *
1031  * Estimate CPU MHz routine by Andrea Arcangeli <andrea@suse.de>
1032  * Small changes by David Sterba <sterd9am@ss1000.ms.mff.cuni.cz> */
1033
1034 #if  defined(__i386) || defined(__x86_64)
1035 __inline__ unsigned long long int rdtsc(void)
1036 {
1037         unsigned long long int x;
1038
1039         __asm__ volatile(".byte 0x0f, 0x31":"=A" (x));
1040         return x;
1041 }
1042 #endif
1043
1044 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1045 void get_freq_dynamic(char *p_client_buffer, size_t client_buffer_size,
1046                 const char *p_format, int divisor)
1047 {
1048 #if  defined(__i386) || defined(__x86_64)
1049         struct timezone tz;
1050         struct timeval tvstart, tvstop;
1051         unsigned long long cycles[2];   /* gotta be 64 bit */
1052         unsigned int microseconds;      /* total time taken */
1053
1054         if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1055                         || divisor <= 0) {
1056                 return;
1057         }
1058
1059         memset(&tz, 0, sizeof(tz));
1060
1061         /* get this function in cached memory */
1062         gettimeofday(&tvstart, &tz);
1063         cycles[0] = rdtsc();
1064         gettimeofday(&tvstart, &tz);
1065
1066         /* we don't trust that this is any specific length of time */
1067         usleep(100);
1068         cycles[1] = rdtsc();
1069         gettimeofday(&tvstop, &tz);
1070         microseconds = ((tvstop.tv_sec - tvstart.tv_sec) * 1000000) +
1071                 (tvstop.tv_usec - tvstart.tv_usec);
1072
1073         snprintf(p_client_buffer, client_buffer_size, p_format,
1074                 (float) ((cycles[1] - cycles[0]) / microseconds) / divisor);
1075         return;
1076 #else
1077         /* FIXME: hardwired: get freq for first cpu!
1078          * this whole function needs to be rethought and redone for
1079          * multi-cpu/multi-core/multi-threaded environments and
1080          * arbitrary combinations thereof */
1081         get_freq(p_client_buffer, client_buffer_size, p_format, divisor, 1);
1082         return;
1083 #endif
1084 }
1085
1086 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1087 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1088
1089 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1090 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1091                 const char *p_format, int divisor, unsigned int cpu)
1092 {
1093         FILE *f;
1094         static int rep = 0;
1095         char frequency[32];
1096         char s[256];
1097         double freq = 0;
1098
1099         if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1100                         || divisor <= 0) {
1101                 return 0;
1102         }
1103
1104         if (!prefer_proc) {
1105                 char current_freq_file[128];
1106
1107                 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1108                         CPUFREQ_POSTFIX);
1109                 f = fopen(current_freq_file, "r");
1110                 if (f) {
1111                         /* if there's a cpufreq /sys node, read the current frequency from
1112                          * this node and divide by 1000 to get Mhz. */
1113                         if (fgets(s, sizeof(s), f)) {
1114                                 s[strlen(s) - 1] = '\0';
1115                                 freq = strtod(s, NULL);
1116                         }
1117                         fclose(f);
1118                         snprintf(p_client_buffer, client_buffer_size, p_format,
1119                                 (freq / 1000) / divisor);
1120                         return 1;
1121                 }
1122         }
1123
1124         // open the CPU information file
1125         f = open_file("/proc/cpuinfo", &rep);
1126         if (!f) {
1127                 perror("Conky: Failed to access '/proc/cpuinfo' at get_freq()");
1128                 return 0;
1129         }
1130
1131         // read the file
1132         while (fgets(s, sizeof(s), f) != NULL) {
1133
1134 #if defined(__i386) || defined(__x86_64)
1135                 // and search for the cpu mhz
1136                 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1137 #else
1138 #if defined(__alpha)
1139                 // different on alpha
1140                 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1141 #else
1142                 // this is different on ppc for some reason
1143                 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1144 #endif // defined(__alpha)
1145 #endif // defined(__i386) || defined(__x86_64)
1146
1147                         // copy just the number
1148                         strcpy(frequency, strchr(s, ':') + 2);
1149 #if defined(__alpha)
1150                         // strip " est.\n"
1151                         frequency[strlen(frequency) - 6] = '\0';
1152                         // kernel reports in Hz
1153                         freq = strtod(frequency, NULL) / 1000000;
1154 #else
1155                         // strip \n
1156                         frequency[strlen(frequency) - 1] = '\0';
1157                         freq = strtod(frequency, NULL);
1158 #endif
1159                         break;
1160                 }
1161                 if (strncmp(s, "processor", 9) == 0) {
1162                         cpu--;
1163                         continue;
1164                 }
1165         }
1166
1167         fclose(f);
1168         snprintf(p_client_buffer, client_buffer_size, p_format,
1169                 (float) freq / divisor);
1170         return 1;
1171 }
1172
1173 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1174
1175 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1176  * like this:
1177 # frequency voltage
1178 1800000 1340
1179 1600000 1292
1180 1400000 1100
1181 1200000 988
1182 1000000 1116
1183 800000 1004
1184 600000 988
1185  * Peter Tarjan (ptarjan@citromail.hu) */
1186
1187 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1188 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1189                 const char *p_format, int divisor, unsigned int cpu)
1190 {
1191         FILE *f;
1192         char s[256];
1193         int freq = 0;
1194         int voltage = 0;
1195         char current_freq_file[128];
1196         int freq_comp = 0;
1197
1198         /* build the voltage file name */
1199         cpu--;
1200         snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1201                 CPUFREQ_POSTFIX);
1202
1203         if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1204                         || divisor <= 0) {
1205                 return 0;
1206         }
1207
1208         /* read the current cpu frequency from the /sys node */
1209         f = fopen(current_freq_file, "r");
1210         if (f) {
1211                 if (fgets(s, sizeof(s), f)) {
1212                         s[strlen(s) - 1] = '\0';
1213                         freq = strtod(s, NULL);
1214                 }
1215                 fclose(f);
1216         } else {
1217                 fprintf(stderr, "Conky: Failed to access '%s' at ", current_freq_file);
1218                 perror("get_voltage()");
1219                 if (f) {
1220                         fclose(f);
1221                 }
1222                 return 0;
1223         }
1224
1225         snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1226                 CPUFREQ_VOLTAGE);
1227
1228         /* use the current cpu frequency to find the corresponding voltage */
1229         f = fopen(current_freq_file, "r");
1230
1231         if (f) {
1232                 while (!feof(f)) {
1233                         char line[256];
1234
1235                         if (fgets(line, 255, f) == NULL) {
1236                                 break;
1237                         }
1238                         sscanf(line, "%d %d", &freq_comp, &voltage);
1239                         if (freq_comp == freq) {
1240                                 break;
1241                         }
1242                 }
1243                 fclose(f);
1244         } else {
1245                 fprintf(stderr, "Conky: Failed to access '%s' at ", current_freq_file);
1246                 perror("get_voltage()");
1247                 if (f) {
1248                         fclose(f);
1249                 }
1250                 return 0;
1251         }
1252         snprintf(p_client_buffer, client_buffer_size, p_format,
1253                 (float) voltage / divisor);
1254         return 1;
1255 }
1256
1257 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1258
1259 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1260 {
1261         static int rep = 0;
1262         char buf[256];
1263         char buf2[256];
1264         FILE *fp;
1265
1266         if (!p_client_buffer || client_buffer_size <= 0) {
1267                 return;
1268         }
1269
1270         /* yeah, slow... :/ */
1271         if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1272                 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1273                 return;
1274         }
1275
1276         snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1277
1278         fp = open_file(buf2, &rep);
1279         if (!fp) {
1280                 snprintf(p_client_buffer, client_buffer_size,
1281                         "can't open fan's state file");
1282                 return;
1283         }
1284         memset(buf, 0, sizeof(buf));
1285         fscanf(fp, "%*s %99s", buf);
1286         fclose(fp);
1287
1288         snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1289 }
1290
1291 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1292
1293 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1294 {
1295         static int rep = 0;
1296         char buf[256];
1297         char buf2[256];
1298         FILE *fp;
1299
1300         if (!p_client_buffer || client_buffer_size <= 0) {
1301                 return;
1302         }
1303
1304         /* yeah, slow... :/ */
1305         if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1306                 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1307                 return;
1308         }
1309
1310         snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1311
1312         fp = open_file(buf2, &rep);
1313         if (!fp) {
1314                 snprintf(p_client_buffer, client_buffer_size,
1315                         "No ac adapter found.... where is it?");
1316                 return;
1317         }
1318         memset(buf, 0, sizeof(buf));
1319         fscanf(fp, "%*s %99s", buf);
1320         fclose(fp);
1321
1322         snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1323 }
1324
1325 /*
1326 /proc/acpi/thermal_zone/THRM/cooling_mode
1327 cooling mode:            active
1328 /proc/acpi/thermal_zone/THRM/polling_frequency
1329 <polling disabled>
1330 /proc/acpi/thermal_zone/THRM/state
1331 state:                   ok
1332 /proc/acpi/thermal_zone/THRM/temperature
1333 temperature:             45 C
1334 /proc/acpi/thermal_zone/THRM/trip_points
1335 critical (S5):           73 C
1336 passive:                 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1337 */
1338
1339 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1340 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1341
1342 int open_acpi_temperature(const char *name)
1343 {
1344         char path[256];
1345         char buf[256];
1346         int fd;
1347
1348         if (name == NULL || strcmp(name, "*") == 0) {
1349                 static int rep = 0;
1350
1351                 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1352                         return -1;
1353                 }
1354                 name = buf;
1355         }
1356
1357         snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1358
1359         fd = open(path, O_RDONLY);
1360         if (fd < 0) {
1361                 ERR("can't open '%s': %s", path, strerror(errno));
1362         }
1363
1364         return fd;
1365 }
1366
1367 static double last_acpi_temp;
1368 static double last_acpi_temp_time;
1369
1370 double get_acpi_temperature(int fd)
1371 {
1372         if (fd <= 0) {
1373                 return 0;
1374         }
1375
1376         /* don't update acpi temperature too often */
1377         if (current_update_time - last_acpi_temp_time < 11.32) {
1378                 return last_acpi_temp;
1379         }
1380         last_acpi_temp_time = current_update_time;
1381
1382         /* seek to beginning */
1383         lseek(fd, 0, SEEK_SET);
1384
1385         /* read */
1386         {
1387                 char buf[256];
1388                 int n;
1389
1390                 n = read(fd, buf, 255);
1391                 if (n < 0) {
1392                         ERR("can't read fd %d: %s", fd, strerror(errno));
1393                 } else {
1394                         buf[n] = '\0';
1395                         sscanf(buf, "temperature: %lf", &last_acpi_temp);
1396                 }
1397         }
1398
1399         return last_acpi_temp;
1400 }
1401
1402 /*
1403 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1404 present:                 yes
1405 design capacity:         4400 mAh
1406 last full capacity:      4064 mAh
1407 battery technology:      rechargeable
1408 design voltage:          14800 mV
1409 design capacity warning: 300 mAh
1410 design capacity low:     200 mAh
1411 capacity granularity 1:  32 mAh
1412 capacity granularity 2:  32 mAh
1413 model number:            02KT
1414 serial number:           16922
1415 battery type:            LION
1416 OEM info:                SANYO
1417 */
1418
1419 /*
1420 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1421 present:                 yes
1422 capacity state:          ok
1423 charging state:          unknown
1424 present rate:            0 mA
1425 remaining capacity:      4064 mAh
1426 present voltage:         16608 mV
1427 */
1428
1429 /*
1430 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1431 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1432 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1433 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1434 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1435
1436 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1437 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1438
1439 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1440 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1441 */
1442
1443 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1444   Linux 2.6.24 onwards battery info is in
1445   /sys/class/power_supply/BAT0/
1446   On my system I get the following.
1447         /sys/class/power_supply/BAT0/uevent:
1448         PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1449         PHYSDEVBUS=acpi
1450         PHYSDEVDRIVER=battery
1451         POWER_SUPPLY_NAME=BAT0
1452         POWER_SUPPLY_TYPE=Battery
1453         POWER_SUPPLY_STATUS=Discharging
1454         POWER_SUPPLY_PRESENT=1
1455         POWER_SUPPLY_TECHNOLOGY=Li-ion
1456         POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1457         POWER_SUPPLY_VOLTAGE_NOW=10780000
1458         POWER_SUPPLY_CURRENT_NOW=13970000
1459         POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1460         POWER_SUPPLY_ENERGY_FULL=27370000
1461         POWER_SUPPLY_ENERGY_NOW=11810000
1462         POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1463         POWER_SUPPLY_MANUFACTURER=Panasonic
1464   On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1465 */
1466
1467 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1468 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1469 #define APM_PATH "/proc/apm"
1470 #define MAX_BATTERY_COUNT 4
1471
1472 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1473 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1474 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1475
1476 static int batteries_initialized = 0;
1477 static char batteries[MAX_BATTERY_COUNT][32];
1478
1479 static int acpi_last_full[MAX_BATTERY_COUNT];
1480 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1481
1482 /* e.g. "charging 75%" */
1483 static char last_battery_str[MAX_BATTERY_COUNT][64];
1484 /* e.g. "3h 15m" */
1485 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1486
1487 static double last_battery_time[MAX_BATTERY_COUNT];
1488
1489 static int last_battery_perct[MAX_BATTERY_COUNT];
1490 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1491
1492 void init_batteries(void)
1493 {
1494         int idx;
1495
1496         if (batteries_initialized) {
1497                 return;
1498         }
1499         for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1500                 batteries[idx][0] = '\0';
1501         }
1502         batteries_initialized = 1;
1503 }
1504
1505 int get_battery_idx(const char *bat)
1506 {
1507         int idx;
1508
1509         for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1510                 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1511                         break;
1512                 }
1513         }
1514
1515         /* if not found, enter a new entry */
1516         if (!strlen(batteries[idx])) {
1517                 snprintf(batteries[idx], 31, "%s", bat);
1518         }
1519
1520         return idx;
1521 }
1522
1523 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1524 {
1525         static int idx, rep = 0, rep2 = 0;
1526         char acpi_path[128];
1527         char sysfs_path[128];
1528
1529         snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1530         snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1531
1532         init_batteries();
1533
1534         idx = get_battery_idx(bat);
1535
1536         /* don't update battery too often */
1537         if (current_update_time - last_battery_time[idx] < 29.5) {
1538                 goto set_return_value;
1539         }
1540
1541         last_battery_time[idx] = current_update_time;
1542
1543         memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1544         memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1545
1546         /* first try SYSFS if that fails try ACPI */
1547  
1548         if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1549                 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1550                 rep = 0;
1551         }
1552   
1553         if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1554                 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1555         }
1556   
1557         if (sysfs_bat_fp[idx] != NULL) {
1558                 /* SYSFS */
1559                 int present_rate = -1;
1560                 int remaining_capacity = -1;
1561                 char charging_state[64];
1562                 char present[4];
1563  
1564                 strcpy(charging_state, "unknown");
1565  
1566                 while (!feof(sysfs_bat_fp[idx])) {
1567                         char buf[256];
1568                         if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1569                                 break;
1570  
1571                         /* let's just hope units are ok */
1572                         if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1573                                 strcpy(present, "yes");
1574                         else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1575                                 strcpy(present, "no");
1576                         else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1577                                 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1578                         /* present_rate is not the same as the
1579                         current flowing now but it is the same value
1580                         which was used in the past. so we continue
1581                         the tradition! */
1582                         else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1583                                 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1584                         else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1585                                 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1586                         else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1587                                 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1588                         else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1589                                 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1590                         else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1591                                 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1592                 }
1593  
1594                 fclose(sysfs_bat_fp[idx]);
1595                 sysfs_bat_fp[idx] = NULL;
1596  
1597                 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1598                 if (remaining_capacity > acpi_last_full[idx])
1599                         acpi_last_full[idx] = remaining_capacity;  /* normalize to 100% */
1600  
1601                 /* not present */
1602                 if (strcmp(present, "No") == 0) {
1603                         strncpy(last_battery_str[idx], "not present", 64);
1604                 }
1605                 /* charging */
1606                 else if (strcmp(charging_state, "Charging") == 0) {
1607                         if (acpi_last_full[idx] != 0 && present_rate > 0) {
1608                                 /* e.g. charging 75% */
1609                                 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1610                                         (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1611                                 /* e.g. 2h 37m */
1612                                 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1613                                               (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1614                         } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1615                                 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1616                                         (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1617                                 snprintf(last_battery_time_str[idx],
1618                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1619                         } else {
1620                                 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1621                                 snprintf(last_battery_time_str[idx],
1622                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1623                         }
1624                 }
1625                 /* discharging */
1626                 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1627                         if (present_rate > 0) {
1628                                 /* e.g. discharging 35% */
1629                                 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1630                                         (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1631                                 /* e.g. 1h 12m */
1632                                 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1633                                               (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1634                         } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1635                                 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1636                                 snprintf(last_battery_time_str[idx],
1637                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1638                         } else {
1639                                 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1640                                         "discharging %d%%",
1641                                         (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1642                                 snprintf(last_battery_time_str[idx],
1643                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1644                         }
1645                 }
1646                 /* charged */
1647                 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1648                 else if (strncmp(charging_state, "Charged", 64) == 0) {
1649                                 /* Below happens with the second battery on my X40,
1650                                  * when the second one is empty and the first one
1651                                  * being charged. */
1652                                 if (remaining_capacity == 0)
1653                                         strcpy(last_battery_str[idx], "empty");
1654                                 else
1655                                         strcpy(last_battery_str[idx], "charged");
1656                 }
1657                 /* unknown, probably full / AC */
1658                 else {
1659                         if (acpi_last_full[idx] != 0
1660                             && remaining_capacity != acpi_last_full[idx])
1661                                 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1662                                         (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1663                         else
1664                                 strncpy(last_battery_str[idx], "AC", 64);
1665                 }
1666         } else if (acpi_bat_fp[idx] != NULL) {
1667                 /* ACPI */
1668                 int present_rate = -1;
1669                 int remaining_capacity = -1;
1670                 char charging_state[64];
1671                 char present[4];
1672
1673                 /* read last full capacity if it's zero */
1674                 if (acpi_last_full[idx] == 0) {
1675                         static int rep3 = 0;
1676                         char path[128];
1677                         FILE *fp;
1678
1679                         snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1680                         fp = open_file(path, &rep3);
1681                         if (fp != NULL) {
1682                                 while (!feof(fp)) {
1683                                         char b[256];
1684
1685                                         if (fgets(b, 256, fp) == NULL) {
1686                                                 break;
1687                                         }
1688                                         if (sscanf(b, "last full capacity: %d",
1689                                                         &acpi_last_full[idx]) != 0) {
1690                                                 break;
1691                                         }
1692                                 }
1693
1694                                 fclose(fp);
1695                         }
1696                 }
1697
1698                 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1699
1700                 strcpy(charging_state, "unknown");
1701
1702                 while (!feof(acpi_bat_fp[idx])) {
1703                         char buf[256];
1704
1705                         if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1706                                 break;
1707                         }
1708
1709                         /* let's just hope units are ok */
1710                         if (strncmp(buf, "present:", 8) == 0) {
1711                                 sscanf(buf, "present: %4s", present);
1712                         } else if (strncmp(buf, "charging state:", 15) == 0) {
1713                                 sscanf(buf, "charging state: %63s", charging_state);
1714                         } else if (strncmp(buf, "present rate:", 13) == 0) {
1715                                 sscanf(buf, "present rate: %d", &present_rate);
1716                         } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1717                                 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1718                         }
1719                 }
1720                 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1721                 if (remaining_capacity > acpi_last_full[idx]) {
1722                         /* normalize to 100% */
1723                         acpi_last_full[idx] = remaining_capacity;
1724                 }
1725
1726                 /* not present */
1727                 if (strcmp(present, "no") == 0) {
1728                         strncpy(last_battery_str[idx], "not present", 64);
1729                 /* charging */
1730                 } else if (strcmp(charging_state, "charging") == 0) {
1731                         if (acpi_last_full[idx] != 0 && present_rate > 0) {
1732                                 /* e.g. charging 75% */
1733                                 snprintf(last_battery_str[idx],
1734                                         sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1735                                         (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1736                                 /* e.g. 2h 37m */
1737                                 format_seconds(last_battery_time_str[idx],
1738                                         sizeof(last_battery_time_str[idx]) - 1,
1739                                         (long) (((acpi_last_full[idx] - remaining_capacity) *
1740                                         3600) / present_rate));
1741                         } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1742                                 snprintf(last_battery_str[idx],
1743                                         sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1744                                         (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1745                                 snprintf(last_battery_time_str[idx],
1746                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1747                         } else {
1748                                 strncpy(last_battery_str[idx], "charging",
1749                                         sizeof(last_battery_str[idx]) - 1);
1750                                 snprintf(last_battery_time_str[idx],
1751                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1752                         }
1753                 /* discharging */
1754                 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1755                         if (present_rate > 0) {
1756                                 /* e.g. discharging 35% */
1757                                 snprintf(last_battery_str[idx],
1758                                         sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1759                                         (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1760                                 /* e.g. 1h 12m */
1761                                 format_seconds(last_battery_time_str[idx],
1762                                         sizeof(last_battery_time_str[idx]) - 1,
1763                                         (long) ((remaining_capacity * 3600) / present_rate));
1764                         } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1765                                 snprintf(last_battery_str[idx],
1766                                         sizeof(last_battery_str[idx]) - 1, "full");
1767                                 snprintf(last_battery_time_str[idx],
1768                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1769                         } else {
1770                                 snprintf(last_battery_str[idx],
1771                                         sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1772                                         (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1773                                 snprintf(last_battery_time_str[idx],
1774                                         sizeof(last_battery_time_str[idx]) - 1, "unknown");
1775                         }
1776                 /* charged */
1777                 } else if (strncmp(charging_state, "charged", 64) == 0) {
1778                         /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1779                         /* Below happens with the second battery on my X40,
1780                          * when the second one is empty and the first one being charged. */
1781                         if (remaining_capacity == 0) {
1782                                 strcpy(last_battery_str[idx], "empty");
1783                         } else {
1784                                 strcpy(last_battery_str[idx], "charged");
1785                         }
1786                 /* unknown, probably full / AC */
1787                 } else {
1788                         if (acpi_last_full[idx] != 0
1789                                         && remaining_capacity != acpi_last_full[idx]) {
1790                                 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1791                                         (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1792                         } else {
1793                                 strncpy(last_battery_str[idx], "AC", 64);
1794                         }
1795                 }
1796         } else {
1797                 /* APM */
1798                 if (apm_bat_fp[idx] == NULL) {
1799                         apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1800                 }
1801
1802                 if (apm_bat_fp[idx] != NULL) {
1803                         unsigned int ac, status, flag;
1804                         int life;
1805
1806                         fscanf(apm_bat_fp[idx], "%*s %*s %*x %x   %x       %x     %d%%",
1807                                 &ac, &status, &flag, &life);
1808
1809                         if (life == -1) {
1810                                 /* could check now that there is ac */
1811                                 snprintf(last_battery_str[idx], 64, "AC");
1812
1813                         /* could check that status == 3 here? */
1814                         } else if (ac && life != 100) {
1815                                 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1816                         } else {
1817                                 snprintf(last_battery_str[idx], 64, "%d%%", life);
1818                         }
1819
1820                         /* it seemed to buffer it so file must be closed (or could use
1821                          * syscalls directly but I don't feel like coding it now) */
1822                         fclose(apm_bat_fp[idx]);
1823                         apm_bat_fp[idx] = NULL;
1824                 }
1825         }
1826
1827 set_return_value:
1828         switch (item) {
1829                 case BATTERY_STATUS:
1830                         snprintf(buffer, n, "%s", last_battery_str[idx]);
1831                         break;
1832                 case BATTERY_TIME:
1833                         snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1834                         break;
1835                 default:
1836                         break;
1837         }
1838 }
1839
1840 int get_battery_perct(const char *bat)
1841 {
1842         static int rep = 0;
1843         int idx;
1844         char acpi_path[128];
1845         char sysfs_path[128];
1846         int remaining_capacity = -1;
1847
1848         snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1849         snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1850
1851         init_batteries();
1852
1853         idx = get_battery_idx(bat);
1854
1855         /* don't update battery too often */
1856         if (current_update_time - last_battery_perct_time[idx] < 30) {
1857                 return last_battery_perct[idx];
1858         }
1859         last_battery_perct_time[idx] = current_update_time;
1860
1861         /* Only check for SYSFS or ACPI */
1862
1863         if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1864                 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1865                 rep = 0;
1866         }
1867
1868         if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1869                 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1870         }
1871
1872         if (sysfs_bat_fp[idx] != NULL) {
1873                 /* SYSFS */
1874                 while (!feof(sysfs_bat_fp[idx])) {
1875                         char buf[256];
1876                         if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1877                                 break;
1878
1879                         if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1880                                 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1881                         } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1882                                 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1883                         } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1884                                 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1885                         } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1886                                 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1887                         }
1888                 }
1889
1890                 fclose(sysfs_bat_fp[idx]);
1891                 sysfs_bat_fp[idx] = NULL;
1892
1893         } else if (acpi_bat_fp[idx] != NULL) {
1894                 /* ACPI */
1895                 /* read last full capacity if it's zero */
1896                 if (acpi_design_capacity[idx] == 0) {
1897                         static int rep2;
1898                         char path[128];
1899                         FILE *fp;
1900
1901                         snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1902                         fp = open_file(path, &rep2);
1903                         if (fp != NULL) {
1904                                 while (!feof(fp)) {
1905                                         char b[256];
1906
1907                                         if (fgets(b, 256, fp) == NULL) {
1908                                                 break;
1909                                         }
1910                                         if (sscanf(b, "last full capacity: %d",
1911                                                                 &acpi_design_capacity[idx]) != 0) {
1912                                                 break;
1913                                         }
1914                                 }
1915                                 fclose(fp);
1916                         }
1917                 }
1918
1919                 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1920
1921                 while (!feof(acpi_bat_fp[idx])) {
1922                         char buf[256];
1923
1924                         if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1925                                 break;
1926                         }
1927
1928                         if (buf[0] == 'r') {
1929                                 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1930                         }
1931                 }
1932         }
1933         if (remaining_capacity < 0) {
1934                 return 0;
1935         }
1936         /* compute the battery percentage */
1937         last_battery_perct[idx] =
1938                 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
1939         return last_battery_perct[idx];
1940 }
1941
1942 int get_battery_perct_bar(const char *bar)
1943 {
1944         int idx;
1945
1946         get_battery_perct(bar);
1947         idx = get_battery_idx(bar);
1948         return (int) (last_battery_perct[idx] * 2.56 - 1);
1949 }
1950
1951 /* On Apple powerbook and ibook:
1952 $ cat /proc/pmu/battery_0
1953 flags      : 00000013
1954 charge     : 3623
1955 max_charge : 3720
1956 current    : 388
1957 voltage    : 16787
1958 time rem.  : 900
1959 $ cat /proc/pmu/info
1960 PMU driver version     : 2
1961 PMU firmware version   : 0c
1962 AC Power               : 1
1963 Battery count          : 1
1964 */
1965
1966 /* defines as in <linux/pmu.h> */
1967 #define PMU_BATT_PRESENT                0x00000001
1968 #define PMU_BATT_CHARGING               0x00000002
1969
1970 static FILE *pmu_battery_fp;
1971 static FILE *pmu_info_fp;
1972 static char pb_battery_info[3][32];
1973 static double pb_battery_info_update;
1974
1975 #define PMU_PATH "/proc/pmu"
1976 void get_powerbook_batt_info(char *buffer, size_t n, int i)
1977 {
1978         static int rep = 0;
1979         const char *batt_path = PMU_PATH "/battery_0";
1980         const char *info_path = PMU_PATH "/info";
1981         unsigned int flags;
1982         int charge, max_charge, ac = -1;
1983         long timeval = -1;
1984
1985         /* don't update battery too often */
1986         if (current_update_time - pb_battery_info_update < 29.5) {
1987                 snprintf(buffer, n, "%s", pb_battery_info[i]);
1988                 return;
1989         }
1990         pb_battery_info_update = current_update_time;
1991
1992         if (pmu_battery_fp == NULL) {
1993                 pmu_battery_fp = open_file(batt_path, &rep);
1994         }
1995
1996         if (pmu_battery_fp != NULL) {
1997                 rewind(pmu_battery_fp);
1998                 while (!feof(pmu_battery_fp)) {
1999                         char buf[32];
2000
2001                         if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2002                                 break;
2003                         }
2004
2005                         if (buf[0] == 'f') {
2006                                 sscanf(buf, "flags      : %8x", &flags);
2007                         } else if (buf[0] == 'c' && buf[1] == 'h') {
2008                                 sscanf(buf, "charge     : %d", &charge);
2009                         } else if (buf[0] == 'm') {
2010                                 sscanf(buf, "max_charge : %d", &max_charge);
2011                         } else if (buf[0] == 't') {
2012                                 sscanf(buf, "time rem.  : %ld", &timeval);
2013                         }
2014                 }
2015         }
2016         if (pmu_info_fp == NULL) {
2017                 pmu_info_fp = open_file(info_path, &rep);
2018         }
2019
2020         if (pmu_info_fp != NULL) {
2021                 rewind(pmu_info_fp);
2022                 while (!feof(pmu_info_fp)) {
2023                         char buf[32];
2024
2025                         if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2026                                 break;
2027                         }
2028                         if (buf[0] == 'A') {
2029                                 sscanf(buf, "AC Power               : %d", &ac);
2030                         }
2031                 }
2032         }
2033         /* update status string */
2034         if ((ac && !(flags & PMU_BATT_PRESENT))) {
2035                 strcpy(pb_battery_info[PB_BATT_STATUS], "AC");
2036         } else if (ac && (flags & PMU_BATT_PRESENT)
2037                         && !(flags & PMU_BATT_CHARGING)) {
2038                 strcpy(pb_battery_info[PB_BATT_STATUS], "charged");
2039         } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2040                 strcpy(pb_battery_info[PB_BATT_STATUS], "charging");
2041         } else {
2042                 strcpy(pb_battery_info[PB_BATT_STATUS], "discharging");
2043         }
2044
2045         /* update percentage string */
2046         if (timeval == 0) {
2047                 pb_battery_info[PB_BATT_PERCENT][0] = 0;
2048         } else {
2049                 snprintf(pb_battery_info[PB_BATT_PERCENT],
2050                         sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2051                         (charge * 100) / max_charge);
2052         }
2053
2054         /* update time string */
2055         if (timeval == 0) {                     /* fully charged or battery not present */
2056                 pb_battery_info[PB_BATT_TIME][0] = 0;
2057         } else if (timeval < 60 * 60) { /* don't show secs */
2058                 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2059                         sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2060         } else {
2061                 format_seconds(pb_battery_info[PB_BATT_TIME],
2062                         sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2063         }
2064
2065         snprintf(buffer, n, "%s", pb_battery_info[i]);
2066 }
2067
2068 void update_top(void)
2069 {
2070         show_nice_processes = 1;
2071         process_find_top(info.cpu, info.memu);
2072         info.first_process = get_first_process();
2073 }
2074
2075 /* The following ifdefs were adapted from gkrellm */
2076 #include <linux/major.h>
2077
2078 #if !defined(MD_MAJOR)
2079 #define MD_MAJOR 9
2080 #endif
2081
2082 #if !defined(LVM_BLK_MAJOR)
2083 #define LVM_BLK_MAJOR 58
2084 #endif
2085
2086 #if !defined(NBD_MAJOR)
2087 #define NBD_MAJOR 43
2088 #endif
2089
2090 void update_diskio(void)
2091 {
2092         static unsigned int last = UINT_MAX;
2093         static unsigned int last_read = UINT_MAX;
2094         static unsigned int last_write = UINT_MAX;
2095         FILE *fp;
2096         static int rep = 0;
2097
2098         char buf[512], devbuf[64];
2099         int i;
2100         unsigned int major, minor;
2101         unsigned int current = 0;
2102         unsigned int current_read = 0;
2103         unsigned int current_write = 0;
2104         unsigned int reads, writes = 0;
2105         int col_count = 0;
2106         int tot, tot_read, tot_write;
2107
2108         if (!(fp = open_file("/proc/diskstats", &rep))) {
2109                 diskio_value = 0;
2110                 return;
2111         }
2112
2113         /* read reads and writes from all disks (minor = 0), including cd-roms
2114          * and floppies, and sum them up */
2115         while (!feof(fp)) {
2116                 fgets(buf, 512, fp);
2117                 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
2118                         &minor, devbuf, &reads, &writes);
2119                 /* ignore subdevices (they have only 3 matching entries in their line)
2120                  * and virtual devices (LVM, network block devices, RAM disks, Loopback)
2121                  *
2122                  * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
2123                 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
2124                                 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
2125                         current += reads + writes;
2126                         current_read += reads;
2127                         current_write += writes;
2128                 } else {
2129                         col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
2130                                 &major, &minor, devbuf, &reads, &writes);
2131                         if (col_count != 5) {
2132                                 continue;
2133                         }
2134                 }
2135                 for (i = 0; i < MAX_DISKIO_STATS; i++) {
2136                         if (diskio_stats[i].dev &&
2137                                         strncmp(devbuf, diskio_stats[i].dev, text_buffer_size) == 0) {
2138                                 diskio_stats[i].current =
2139                                         (reads + writes - diskio_stats[i].last) / 2;
2140                                 diskio_stats[i].current_read =
2141                                         (reads - diskio_stats[i].last_read) / 2;
2142                                 diskio_stats[i].current_write =
2143                                         (writes - diskio_stats[i].last_write) / 2;
2144                                 if (reads + writes < diskio_stats[i].last) {
2145                                         diskio_stats[i].current = 0;
2146                                 }
2147                                 if (reads < diskio_stats[i].last_read) {
2148                                         diskio_stats[i].current_read = 0;
2149                                         diskio_stats[i].current = diskio_stats[i].current_write;
2150                                 }
2151                                 if (writes < diskio_stats[i].last_write) {
2152                                         diskio_stats[i].current_write = 0;
2153                                         diskio_stats[i].current = diskio_stats[i].current_read;
2154                                 }
2155                                 diskio_stats[i].last = reads + writes;
2156                                 diskio_stats[i].last_read = reads;
2157                                 diskio_stats[i].last_write = writes;
2158                         }
2159                 }
2160         }
2161
2162         /* since the values in /proc/diststats are absolute, we have to substract
2163          * our last reading. The numbers stand for "sectors read", and we therefore
2164          * have to divide by two to get KB */
2165         tot = ((double) (current - last) / 2);
2166         tot_read = ((double) (current_read - last_read) / 2);
2167         tot_write = ((double) (current_write - last_write) / 2);
2168
2169         if (last_read > current_read) {
2170                 tot_read = 0;
2171         }
2172         if (last_write > current_write) {
2173                 tot_write = 0;
2174         }
2175
2176         if (last > current) {
2177                 /* we hit this either if it's the very first time we run this, or
2178                  * when /proc/diskstats overflows; while 0 is not correct, it's at
2179                  * least not way off */
2180                 tot = 0;
2181         }
2182         last = current;
2183         last_read = current_read;
2184         last_write = current_write;
2185
2186         diskio_value = tot;
2187         diskio_read_value = tot_read;
2188         diskio_write_value = tot_write;
2189
2190         fclose(fp);
2191 }
2192
2193 /* Here come the IBM ACPI-specific things. For reference, see
2194  * http://ibm-acpi.sourceforge.net/README
2195  * If IBM ACPI is installed, /proc/acpi/ibm contains the following files:
2196 bay
2197 beep
2198 bluetooth
2199 brightness
2200 cmos
2201 dock
2202 driver
2203 ecdump
2204 fan
2205 hotkey
2206 led
2207 light
2208 thermal
2209 video
2210 volume
2211  * The content of these files is described in detail in the aforementioned
2212  * README - some of them also in the following functions accessing them.
2213  * Peter Tarjan (ptarjan@citromail.hu) */
2214
2215 #define IBM_ACPI_DIR "/proc/acpi/ibm"
2216
2217 /* get fan speed on IBM/Lenovo laptops running the ibm acpi.
2218  * /proc/acpi/ibm/fan looks like this (3 lines):
2219 status:         disabled
2220 speed:          2944
2221 commands:       enable, disable
2222  * Peter Tarjan (ptarjan@citromail.hu) */
2223
2224 void get_ibm_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
2225 {
2226         FILE *fp;
2227         unsigned int speed = 0;
2228         char fan[128];
2229
2230         if (!p_client_buffer || client_buffer_size <= 0) {
2231                 return;
2232         }
2233
2234         snprintf(fan, 127, "%s/fan", IBM_ACPI_DIR);
2235
2236         fp = fopen(fan, "r");
2237         if (fp != NULL) {
2238                 while (!feof(fp)) {
2239                         char line[256];
2240
2241                         if (fgets(line, 255, fp) == NULL) {
2242                                 break;
2243                         }
2244                         if (sscanf(line, "speed: %u", &speed)) {
2245                                 break;
2246                         }
2247                 }
2248         } else {
2249                 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2250                         "ibm* from your Conky config file.", fan, strerror(errno));
2251         }
2252
2253         fclose(fp);
2254         snprintf(p_client_buffer, client_buffer_size, "%d", speed);
2255 }
2256
2257 /* get the measured temperatures from the temperature sensors
2258  * on IBM/Lenovo laptops running the ibm acpi.
2259  * There are 8 values in /proc/acpi/ibm/thermal, and according to
2260  * http://ibm-acpi.sourceforge.net/README
2261  * these mean the following (at least on an IBM R51...)
2262  * 0:  CPU (also on the T series laptops)
2263  * 1:  Mini PCI Module (?)
2264  * 2:  HDD (?)
2265  * 3:  GPU (also on the T series laptops)
2266  * 4:  Battery (?)
2267  * 5:  N/A
2268  * 6:  Battery (?)
2269  * 7:  N/A
2270  * I'm not too sure about those with the question mark, but the values I'm
2271  * reading from *my* thermal file (on a T42p) look realistic for the
2272  * hdd and the battery.
2273  * #5 and #7 are always -128.
2274  * /proc/acpi/ibm/thermal looks like this (1 line):
2275 temperatures:   41 43 31 46 33 -128 29 -128
2276  * Peter Tarjan (ptarjan@citromail.hu) */
2277
2278 static double last_ibm_acpi_temp_time;
2279 void get_ibm_acpi_temps(void)
2280 {
2281
2282         FILE *fp;
2283         char thermal[128];
2284
2285         /* don't update too often */
2286         if (current_update_time - last_ibm_acpi_temp_time < 10.00) {
2287                 return;
2288         }
2289         last_ibm_acpi_temp_time = current_update_time;
2290
2291         /* if (!p_client_buffer || client_buffer_size <= 0) {
2292                 return;
2293         } */
2294
2295         snprintf(thermal, 127, "%s/thermal", IBM_ACPI_DIR);
2296         fp = fopen(thermal, "r");
2297
2298         if (fp != NULL) {
2299                 while (!feof(fp)) {
2300                         char line[256];
2301
2302                         if (fgets(line, 255, fp) == NULL) {
2303                                 break;
2304                         }
2305                         if (sscanf(line, "temperatures: %d %d %d %d %d %d %d %d",
2306                                         &ibm_acpi.temps[0], &ibm_acpi.temps[1], &ibm_acpi.temps[2],
2307                                         &ibm_acpi.temps[3], &ibm_acpi.temps[4], &ibm_acpi.temps[5],
2308                                         &ibm_acpi.temps[6], &ibm_acpi.temps[7])) {
2309                                 break;
2310                         }
2311                 }
2312         } else {
2313                 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2314                         "ibm* from your Conky config file.", thermal, strerror(errno));
2315         }
2316
2317         fclose(fp);
2318 }
2319
2320 /* get volume (0-14) on IBM/Lenovo laptops running the ibm acpi.
2321  * "Volume" here is none of the mixer volumes, but a "master of masters"
2322  * volume adjusted by the IBM volume keys.
2323  * /proc/acpi/ibm/fan looks like this (4 lines):
2324 level:          4
2325 mute:           off
2326 commands:       up, down, mute
2327 commands:       level <level> (<level> is 0-15)
2328  * Peter Tarjan (ptarjan@citromail.hu) */
2329
2330 void get_ibm_acpi_volume(char *p_client_buffer, size_t client_buffer_size)
2331 {
2332         FILE *fp;
2333         char volume[128];
2334         unsigned int vol = -1;
2335         char mute[3] = "";
2336
2337         if (!p_client_buffer || client_buffer_size <= 0) {
2338                 return;
2339         }
2340
2341         snprintf(volume, 127, "%s/volume", IBM_ACPI_DIR);
2342
2343         fp = fopen(volume, "r");
2344         if (fp != NULL) {
2345                 while (!feof(fp)) {
2346                         char line[256];
2347                         unsigned int read_vol = -1;
2348
2349                         if (fgets(line, 255, fp) == NULL) {
2350                                 break;
2351                         }
2352                         if (sscanf(line, "level: %u", &read_vol)) {
2353                                 vol = read_vol;
2354                                 continue;
2355                         }
2356                         if (sscanf(line, "mute: %s", mute)) {
2357                                 break;
2358                         }
2359                 }
2360         } else {
2361                 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2362                         "ibm* from your Conky config file.", volume, strerror(errno));
2363         }
2364
2365         fclose(fp);
2366
2367         if (strcmp(mute, "on") == 0) {
2368                 snprintf(p_client_buffer, client_buffer_size, "%s", "mute");
2369                 return;
2370         } else {
2371                 snprintf(p_client_buffer, client_buffer_size, "%d", vol);
2372                 return;
2373         }
2374 }
2375
2376 /* static FILE *fp = NULL; */
2377
2378 /* get LCD brightness on IBM/Lenovo laptops running the ibm acpi.
2379  * /proc/acpi/ibm/brightness looks like this (3 lines):
2380 level:          7
2381 commands:       up, down
2382 commands:       level <level> (<level> is 0-7)
2383  * Peter Tarjan (ptarjan@citromail.hu) */
2384
2385 void get_ibm_acpi_brightness(char *p_client_buffer, size_t client_buffer_size)
2386 {
2387         FILE *fp;
2388         unsigned int brightness = 0;
2389         char filename[128];
2390
2391         if (!p_client_buffer || client_buffer_size <= 0) {
2392                 return;
2393         }
2394
2395         snprintf(filename, 127, "%s/brightness", IBM_ACPI_DIR);
2396
2397         fp = fopen(filename, "r");
2398         if (fp != NULL) {
2399                 while (!feof(fp)) {
2400                         char line[256];
2401
2402                         if (fgets(line, 255, fp) == NULL) {
2403                                 break;
2404                         }
2405                         if (sscanf(line, "level: %u", &brightness)) {
2406                                 break;
2407                         }
2408                 }
2409         } else {
2410                 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2411                         "ibm* from your Conky config file.", filename, strerror(errno));
2412         }
2413
2414         fclose(fp);
2415
2416         snprintf(p_client_buffer, client_buffer_size, "%d", brightness);
2417 }
2418
2419 void update_entropy(void)
2420 {
2421         static int rep = 0;
2422         const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2423         const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2424         FILE *fp1, *fp2;
2425
2426         info.entropy.entropy_avail = 0;
2427         info.entropy.poolsize = 0;
2428
2429         if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2430                 return;
2431         }
2432
2433         if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2434                 fclose(fp1);
2435                 return;
2436         }
2437
2438         fscanf(fp1, "%u", &info.entropy.entropy_avail);
2439         fscanf(fp2, "%u", &info.entropy.poolsize);
2440
2441         fclose(fp1);
2442         fclose(fp2);
2443
2444         info.mask |= (1 << INFO_ENTROPY);
2445 }
2446
2447 const char *get_disk_protect_queue(const char *disk)
2448 {
2449         FILE *fp;
2450         char path[128];
2451         int state;
2452
2453         snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2454         if ((fp = fopen(path, "r")) == NULL)
2455                 return "n/a   ";
2456         if (fscanf(fp, "%d\n", &state) != 1) {
2457                 fclose(fp);
2458                 return "failed";
2459         }
2460         fclose(fp);
2461         return state ? "frozen" : "free  ";
2462 }
2463