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