* Applied 2 patches:
[monky] / src / linux.c
1 /* Conky, a system monitor, based on torsmo
2  *
3  * Any original torsmo code is licensed under the BSD license
4  *
5  * All code written since the fork of torsmo is licensed under the GPL
6  *
7  * Please see COPYING for details
8  *
9  * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
10  * Copyright (c) 2007 Toni Spets
11  * Copyright (c) 2005-2007 Brenden Matthews, Philip Kovacs, et. al.
12  *      (see AUTHORS)
13  * All rights reserved.
14  *
15  * This program is free software: you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation, either version 3 of the License, or
18  * (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  * You should have received a copy of the GNU General Public License
25  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
26  *
27  * $Id$ */
28
29 #include "conky.h"
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <dirent.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <limits.h>
37 #include <sys/types.h>
38 #include <sys/sysinfo.h>
39 #include <sys/stat.h>
40 #ifndef HAVE_CLOCK_GETTIME
41 #include <sys/time.h>
42 #endif
43 #include <fcntl.h>
44 #include <unistd.h>
45 // #include <assert.h>
46 #include <time.h>
47 #include "top.h"
48
49 #include <sys/ioctl.h>
50 #include <sys/socket.h>
51 #include <netinet/in.h>
52 #include <linux/sockios.h>
53 #include <net/if.h>
54 #include <math.h>
55
56 #ifdef HAVE_IWLIB
57 #include <iwlib.h>
58 #endif
59
60 #define SHORTSTAT_TEMPL "%*s %llu %llu %llu"
61 #define LONGSTAT_TEMPL "%*s %llu %llu %llu "
62
63 static int show_nice_processes;
64
65 /* This flag tells the linux routines to use the /proc system where possible,
66  * even if other api's are available, e.g. sysinfo() or getloadavg().
67  * the reason for this is to allow for /proc-based distributed monitoring.
68  * using a flag in this manner creates less confusing code. */
69 static int prefer_proc = 0;
70
71 void prepare_update()
72 {
73 }
74
75 void update_uptime()
76 {
77 #ifdef HAVE_SYSINFO
78         if (!prefer_proc) {
79                 struct sysinfo s_info;
80
81                 sysinfo(&s_info);
82                 info.uptime = (double) s_info.uptime;
83         } else
84 #endif
85         {
86                 static int rep = 0;
87                 FILE *fp;
88
89                 if (!(fp = open_file("/proc/uptime", &rep))) {
90                         info.uptime = 0.0;
91                         return;
92                 }
93                 fscanf(fp, "%lf", &info.uptime);
94                 fclose(fp);
95         }
96         info.mask |= (1 << INFO_UPTIME);
97 }
98
99 int check_mount(char *s)
100 {
101         int ret = 0;
102         FILE *mtab = fopen("/etc/mtab", "r");
103
104         if (mtab) {
105                 char buf1[256], buf2[128];
106
107                 while (fgets(buf1, 256, mtab)) {
108                         sscanf(buf1, "%*s %128s", buf2);
109                         if (!strcmp(s, buf2)) {
110                                 ret = 1;
111                                 break;
112                         }
113                 }
114                 fclose(mtab);
115         } else {
116                 ERR("Could not open mtab");
117         }
118         return ret;
119 }
120
121 /* these things are also in sysinfo except Buffers:
122  * (that's why I'm reading them from proc) */
123
124 void update_meminfo()
125 {
126         FILE *meminfo_fp;
127         static int rep = 0;
128
129         /* unsigned int a; */
130         char buf[256];
131
132         info.mem = info.memmax = info.swap = info.swapmax = info.bufmem =
133                 info.buffers = info.cached = 0;
134
135         if (!(meminfo_fp = open_file("/proc/meminfo", &rep))) {
136                 return;
137         }
138
139         while (!feof(meminfo_fp)) {
140                 if (fgets(buf, 255, meminfo_fp) == NULL) {
141                         break;
142                 }
143
144                 if (strncmp(buf, "MemTotal:", 9) == 0) {
145                         sscanf(buf, "%*s %Lu", &info.memmax);
146                 } else if (strncmp(buf, "MemFree:", 8) == 0) {
147                         sscanf(buf, "%*s %Lu", &info.mem);
148                 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
149                         sscanf(buf, "%*s %Lu", &info.swapmax);
150                 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
151                         sscanf(buf, "%*s %Lu", &info.swap);
152                 } else if (strncmp(buf, "Buffers:", 8) == 0) {
153                         sscanf(buf, "%*s %Lu", &info.buffers);
154                 } else if (strncmp(buf, "Cached:", 7) == 0) {
155                         sscanf(buf, "%*s %Lu", &info.cached);
156                 }
157         }
158
159         info.mem = info.memmax - info.mem;
160         info.swap = info.swapmax - info.swap;
161
162         info.bufmem = info.cached + info.buffers;
163
164         info.mask |= (1 << INFO_MEM) | (1 << INFO_BUFFERS);
165
166         fclose(meminfo_fp);
167 }
168
169 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 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1295   Linux 2.6.24 onwards battery info is in
1296   /sys/class/power_supply/BAT0/
1297   On my system I get the following.
1298         /sys/class/power_supply/BAT0/uevent:
1299         PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1300         PHYSDEVBUS=acpi
1301         PHYSDEVDRIVER=battery
1302         POWER_SUPPLY_NAME=BAT0
1303         POWER_SUPPLY_TYPE=Battery
1304         POWER_SUPPLY_STATUS=Discharging
1305         POWER_SUPPLY_PRESENT=1
1306         POWER_SUPPLY_TECHNOLOGY=Li-ion
1307         POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1308         POWER_SUPPLY_VOLTAGE_NOW=10780000
1309         POWER_SUPPLY_CURRENT_NOW=13970000
1310         POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1311         POWER_SUPPLY_ENERGY_FULL=27370000
1312         POWER_SUPPLY_ENERGY_NOW=11810000
1313         POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1314         POWER_SUPPLY_MANUFACTURER=Panasonic
1315   On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1316 */
1317
1318 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1319 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1320 #define APM_PATH "/proc/apm"
1321 #define MAX_BATTERY_COUNT 4
1322
1323 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT];
1324 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT];
1325 static FILE *apm_bat_fp[MAX_BATTERY_COUNT];
1326
1327 static int batteries_initialized = 0;
1328 static char batteries[MAX_BATTERY_COUNT][32];
1329
1330 static int acpi_last_full[MAX_BATTERY_COUNT];
1331 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1332
1333 /* e.g. "charging 75%" */
1334 static char last_battery_str[MAX_BATTERY_COUNT][64];
1335 /* e.g. "3h 15m" */
1336 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1337
1338 static double last_battery_time[MAX_BATTERY_COUNT];
1339
1340 static int last_battery_perct[MAX_BATTERY_COUNT];
1341 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1342
1343 void init_batteries(void)
1344 {
1345         int idx;
1346
1347         if (batteries_initialized) {
1348                 return;
1349         }
1350         for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1351                 batteries[idx][0] = '\0';
1352         }
1353         batteries_initialized = 1;
1354 }
1355
1356 int get_battery_idx(const char *bat)
1357 {
1358         int idx;
1359
1360         for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1361                 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1362                         break;
1363                 }
1364         }
1365
1366         /* if not found, enter a new entry */
1367         if (!strlen(batteries[idx])) {
1368                 snprintf(batteries[idx], 31, "%s", bat);
1369         }
1370
1371         return idx;
1372 }
1373
1374 void get_battery_stuff(char *buf, unsigned int n, const char *bat, int item)
1375 {
1376         static int idx, rep = 0, rep2 = 0;
1377         char acpi_path[128];
1378
1379         snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1380         char sysfs_path[128];
1381         snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1382
1383         init_batteries();
1384
1385         idx = get_battery_idx(bat);
1386
1387         /* don't update battery too often */
1388         if (current_update_time - last_battery_time[idx] < 29.5) {
1389                 goto set_return_value;
1390         }
1391
1392         last_battery_time[idx] = current_update_time;
1393
1394         memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1395         memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1396
1397         /* first try SYSFS if that fails try ACPI */
1398  
1399         if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL)
1400                 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1401   
1402         if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL)
1403                 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1404   
1405         if (sysfs_bat_fp[idx] != NULL) {
1406                 /* SYSFS */
1407                 int present_rate = -1;
1408                 int remaining_capacity = -1;
1409                 char charging_state[64];
1410                 char present[4];
1411  
1412                 strcpy(charging_state, "Unknown");
1413  
1414                 while (!feof(sysfs_bat_fp[idx])) {
1415                         char buf[256];
1416                         if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1417                                 break;
1418  
1419                         /* let's just hope units are ok */
1420                         if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1421                                 strcpy(present, "Yes");
1422                         else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1423                                 strcpy(present, "No");
1424                         else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1425                                 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1426                         /* present_rate is not the same as the
1427                         current flowing now but it is the same value
1428                         which was used in the past. so we continue
1429                         the tradition! */
1430                         else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1431                                 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1432                         else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1433                                 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1434                         else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1435                                 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1436                         else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1437                                 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1438                         else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1439                                 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1440                 }
1441  
1442                 fclose(sysfs_bat_fp[idx]);
1443                 sysfs_bat_fp[idx] = NULL;
1444  
1445                 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1446                 if (remaining_capacity > acpi_last_full[idx])
1447                         acpi_last_full[idx] = remaining_capacity;  /* normalize to 100% */
1448  
1449                 /* not present */
1450                 if (strcmp(present, "No") == 0) {
1451                         strncpy(last_battery_str[idx], "not present", 64);
1452                 }
1453                 /* charging */
1454                 else if (strcmp(charging_state, "Charging") == 0) {
1455                         if (acpi_last_full[idx] != 0 && present_rate > 0) {
1456                                 /* e.g. charging 75% */
1457                                 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "Charging %i%%",
1458                                         (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1459                                 /* e.g. 2h 37m */
1460                                 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1461                                               (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1462                         } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1463                                 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "Charging %d%%",
1464                                         (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1465                         } else {
1466                                 strncpy(last_battery_str[idx], "Charging", sizeof(last_battery_str[idx])-1);
1467                         }
1468                 }
1469                 /* discharging */
1470                 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1471                         if (present_rate > 0) {
1472                                 /* e.g. discharging 35% */
1473                                 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "Discharging %i%%",
1474                                         (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1475                                 /* e.g. 1h 12m */
1476                                 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1477                                               (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1478                         } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1479                                 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "Full");
1480                         } else {
1481                                 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1482                                         "Discharging %d%%",
1483                                         (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1484                         }
1485                 }
1486                 /* charged */
1487                 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1488                 else if (strncmp(charging_state, "Charged", 64) == 0) {
1489                                 /* Below happens with the second battery on my X40,
1490                                  * when the second one is empty and the first one
1491                                  * being charged. */
1492                                 if (remaining_capacity == 0)
1493                                         strcpy(last_battery_str[idx], "Empty");
1494                                 else
1495                                         strcpy(last_battery_str[idx], "Charged");
1496                 }
1497                 /* unknown, probably full / AC */
1498                 else {
1499                         if (acpi_last_full[idx] != 0
1500                             && remaining_capacity != acpi_last_full[idx])
1501                                 snprintf(last_battery_str[idx], 64, "Unknown %d%%",
1502                                         (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1503                         else
1504                                 strncpy(last_battery_str[idx], "AC", 64);
1505                 }
1506         } else if (acpi_bat_fp[idx] != NULL) {
1507                 /* ACPI */
1508                 int present_rate = -1;
1509                 int remaining_capacity = -1;
1510                 char charging_state[64];
1511                 char present[4];
1512
1513                 /* read last full capacity if it's zero */
1514                 if (acpi_last_full[idx] == 0) {
1515                         static int rep = 0;
1516                         char path[128];
1517                         FILE *fp;
1518
1519                         snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1520                         fp = open_file(path, &rep);
1521                         if (fp != NULL) {
1522                                 while (!feof(fp)) {
1523                                         char b[256];
1524
1525                                         if (fgets(b, 256, fp) == NULL) {
1526                                                 break;
1527                                         }
1528                                         if (sscanf(b, "last full capacity: %d",
1529                                                         &acpi_last_full[idx]) != 0) {
1530                                                 break;
1531                                         }
1532                                 }
1533
1534                                 fclose(fp);
1535                         }
1536                 }
1537
1538                 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1539
1540                 strcpy(charging_state, "unknown");
1541
1542                 while (!feof(acpi_bat_fp[idx])) {
1543                         char buf[256];
1544
1545                         if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1546                                 break;
1547                         }
1548
1549                         /* let's just hope units are ok */
1550                         if (strncmp(buf, "present:", 8) == 0) {
1551                                 sscanf(buf, "present: %4s", present);
1552                         } else if (strncmp(buf, "charging state:", 15) == 0) {
1553                                 sscanf(buf, "charging state: %63s", charging_state);
1554                         } else if (strncmp(buf, "present rate:", 13) == 0) {
1555                                 sscanf(buf, "present rate: %d", &present_rate);
1556                         } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1557                                 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1558                         }
1559                 }
1560                 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1561                 if (remaining_capacity > acpi_last_full[idx]) {
1562                         /* normalize to 100% */
1563                         acpi_last_full[idx] = remaining_capacity;
1564                 }
1565
1566                 /* not present */
1567                 if (strcmp(present, "no") == 0) {
1568                         strncpy(last_battery_str[idx], "not present", 64);
1569                 /* charging */
1570                 } else if (strcmp(charging_state, "charging") == 0) {
1571                         if (acpi_last_full[idx] != 0 && present_rate > 0) {
1572                                 /* e.g. charging 75% */
1573                                 snprintf(last_battery_str[idx],
1574                                         sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1575                                         (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1576                                 /* e.g. 2h 37m */
1577                                 format_seconds(last_battery_time_str[idx],
1578                                         sizeof(last_battery_time_str[idx]) - 1,
1579                                         (long) (((acpi_last_full[idx] - remaining_capacity) *
1580                                         3600) / present_rate));
1581                         } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1582                                 snprintf(last_battery_str[idx],
1583                                         sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1584                                         (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1585                         } else {
1586                                 strncpy(last_battery_str[idx], "charging",
1587                                         sizeof(last_battery_str[idx]) - 1);
1588                         }
1589                 /* discharging */
1590                 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1591                         if (present_rate > 0) {
1592                                 /* e.g. discharging 35% */
1593                                 snprintf(last_battery_str[idx],
1594                                         sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1595                                         (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1596                                 /* e.g. 1h 12m */
1597                                 format_seconds(last_battery_time_str[idx],
1598                                         sizeof(last_battery_time_str[idx]) - 1,
1599                                         (long) ((remaining_capacity * 3600) / present_rate));
1600                         } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1601                                 snprintf(last_battery_str[idx],
1602                                         sizeof(last_battery_str[idx]) - 1, "full");
1603                         } else {
1604                                 snprintf(last_battery_str[idx],
1605                                         sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1606                                         (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1607                         }
1608                 /* charged */
1609                 } else if (strncmp(charging_state, "charged", 64) == 0) {
1610                         /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1611                         /* Below happens with the second battery on my X40,
1612                          * when the second one is empty and the first one being charged. */
1613                         if (remaining_capacity == 0) {
1614                                 strcpy(last_battery_str[idx], "empty");
1615                         } else {
1616                                 strcpy(last_battery_str[idx], "charged");
1617                         }
1618                 /* unknown, probably full / AC */
1619                 } else {
1620                         if (acpi_last_full[idx] != 0
1621                                         && remaining_capacity != acpi_last_full[idx]) {
1622                                 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1623                                         (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1624                         } else {
1625                                 strncpy(last_battery_str[idx], "AC", 64);
1626                         }
1627                 }
1628         } else {
1629                 /* APM */
1630                 if (apm_bat_fp[idx] == NULL) {
1631                         apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1632                 }
1633
1634                 if (apm_bat_fp[idx] != NULL) {
1635                         int ac, status, flag, life;
1636
1637                         fscanf(apm_bat_fp[idx], "%*s %*s %*x %x   %x       %x     %d%%",
1638                                 &ac, &status, &flag, &life);
1639
1640                         if (life == -1) {
1641                                 /* could check now that there is ac */
1642                                 snprintf(last_battery_str[idx], 64, "AC");
1643
1644                         /* could check that status == 3 here? */
1645                         } else if (ac && life != 100) {
1646                                 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1647                         } else {
1648                                 snprintf(last_battery_str[idx], 64, "%d%%", life);
1649                         }
1650
1651                         /* it seemed to buffer it so file must be closed (or could use
1652                          * syscalls directly but I don't feel like coding it now) */
1653                         fclose(apm_bat_fp[idx]);
1654                         apm_bat_fp[idx] = NULL;
1655                 }
1656         }
1657
1658 set_return_value:
1659         switch (item) {
1660                 case BATTERY_STATUS:
1661                         snprintf(buf, n, "%s", last_battery_str[idx]);
1662                         break;
1663                 case BATTERY_TIME:
1664                         snprintf(buf, n, "%s", last_battery_time_str[idx]);
1665                         break;
1666                 default:
1667                         break;
1668         }
1669 }
1670
1671 int get_battery_perct(const char *bat)
1672 {
1673         static int rep;
1674         int idx;
1675         char acpi_path[128];
1676
1677         snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1678         char sysfs_path[128];
1679         snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1680
1681         init_batteries();
1682
1683         idx = get_battery_idx(bat);
1684
1685         /* don't update battery too often */
1686         if (current_update_time - last_battery_perct_time[idx] < 30) {
1687                 return last_battery_perct[idx];
1688         }
1689         last_battery_perct_time[idx] = current_update_time;
1690
1691         /* Only check for SYSFS or ACPI */
1692
1693         if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL)
1694                 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1695
1696         if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL)
1697                 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1698
1699         int remaining_capacity = -1;
1700
1701         if (sysfs_bat_fp[idx] != NULL) {
1702                 /* SYSFS */
1703                 while (!feof(sysfs_bat_fp[idx])) {
1704                         char buf[256];
1705                         if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1706                                 break;
1707
1708                         if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1709                                 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1710                         else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) != 0)
1711                                 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1712                         else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1713                                 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1714                         else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) != 0)
1715                                 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1716                 }
1717
1718                 fclose(sysfs_bat_fp[idx]);
1719                 sysfs_bat_fp[idx] = NULL;
1720
1721         } else if (acpi_bat_fp[idx] != NULL) {
1722                 /* ACPI */
1723                 /* read last full capacity if it's zero */
1724                 if (acpi_design_capacity[idx] == 0) {
1725                         static int rep;
1726                         char path[128];
1727                         FILE *fp;
1728
1729                         snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1730                         fp = open_file(path, &rep);
1731                         if (fp != NULL) {
1732                                 while (!feof(fp)) {
1733                                         char b[256];
1734
1735                                         if (fgets(b, 256, fp) == NULL) {
1736                                                 break;
1737                                         }
1738                                         if (sscanf(b, "last full capacity: %d",
1739                                                                 &acpi_design_capacity[idx]) != 0) {
1740                                                 break;
1741                                         }
1742                                 }
1743                                 fclose(fp);
1744                         }
1745                 }
1746
1747                 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1748
1749                 while (!feof(acpi_bat_fp[idx])) {
1750                         char buf[256];
1751
1752                         if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1753                                 break;
1754                         }
1755
1756                         if (buf[0] == 'r') {
1757                                 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1758                         }
1759                 }
1760         }
1761         if (remaining_capacity < 0) {
1762                 return 0;
1763         }
1764         /* compute the battery percentage */
1765         last_battery_perct[idx] =
1766                 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
1767         return last_battery_perct[idx];
1768 }
1769
1770 int get_battery_perct_bar(const char *bar)
1771 {
1772         int idx;
1773
1774         get_battery_perct(bar);
1775         idx = get_battery_idx(bar);
1776         return (int) (last_battery_perct[idx] * 2.56 - 1);
1777 }
1778
1779 /* On Apple powerbook and ibook:
1780 $ cat /proc/pmu/battery_0
1781 flags      : 00000013
1782 charge     : 3623
1783 max_charge : 3720
1784 current    : 388
1785 voltage    : 16787
1786 time rem.  : 900
1787 $ cat /proc/pmu/info
1788 PMU driver version     : 2
1789 PMU firmware version   : 0c
1790 AC Power               : 1
1791 Battery count          : 1
1792 */
1793
1794 /* defines as in <linux/pmu.h> */
1795 #define PMU_BATT_PRESENT                0x00000001
1796 #define PMU_BATT_CHARGING               0x00000002
1797
1798 static FILE *pmu_battery_fp;
1799 static FILE *pmu_info_fp;
1800 static char pb_battery_info[3][32];
1801 static double pb_battery_info_update;
1802
1803 #define PMU_PATH "/proc/pmu"
1804 void get_powerbook_batt_info(char *buf, size_t n, int i)
1805 {
1806         static int rep = 0;
1807         const char *batt_path = PMU_PATH "/battery_0";
1808         const char *info_path = PMU_PATH "/info";
1809         int flags, charge, max_charge, ac = -1;
1810         long time = -1;
1811
1812         /* don't update battery too often */
1813         if (current_update_time - pb_battery_info_update < 29.5) {
1814                 snprintf(buf, n, "%s", pb_battery_info[i]);
1815                 return;
1816         }
1817         pb_battery_info_update = current_update_time;
1818
1819         if (pmu_battery_fp == NULL) {
1820                 pmu_battery_fp = open_file(batt_path, &rep);
1821         }
1822
1823         if (pmu_battery_fp != NULL) {
1824                 rewind(pmu_battery_fp);
1825                 while (!feof(pmu_battery_fp)) {
1826                         char buf[32];
1827
1828                         if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
1829                                 break;
1830                         }
1831
1832                         if (buf[0] == 'f') {
1833                                 sscanf(buf, "flags      : %8x", &flags);
1834                         } else if (buf[0] == 'c' && buf[1] == 'h') {
1835                                 sscanf(buf, "charge     : %d", &charge);
1836                         } else if (buf[0] == 'm') {
1837                                 sscanf(buf, "max_charge : %d", &max_charge);
1838                         } else if (buf[0] == 't') {
1839                                 sscanf(buf, "time rem.  : %ld", &time);
1840                         }
1841                 }
1842         }
1843         if (pmu_info_fp == NULL) {
1844                 pmu_info_fp = open_file(info_path, &rep);
1845         }
1846
1847         if (pmu_info_fp != NULL) {
1848                 rewind(pmu_info_fp);
1849                 while (!feof(pmu_info_fp)) {
1850                         char buf[32];
1851
1852                         if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
1853                                 break;
1854                         }
1855                         if (buf[0] == 'A') {
1856                                 sscanf(buf, "AC Power               : %d", &ac);
1857                         }
1858                 }
1859         }
1860         /* update status string */
1861         if ((ac && !(flags & PMU_BATT_PRESENT))) {
1862                 strcpy(pb_battery_info[PB_BATT_STATUS], "AC");
1863         } else if (ac && (flags & PMU_BATT_PRESENT)
1864                         && !(flags & PMU_BATT_CHARGING)) {
1865                 strcpy(pb_battery_info[PB_BATT_STATUS], "charged");
1866         } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
1867                 strcpy(pb_battery_info[PB_BATT_STATUS], "charging");
1868         } else {
1869                 strcpy(pb_battery_info[PB_BATT_STATUS], "discharging");
1870         }
1871
1872         /* update percentage string */
1873         if (time == 0) {
1874                 pb_battery_info[PB_BATT_PERCENT][0] = 0;
1875         } else {
1876                 snprintf(pb_battery_info[PB_BATT_PERCENT],
1877                         sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
1878                         (charge * 100) / max_charge);
1879         }
1880
1881         /* update time string */
1882         if (time == 0) {                        /* fully charged or battery not present */
1883                 pb_battery_info[PB_BATT_TIME][0] = 0;
1884         } else if (time < 60 * 60) {    /* don't show secs */
1885                 format_seconds_short(pb_battery_info[PB_BATT_TIME],
1886                         sizeof(pb_battery_info[PB_BATT_TIME]), time);
1887         } else {
1888                 format_seconds(pb_battery_info[PB_BATT_TIME],
1889                         sizeof(pb_battery_info[PB_BATT_TIME]), time);
1890         }
1891
1892         snprintf(buf, n, "%s", pb_battery_info[i]);
1893 }
1894
1895 void update_top()
1896 {
1897         show_nice_processes = 1;
1898         process_find_top(info.cpu, info.memu);
1899         info.first_process = get_first_process();
1900 }
1901
1902 /* The following ifdefs were adapted from gkrellm */
1903 #include <linux/major.h>
1904
1905 #if !defined(MD_MAJOR)
1906 #define MD_MAJOR 9
1907 #endif
1908
1909 #if !defined(LVM_BLK_MAJOR)
1910 #define LVM_BLK_MAJOR 58
1911 #endif
1912
1913 #if !defined(NBD_MAJOR)
1914 #define NBD_MAJOR 43
1915 #endif
1916
1917 void update_diskio()
1918 {
1919         static unsigned int last = UINT_MAX;
1920         static unsigned int last_read = UINT_MAX;
1921         static unsigned int last_write = UINT_MAX;
1922         FILE *fp;
1923         static int rep = 0;
1924
1925         char buf[512], devbuf[64];
1926         int major, minor, i;
1927         unsigned int current = 0;
1928         unsigned int current_read = 0;
1929         unsigned int current_write = 0;
1930         unsigned int reads, writes = 0;
1931         int col_count = 0;
1932
1933         if (!(fp = open_file("/proc/diskstats", &rep))) {
1934                 diskio_value = 0;
1935                 return;
1936         }
1937
1938         /* read reads and writes from all disks (minor = 0), including cd-roms
1939          * and floppies, and sum them up */
1940         while (!feof(fp)) {
1941                 fgets(buf, 512, fp);
1942                 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
1943                         &minor, devbuf, &reads, &writes);
1944                 /* ignore subdevices (they have only 3 matching entries in their line)
1945                  * and virtual devices (LVM, network block devices, RAM disks, Loopback)
1946                  *
1947                  * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
1948                 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
1949                                 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
1950                         current += reads + writes;
1951                         current_read += reads;
1952                         current_write += writes;
1953                 } else {
1954                         col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
1955                                 &major, &minor, devbuf, &reads, &writes);
1956                         if (col_count != 5) {
1957                                 continue;
1958                         }
1959                 }
1960                 for (i = 0; i < MAX_DISKIO_STATS; i++) {
1961                         if (diskio_stats[i].dev &&
1962                                         strcmp(devbuf, diskio_stats[i].dev) == 0) {
1963                                 diskio_stats[i].current =
1964                                         (reads + writes - diskio_stats[i].last) / 2;
1965                                 diskio_stats[i].current_read =
1966                                         (reads - diskio_stats[i].last_read) / 2;
1967                                 diskio_stats[i].current_write =
1968                                         (writes - diskio_stats[i].last_write) / 2;
1969                                 if (reads + writes < diskio_stats[i].last) {
1970                                         diskio_stats[i].current = 0;
1971                                 }
1972                                 if (reads < diskio_stats[i].last_read) {
1973                                         diskio_stats[i].current_read = 0;
1974                                         diskio_stats[i].current = diskio_stats[i].current_write;
1975                                 }
1976                                 if (writes < diskio_stats[i].last_write) {
1977                                         diskio_stats[i].current_write = 0;
1978                                         diskio_stats[i].current = diskio_stats[i].current_read;
1979                                 }
1980                                 diskio_stats[i].last = reads + writes;
1981                                 diskio_stats[i].last_read = reads;
1982                                 diskio_stats[i].last_write = writes;
1983                         }
1984                 }
1985         }
1986
1987         /* since the values in /proc/diststats are absolute, we have to substract
1988          * our last reading. The numbers stand for "sectors read", and we therefore
1989          * have to divide by two to get KB */
1990         int tot = ((double) (current - last) / 2);
1991         int tot_read = ((double) (current_read - last_read) / 2);
1992         int tot_write = ((double) (current_write - last_write) / 2);
1993
1994         if (last_read > current_read) {
1995                 tot_read = 0;
1996         }
1997         if (last_write > current_write) {
1998                 tot_write = 0;
1999         }
2000
2001         if (last > current) {
2002                 /* we hit this either if it's the very first time we run this, or
2003                  * when /proc/diskstats overflows; while 0 is not correct, it's at
2004                  * least not way off */
2005                 tot = 0;
2006         }
2007         last = current;
2008         last_read = current_read;
2009         last_write = current_write;
2010
2011         diskio_value = tot;
2012         diskio_read_value = tot_read;
2013         diskio_write_value = tot_write;
2014
2015         fclose(fp);
2016 }
2017
2018 /* Here come the IBM ACPI-specific things. For reference, see
2019  * http://ibm-acpi.sourceforge.net/README
2020  * If IBM ACPI is installed, /proc/acpi/ibm contains the following files:
2021 bay
2022 beep
2023 bluetooth
2024 brightness
2025 cmos
2026 dock
2027 driver
2028 ecdump
2029 fan
2030 hotkey
2031 led
2032 light
2033 thermal
2034 video
2035 volume
2036  * The content of these files is described in detail in the aforementioned
2037  * README - some of them also in the following functions accessing them.
2038  * Peter Tarjan (ptarjan@citromail.hu) */
2039
2040 #define IBM_ACPI_DIR "/proc/acpi/ibm"
2041
2042 /* get fan speed on IBM/Lenovo laptops running the ibm acpi.
2043  * /proc/acpi/ibm/fan looks like this (3 lines):
2044 status:         disabled
2045 speed:          2944
2046 commands:       enable, disable
2047  * Peter Tarjan (ptarjan@citromail.hu) */
2048
2049 void get_ibm_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
2050 {
2051         if (!p_client_buffer || client_buffer_size <= 0) {
2052                 return;
2053         }
2054
2055         FILE *fp;
2056         unsigned int speed = 0;
2057         char fan[128];
2058
2059         snprintf(fan, 127, "%s/fan", IBM_ACPI_DIR);
2060
2061         fp = fopen(fan, "r");
2062         if (fp != NULL) {
2063                 while (!feof(fp)) {
2064                         char line[256];
2065
2066                         if (fgets(line, 255, fp) == NULL) {
2067                                 break;
2068                         }
2069                         if (sscanf(line, "speed: %d", &speed)) {
2070                                 break;
2071                         }
2072                 }
2073         } else {
2074                 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2075                         "ibm* from your Conky config file.", fan, strerror(errno));
2076         }
2077
2078         fclose(fp);
2079         snprintf(p_client_buffer, client_buffer_size, "%d", speed);
2080 }
2081
2082 /* get the measured temperatures from the temperature sensors
2083  * on IBM/Lenovo laptops running the ibm acpi.
2084  * There are 8 values in /proc/acpi/ibm/thermal, and according to
2085  * http://ibm-acpi.sourceforge.net/README
2086  * these mean the following (at least on an IBM R51...)
2087  * 0:  CPU (also on the T series laptops)
2088  * 1:  Mini PCI Module (?)
2089  * 2:  HDD (?)
2090  * 3:  GPU (also on the T series laptops)
2091  * 4:  Battery (?)
2092  * 5:  N/A
2093  * 6:  Battery (?)
2094  * 7:  N/A
2095  * I'm not too sure about those with the question mark, but the values I'm
2096  * reading from *my* thermal file (on a T42p) look realistic for the
2097  * hdd and the battery.
2098  * #5 and #7 are always -128.
2099  * /proc/acpi/ibm/thermal looks like this (1 line):
2100 temperatures:   41 43 31 46 33 -128 29 -128
2101  * Peter Tarjan (ptarjan@citromail.hu) */
2102
2103 static double last_ibm_acpi_temp_time;
2104 void get_ibm_acpi_temps()
2105 {
2106
2107         /* don't update too often */
2108         if (current_update_time - last_ibm_acpi_temp_time < 10.00) {
2109                 return;
2110         }
2111         last_ibm_acpi_temp_time = current_update_time;
2112
2113         /* if (!p_client_buffer || client_buffer_size <= 0) {
2114                 return;
2115         } */
2116
2117         FILE *fp;
2118
2119         char thermal[128];
2120
2121         snprintf(thermal, 127, "%s/thermal", IBM_ACPI_DIR);
2122         fp = fopen(thermal, "r");
2123
2124         if (fp != NULL) {
2125                 while (!feof(fp)) {
2126                         char line[256];
2127
2128                         if (fgets(line, 255, fp) == NULL) {
2129                                 break;
2130                         }
2131                         if (sscanf(line, "temperatures: %d %d %d %d %d %d %d %d",
2132                                         &ibm_acpi.temps[0], &ibm_acpi.temps[1], &ibm_acpi.temps[2],
2133                                         &ibm_acpi.temps[3], &ibm_acpi.temps[4], &ibm_acpi.temps[5],
2134                                         &ibm_acpi.temps[6], &ibm_acpi.temps[7])) {
2135                                 break;
2136                         }
2137                 }
2138         } else {
2139                 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2140                         "ibm* from your Conky config file.", thermal, strerror(errno));
2141         }
2142
2143         fclose(fp);
2144 }
2145
2146 /* get volume (0-14) on IBM/Lenovo laptops running the ibm acpi.
2147  * "Volume" here is none of the mixer volumes, but a "master of masters"
2148  * volume adjusted by the IBM volume keys.
2149  * /proc/acpi/ibm/fan looks like this (4 lines):
2150 level:          4
2151 mute:           off
2152 commands:       up, down, mute
2153 commands:       level <level> (<level> is 0-15)
2154  * Peter Tarjan (ptarjan@citromail.hu) */
2155
2156 void get_ibm_acpi_volume(char *p_client_buffer, size_t client_buffer_size)
2157 {
2158         if (!p_client_buffer || client_buffer_size <= 0) {
2159                 return;
2160         }
2161
2162         FILE *fp;
2163
2164         char volume[128];
2165
2166         snprintf(volume, 127, "%s/volume", IBM_ACPI_DIR);
2167         unsigned int vol = -1;
2168         char mute[3] = "";
2169
2170         fp = fopen(volume, "r");
2171         if (fp != NULL) {
2172                 while (!feof(fp)) {
2173                         char line[256];
2174                         unsigned int read_vol = -1;
2175
2176                         if (fgets(line, 255, fp) == NULL) {
2177                                 break;
2178                         }
2179                         if (sscanf(line, "level: %d", &read_vol)) {
2180                                 vol = read_vol;
2181                                 continue;
2182                         }
2183                         if (sscanf(line, "mute: %s", mute)) {
2184                                 break;
2185                         }
2186                 }
2187         } else {
2188                 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2189                         "ibm* from your Conky config file.", volume, strerror(errno));
2190         }
2191
2192         fclose(fp);
2193
2194         if (strcmp(mute, "on") == 0) {
2195                 snprintf(p_client_buffer, client_buffer_size, "%s", "mute");
2196                 return;
2197         } else {
2198                 snprintf(p_client_buffer, client_buffer_size, "%d", vol);
2199                 return;
2200         }
2201 }
2202
2203 /* static FILE *fp = NULL; */
2204
2205 /* get LCD brightness on IBM/Lenovo laptops running the ibm acpi.
2206  * /proc/acpi/ibm/brightness looks like this (3 lines):
2207 level:          7
2208 commands:       up, down
2209 commands:       level <level> (<level> is 0-7)
2210  * Peter Tarjan (ptarjan@citromail.hu) */
2211
2212 void get_ibm_acpi_brightness(char *p_client_buffer, size_t client_buffer_size)
2213 {
2214         if (!p_client_buffer || client_buffer_size <= 0) {
2215                 return;
2216         }
2217
2218         FILE *fp;
2219         unsigned int brightness = 0;
2220         char filename[128];
2221
2222         snprintf(filename, 127, "%s/brightness", IBM_ACPI_DIR);
2223
2224         fp = fopen(filename, "r");
2225         if (fp != NULL) {
2226                 while (!feof(fp)) {
2227                         char line[256];
2228
2229                         if (fgets(line, 255, fp) == NULL) {
2230                                 break;
2231                         }
2232                         if (sscanf(line, "level: %d", &brightness)) {
2233                                 break;
2234                         }
2235                 }
2236         } else {
2237                 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2238                         "ibm* from your Conky config file.", filename, strerror(errno));
2239         }
2240
2241         fclose(fp);
2242
2243         snprintf(p_client_buffer, client_buffer_size, "%d", brightness);
2244 }
2245
2246 void update_entropy(void)
2247 {
2248         static int rep = 0;
2249         const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2250         const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2251         FILE *fp1, *fp2;
2252
2253         info.entropy.entropy_avail = 0;
2254         info.entropy.poolsize = 0;
2255
2256         if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2257                 return;
2258         }
2259
2260         if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2261                 fclose(fp1);
2262                 return;
2263         }
2264
2265         fscanf(fp1, "%u", &info.entropy.entropy_avail);
2266         fscanf(fp2, "%u", &info.entropy.poolsize);
2267
2268         fclose(fp1);
2269         fclose(fp2);
2270
2271         info.mask |= (1 << INFO_ENTROPY);
2272 }