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