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