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