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