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