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