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
4 * Conky, a system monitor, based on torsmo
6 * Any original torsmo code is licensed under the BSD license
8 * All code written since the fork of torsmo is licensed under the GPL
10 * Please see COPYING for details
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.
16 * All rights reserved.
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.
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/>.
37 #include "temphelper.h"
42 #include <sys/types.h>
43 #include <sys/sysinfo.h>
45 #ifndef HAVE_CLOCK_GETTIME
50 // #include <assert.h>
54 #include <sys/ioctl.h>
55 #include <sys/socket.h>
56 #include <netinet/in.h>
57 #include <linux/sockios.h>
59 #include <arpa/inet.h>
63 #include <linux/route.h>
66 /* The following ifdefs were adapted from gkrellm */
67 #include <linux/major.h>
69 #if !defined(MD_MAJOR)
73 #if !defined(LVM_BLK_MAJOR)
74 #define LVM_BLK_MAJOR 58
77 #if !defined(NBD_MAJOR)
85 #define SHORTSTAT_TEMPL "%*s %llu %llu %llu"
86 #define LONGSTAT_TEMPL "%*s %llu %llu %llu "
88 /* This flag tells the linux routines to use the /proc system where possible,
89 * even if other api's are available, e.g. sysinfo() or getloadavg().
90 * the reason for this is to allow for /proc-based distributed monitoring.
91 * using a flag in this manner creates less confusing code. */
92 static int prefer_proc = 0;
94 void prepare_update(void)
98 void update_uptime(void)
102 struct sysinfo s_info;
105 info.uptime = (double) s_info.uptime;
112 if (!(fp = open_file("/proc/uptime", &rep))) {
116 fscanf(fp, "%lf", &info.uptime);
121 int check_mount(char *s)
124 FILE *mtab = fopen("/etc/mtab", "r");
127 char buf1[256], buf2[128];
129 while (fgets(buf1, 256, mtab)) {
130 sscanf(buf1, "%*s %128s", buf2);
131 if (!strcmp(s, buf2)) {
138 NORM_ERR("Could not open mtab");
143 /* these things are also in sysinfo except Buffers:
144 * (that's why I'm reading them from proc) */
146 void update_meminfo(void)
151 /* unsigned int a; */
154 info.mem = info.memmax = info.swap = info.swapfree = info.swapmax = info.bufmem =
155 info.buffers = info.cached = info.memfree = info.memeasyfree = 0;
157 if (!(meminfo_fp = open_file("/proc/meminfo", &rep))) {
161 while (!feof(meminfo_fp)) {
162 if (fgets(buf, 255, meminfo_fp) == NULL) {
166 if (strncmp(buf, "MemTotal:", 9) == 0) {
167 sscanf(buf, "%*s %llu", &info.memmax);
168 } else if (strncmp(buf, "MemFree:", 8) == 0) {
169 sscanf(buf, "%*s %llu", &info.memfree);
170 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
171 sscanf(buf, "%*s %llu", &info.swapmax);
172 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
173 sscanf(buf, "%*s %llu", &info.swapfree);
174 } else if (strncmp(buf, "Buffers:", 8) == 0) {
175 sscanf(buf, "%*s %llu", &info.buffers);
176 } else if (strncmp(buf, "Cached:", 7) == 0) {
177 sscanf(buf, "%*s %llu", &info.cached);
181 info.mem = info.memmax - info.memfree;
182 info.memeasyfree = info.memfree;
183 info.swap = info.swapmax - info.swapfree;
185 info.bufmem = info.cached + info.buffers;
190 int get_laptop_mode(void)
195 if ((fp = fopen("/proc/sys/vm/laptop_mode", "r")) != NULL)
196 fscanf(fp, "%d\n", &val);
202 * # cat /sys/block/sda/queue/scheduler
203 * noop [anticipatory] cfq
205 char *get_ioscheduler(char *disk)
211 return strndup("n/a", text_buffer_size);
213 snprintf(buf, 127, "/sys/block/%s/queue/scheduler", disk);
214 if ((fp = fopen(buf, "r")) == NULL) {
215 return strndup("n/a", text_buffer_size);
218 fscanf(fp, "%127s", buf);
220 buf[strlen(buf) - 1] = '\0';
222 return strndup(buf + 1, text_buffer_size);
226 return strndup("n/a", text_buffer_size);
229 #define COND_FREE(x) if(x) free(x); x = 0
230 #define SAVE_SET_STRING(x, y) \
231 if (x && strcmp((char *)x, (char *)y)) { \
233 x = strndup("multiple", text_buffer_size); \
235 x = strndup(y, text_buffer_size); \
238 void update_gateway_info_failure(const char *reason)
243 //2 pointers to 1 location causes a crash when we try to free them both
244 info.gw_info.iface = strndup("failed", text_buffer_size);
245 info.gw_info.ip = strndup("failed", text_buffer_size);
249 /* Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT */
250 #define RT_ENTRY_FORMAT "%63s %lx %lx %x %*d %*d %*d %lx %*d %*d %*d\n"
252 void update_gateway_info(void)
257 unsigned long dest, gate, mask;
260 struct gateway_info *gw_info = &info.gw_info;
262 COND_FREE(gw_info->iface);
263 COND_FREE(gw_info->ip);
266 if ((fp = fopen("/proc/net/route", "r")) == NULL) {
267 update_gateway_info_failure("fopen()");
271 /* skip over the table header line, which is always present */
272 fscanf(fp, "%*[^\n]\n");
275 if(fscanf(fp, RT_ENTRY_FORMAT,
276 iface, &dest, &gate, &flags, &mask) != 5) {
277 update_gateway_info_failure("fscanf()");
280 if (!(dest || mask) && ((flags & RTF_GATEWAY) || !gate) ) {
282 SAVE_SET_STRING(gw_info->iface, iface)
284 SAVE_SET_STRING(gw_info->ip, inet_ntoa(ina))
291 void update_net_stats(void)
295 static char first = 1;
297 // FIXME: arbitrary size chosen to keep code simple.
299 unsigned int curtmp1, curtmp2;
306 // wireless info variables
307 int skfd, has_bitrate = 0;
308 struct wireless_info *winfo;
313 delta = current_update_time - last_update_time;
314 if (delta <= 0.0001) {
318 /* open file and ignore first two lines */
319 if (!(net_dev_fp = open_file("/proc/net/dev", &rep))) {
324 fgets(buf, 255, net_dev_fp); /* garbage */
325 fgets(buf, 255, net_dev_fp); /* garbage (field names) */
327 /* read each interface */
328 for (i2 = 0; i2 < 16; i2++) {
332 long long r, t, last_recv, last_trans;
334 if (fgets(buf, 255, net_dev_fp) == NULL) {
338 while (isspace((int) *p)) {
344 while (*p && *p != ':') {
353 ns = get_net_stat(s, NULL, NULL);
355 memset(&(ns->addr.sa_data), 0, 14);
357 memset(ns->addrs, 0, 17 * 16 + 1); /* Up to 17 chars per ip, max 16 interfaces. Nasty memory usage... */
359 last_recv = ns->recv;
360 last_trans = ns->trans;
362 /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
363 sscanf(p, "%lld %*d %*d %*d %*d %*d %*d %*d %lld",
366 /* if recv or trans is less than last time, an overflow happened */
367 if (r < ns->last_read_recv) {
370 ns->recv += (r - ns->last_read_recv);
372 ns->last_read_recv = r;
374 if (t < ns->last_read_trans) {
377 ns->trans += (t - ns->last_read_trans);
379 ns->last_read_trans = t;
381 /*** ip addr patch ***/
382 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
384 conf.ifc_buf = malloc(sizeof(struct ifreq) * 16);
385 conf.ifc_len = sizeof(struct ifreq) * 16;
386 memset(conf.ifc_buf, 0, conf.ifc_len);
388 ioctl((long) i, SIOCGIFCONF, &conf);
390 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
391 struct net_stat *ns2;
393 if (!(((struct ifreq *) conf.ifc_buf) + k))
397 ((struct ifreq *) conf.ifc_buf)[k].ifr_ifrn.ifrn_name, NULL, NULL);
398 ns2->addr = ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.ifru_addr;
399 sprintf(temp_addr, "%u.%u.%u.%u, ",
400 ns2->addr.sa_data[2] & 255,
401 ns2->addr.sa_data[3] & 255,
402 ns2->addr.sa_data[4] & 255,
403 ns2->addr.sa_data[5] & 255);
404 if(NULL == strstr(ns2->addrs, temp_addr))
405 strncpy(ns2->addrs + strlen(ns2->addrs), temp_addr, 17);
412 /*** end ip addr patch ***/
415 /* calculate speeds */
416 ns->net_rec[0] = (ns->recv - last_recv) / delta;
417 ns->net_trans[0] = (ns->trans - last_trans) / delta;
424 #pragma omp parallel for reduction(+:curtmp1, curtmp2) schedule(dynamic,10)
425 #endif /* HAVE_OPENMP */
426 for (i = 0; i < info.net_avg_samples; i++) {
427 curtmp1 = curtmp1 + ns->net_rec[i];
428 curtmp2 = curtmp2 + ns->net_trans[i];
436 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
437 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
438 if (info.net_avg_samples > 1) {
440 #pragma omp parallel for schedule(dynamic,10)
441 #endif /* HAVE_OPENMP */
442 for (i = info.net_avg_samples; i > 1; i--) {
443 ns->net_rec[i - 1] = ns->net_rec[i - 2];
444 ns->net_trans[i - 1] = ns->net_trans[i - 2];
449 /* update wireless info */
450 winfo = malloc(sizeof(struct wireless_info));
451 memset(winfo, 0, sizeof(struct wireless_info));
453 skfd = iw_sockets_open();
454 if (iw_get_basic_config(skfd, s, &(winfo->b)) > -1) {
456 // set present winfo variables
457 if (iw_get_stats(skfd, s, &(winfo->stats),
458 &winfo->range, winfo->has_range) >= 0) {
459 winfo->has_stats = 1;
461 if (iw_get_range_info(skfd, s, &(winfo->range)) >= 0) {
462 winfo->has_range = 1;
464 if (iw_get_ext(skfd, s, SIOCGIWAP, &wrq) >= 0) {
465 winfo->has_ap_addr = 1;
466 memcpy(&(winfo->ap_addr), &(wrq.u.ap_addr), sizeof(sockaddr));
470 if (iw_get_ext(skfd, s, SIOCGIWRATE, &wrq) >= 0) {
471 memcpy(&(winfo->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
472 iw_print_bitrate(ns->bitrate, 16, winfo->bitrate.value);
477 if (winfo->has_range && winfo->has_stats
478 && ((winfo->stats.qual.level != 0)
479 || (winfo->stats.qual.updated & IW_QUAL_DBM))) {
480 if (!(winfo->stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
481 ns->link_qual = winfo->stats.qual.qual;
482 ns->link_qual_max = winfo->range.max_qual.qual;
487 if (winfo->has_ap_addr) {
488 iw_sawap_ntop(&winfo->ap_addr, ns->ap);
492 if (winfo->b.has_essid) {
493 if (winfo->b.essid_on) {
494 snprintf(ns->essid, 32, "%s", winfo->b.essid);
496 snprintf(ns->essid, 32, "off/any");
500 snprintf(ns->mode, 16, "%s", iw_operation_mode[winfo->b.mode]);
502 iw_sockets_close(skfd);
513 void update_total_processes(void)
517 struct sysinfo s_info;
520 info.procs = s_info.procs;
527 if (!(fp = open_file("/proc/loadavg", &rep))) {
531 fscanf(fp, "%*f %*f %*f %*d/%hu", &info.procs);
536 #define CPU_SAMPLE_COUNT 15
538 unsigned long long cpu_user;
539 unsigned long long cpu_system;
540 unsigned long long cpu_nice;
541 unsigned long long cpu_idle;
542 unsigned long long cpu_iowait;
543 unsigned long long cpu_irq;
544 unsigned long long cpu_softirq;
545 unsigned long long cpu_steal;
546 unsigned long long cpu_total;
547 unsigned long long cpu_active_total;
548 unsigned long long cpu_last_total;
549 unsigned long long cpu_last_active_total;
550 double cpu_val[CPU_SAMPLE_COUNT];
552 static short cpu_setup = 0;
554 /* Determine if this kernel gives us "extended" statistics information in
556 * Kernels around 2.5 and earlier only reported user, system, nice, and
557 * idle values in proc stat.
558 * Kernels around 2.6 and greater report these PLUS iowait, irq, softirq,
560 void determine_longstat(char *buf)
562 unsigned long long iowait = 0;
564 KFLAG_SETOFF(KFLAG_IS_LONGSTAT);
565 /* scanf will either return -1 or 1 because there is only 1 assignment */
566 if (sscanf(buf, "%*s %*d %*d %*d %*d %llu", &iowait) > 0) {
567 KFLAG_SETON(KFLAG_IS_LONGSTAT);
571 void get_cpu_count(void)
577 if (info.cpu_usage) {
581 if (!(stat_fp = open_file("/proc/stat", &rep))) {
587 while (!feof(stat_fp)) {
588 if (fgets(buf, 255, stat_fp) == NULL) {
592 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
593 if (info.cpu_count == 0) {
594 determine_longstat(buf);
599 info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
604 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
605 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
607 inline static void update_stat(void)
611 static struct cpu_info *cpu = NULL;
616 const char *stat_template = NULL;
617 unsigned int malloc_cpu_size = 0;
618 extern void* global_cpu;
619 static double last_stat_update = 0.0;
621 /* since we use wrappers for this function, the update machinery
622 * can't eliminate double invocations of this function. Check for
623 * them here, otherwise cpu_usage counters are freaking out. */
624 if (last_stat_update == current_update_time)
626 last_stat_update = current_update_time;
628 /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
629 if (!cpu_setup || !info.cpu_usage) {
634 if (!stat_template) {
636 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
640 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
641 cpu = malloc(malloc_cpu_size);
642 memset(cpu, 0, malloc_cpu_size);
646 if (!(stat_fp = open_file("/proc/stat", &rep))) {
648 if (info.cpu_usage) {
649 memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
655 while (!feof(stat_fp)) {
656 if (fgets(buf, 255, stat_fp) == NULL) {
660 if (strncmp(buf, "procs_running ", 14) == 0) {
661 sscanf(buf, "%*s %hu", &info.run_procs);
662 } else if (strncmp(buf, "cpu", 3) == 0) {
664 if (isdigit(buf[3])) {
665 idx = atoi(&buf[3]) + 1;
669 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
670 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
671 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
672 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
673 &(cpu[idx].cpu_steal));
675 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
676 cpu[idx].cpu_system + cpu[idx].cpu_idle +
677 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
678 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
680 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
681 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
683 delta = current_update_time - last_update_time;
685 if (delta <= 0.001) {
689 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
690 cpu[idx].cpu_last_active_total) /
691 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
694 #pragma omp parallel for reduction(+:curtmp) schedule(dynamic,10)
695 #endif /* HAVE_OPENMP */
696 for (i = 0; i < info.cpu_avg_samples; i++) {
697 curtmp = curtmp + cpu[idx].cpu_val[i];
699 /* TESTING -- I've removed this, because I don't think it is right.
700 * You shouldn't divide by the cpu count here ...
701 * removing for testing */
703 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
706 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
708 /* TESTING -- this line replaces the prev. "suspect" if/else */
709 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
711 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
712 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
714 #pragma omp parallel for schedule(dynamic,10)
715 #endif /* HAVE_OPENMP */
716 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
717 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
724 void update_running_processes(void)
729 void update_cpu_usage(void)
734 void update_load_average(void)
736 #ifdef HAVE_GETLOADAVG
741 info.loadavg[0] = (float) v[0];
742 info.loadavg[1] = (float) v[1];
743 info.loadavg[2] = (float) v[2];
750 if (!(fp = open_file("/proc/loadavg", &rep))) {
751 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
754 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
760 #define PROC_I8K "/proc/i8k"
761 #define I8K_DELIM " "
762 static char *i8k_procbuf = NULL;
763 void update_i8k(void)
768 i8k_procbuf = (char *) malloc(128 * sizeof(char));
770 if ((fp = fopen(PROC_I8K, "r")) == NULL) {
771 CRIT_ERR(NULL, NULL, "/proc/i8k doesn't exist! use insmod to make sure the kernel "
772 "driver is loaded...");
775 memset(&i8k_procbuf[0], 0, 128);
776 if (fread(&i8k_procbuf[0], sizeof(char), 128, fp) == 0) {
777 NORM_ERR("something wrong with /proc/i8k...");
782 i8k.version = strtok(&i8k_procbuf[0], I8K_DELIM);
783 i8k.bios = strtok(NULL, I8K_DELIM);
784 i8k.serial = strtok(NULL, I8K_DELIM);
785 i8k.cpu_temp = strtok(NULL, I8K_DELIM);
786 i8k.left_fan_status = strtok(NULL, I8K_DELIM);
787 i8k.right_fan_status = strtok(NULL, I8K_DELIM);
788 i8k.left_fan_rpm = strtok(NULL, I8K_DELIM);
789 i8k.right_fan_rpm = strtok(NULL, I8K_DELIM);
790 i8k.ac_status = strtok(NULL, I8K_DELIM);
791 i8k.buttons_status = strtok(NULL, I8K_DELIM);
794 /***********************************************************/
795 /***********************************************************/
796 /***********************************************************/
798 static int no_dots(const struct dirent *d)
800 if (d->d_name[0] == '.') {
806 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
808 struct dirent **namelist;
811 n = scandir(dir, &namelist, no_dots, alphasort);
814 NORM_ERR("scandir for %s: %s", dir, strerror(errno));
825 strncpy(s, namelist[0]->d_name, 255);
829 #pragma omp parallel for schedule(dynamic,10)
830 #endif /* HAVE_OPENMP */
831 for (i = 0; i < n; i++) {
840 static int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
841 int *divisor, char *devtype)
848 memset(buf, 0, sizeof(buf));
850 /* if device is NULL or *, get first */
851 if (dev == NULL || strcmp(dev, "*") == 0) {
854 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
860 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
862 /* buf holds result from get_first_file_in_a_directory() above,
863 * e.g. "hwmon0" -- append "/device" */
864 strcat(buf, "/device");
866 /* dev holds device number N as a string,
867 * e.g. "0", -- convert to "hwmon0/device" */
868 sprintf(buf, "hwmon%s/device", dev);
873 /* change vol to in, tempf to temp */
874 if (strcmp(type, "vol") == 0) {
876 } else if (strcmp(type, "tempf") == 0) {
881 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
883 /* first, attempt to open file in /device */
884 fd = open(path, O_RDONLY);
887 /* if it fails, strip the /device from dev and attempt again */
888 buf[strlen(buf) - 7] = 0;
889 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
890 fd = open(path, O_RDONLY);
892 CRIT_ERR(NULL, NULL, "can't open '%s': %s\nplease check your device or remove this "
893 "var from "PACKAGE_NAME, path, strerror(errno));
897 strncpy(devtype, path, 255);
899 if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
900 || strcmp(type, "tempf") == 0) {
905 /* fan does not use *_div as a read divisor */
906 if (strcmp("fan", type) == 0) {
910 /* test if *_div file exist, open it and use it as divisor */
911 if (strcmp(type, "tempf") == 0) {
912 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
914 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
917 divfd = open(path, O_RDONLY);
923 divn = read(divfd, divbuf, 63);
924 /* should read until n == 0 but I doubt that kernel will give these
925 * in multiple pieces. :) */
927 NORM_ERR("open_sysfs_sensor(): can't read from sysfs");
930 *divisor = atoi(divbuf);
938 static double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
946 lseek(*fd, 0, SEEK_SET);
952 n = read(*fd, buf, 63);
953 /* should read until n == 0 but I doubt that kernel will give these
954 * in multiple pieces. :) */
956 NORM_ERR("get_sysfs_info(): read from %s failed\n", devtype);
965 *fd = open(devtype, O_RDONLY);
967 NORM_ERR("can't open '%s': %s", devtype, strerror(errno));
970 /* My dirty hack for computing CPU value
971 * Filedil, from forums.gentoo.org */
972 /* if (strstr(devtype, "temp1_input") != NULL) {
973 return -15.096 + 1.4893 * (val / 1000.0);
976 /* divide voltage and temperature by 1000 */
977 /* or if any other divisor is given, use that */
978 if (strcmp(type, "tempf") == 0) {
980 return ((val / divisor + 40) * 9.0 / 5) - 40;
981 } else if (divisor) {
982 return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
984 return ((val + 40) * 9.0 / 5) - 40;
988 return val / divisor;
989 } else if (divisor) {
997 #define HWMON_RESET() {\
1002 static void parse_sysfs_sensor(struct text_object *obj, const char *arg, const char *path, const char *type)
1004 char buf1[64], buf2[64];
1005 float factor, offset;
1008 if (sscanf(arg, "%63s %d %f %f", buf2, &n, &factor, &offset) == 4) found = 1; else HWMON_RESET();
1009 if (!found && sscanf(arg, "%63s %63s %d %f %f", buf1, buf2, &n, &factor, &offset) == 5) found = 1; else if (!found) HWMON_RESET();
1010 if (!found && sscanf(arg, "%63s %63s %d", buf1, buf2, &n) == 3) found = 1; else if (!found) HWMON_RESET();
1011 if (!found && sscanf(arg, "%63s %d", buf2, &n) == 2) found = 1; else if (!found) HWMON_RESET();
1014 NORM_ERR("i2c failed to parse arguments");
1015 obj->type = OBJ_text;
1018 DBGP("parsed %s args: '%s' '%s' %d %f %f\n", type, buf1, buf2, n, factor, offset);
1019 obj->data.sysfs.fd = open_sysfs_sensor(path, (*buf1) ? buf1 : 0, buf2, n,
1020 &obj->data.sysfs.arg, obj->data.sysfs.devtype);
1021 strncpy(obj->data.sysfs.type, buf2, 63);
1022 obj->data.sysfs.factor = factor;
1023 obj->data.sysfs.offset = offset;
1026 #define PARSER_GENERATOR(name, path) \
1027 void parse_##name##_sensor(struct text_object *obj, const char *arg) \
1029 parse_sysfs_sensor(obj, arg, path, #name); \
1032 PARSER_GENERATOR(i2c, "/sys/bus/i2c/devices/")
1033 PARSER_GENERATOR(hwmon, "/sys/class/hwmon/")
1034 PARSER_GENERATOR(platform, "/sys/bus/platform/devices/")
1036 void print_sysfs_sensor(struct text_object *obj, char *p, int p_max_size)
1040 r = get_sysfs_info(&obj->data.sysfs.fd, obj->data.sysfs.arg,
1041 obj->data.sysfs.devtype, obj->data.sysfs.type);
1043 r = r * obj->data.sysfs.factor + obj->data.sysfs.offset;
1045 if (!strncmp(obj->data.sysfs.type, "temp", 4)) {
1046 temp_print(p, p_max_size, r, TEMP_CELSIUS);
1047 } else if (r >= 100.0 || r == 0) {
1048 snprintf(p, p_max_size, "%d", (int) r);
1050 snprintf(p, p_max_size, "%.1f", r);
1054 /* Prior to kernel version 2.6.12, the CPU fan speed was available in
1055 * ADT746X_FAN_OLD, whereas later kernel versions provide this information in
1057 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
1058 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
1060 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
1063 char adt746x_fan_state[64];
1066 if (!p_client_buffer || client_buffer_size <= 0) {
1070 if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
1071 && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL) {
1072 sprintf(adt746x_fan_state, "adt746x not found");
1074 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
1075 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
1079 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_fan_state);
1082 /* Prior to kernel version 2.6.12, the CPU temperature was found in
1083 * ADT746X_CPU_OLD, whereas later kernel versions provide this information in
1085 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
1086 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
1088 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
1091 char adt746x_cpu_state[64];
1094 if (!p_client_buffer || client_buffer_size <= 0) {
1098 if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
1099 && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL) {
1100 sprintf(adt746x_cpu_state, "adt746x not found");
1102 fscanf(fp, "%2s", adt746x_cpu_state);
1106 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state);
1109 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1110 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1112 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1113 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1114 const char *p_format, int divisor, unsigned int cpu)
1122 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1128 char current_freq_file[128];
1130 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1132 f = fopen(current_freq_file, "r");
1134 /* if there's a cpufreq /sys node, read the current frequency from
1135 * this node and divide by 1000 to get Mhz. */
1136 if (fgets(s, sizeof(s), f)) {
1137 s[strlen(s) - 1] = '\0';
1138 freq = strtod(s, NULL);
1141 snprintf(p_client_buffer, client_buffer_size, p_format,
1142 (freq / 1000) / divisor);
1147 // open the CPU information file
1148 f = open_file("/proc/cpuinfo", &rep);
1150 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1155 while (fgets(s, sizeof(s), f) != NULL) {
1157 #if defined(__i386) || defined(__x86_64)
1158 // and search for the cpu mhz
1159 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1161 #if defined(__alpha)
1162 // different on alpha
1163 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1165 // this is different on ppc for some reason
1166 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1167 #endif // defined(__alpha)
1168 #endif // defined(__i386) || defined(__x86_64)
1170 // copy just the number
1171 strcpy(frequency, strchr(s, ':') + 2);
1172 #if defined(__alpha)
1174 frequency[strlen(frequency) - 6] = '\0';
1175 // kernel reports in Hz
1176 freq = strtod(frequency, NULL) / 1000000;
1179 frequency[strlen(frequency) - 1] = '\0';
1180 freq = strtod(frequency, NULL);
1184 if (strncmp(s, "processor", 9) == 0) {
1191 snprintf(p_client_buffer, client_buffer_size, p_format,
1192 (float) freq / divisor);
1196 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1198 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1208 * Peter Tarjan (ptarjan@citromail.hu) */
1210 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1211 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1212 const char *p_format, int divisor, unsigned int cpu)
1218 char current_freq_file[128];
1221 /* build the voltage file name */
1223 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1226 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1231 /* read the current cpu frequency from the /sys node */
1232 f = fopen(current_freq_file, "r");
1234 if (fgets(s, sizeof(s), f)) {
1235 s[strlen(s) - 1] = '\0';
1236 freq = strtod(s, NULL);
1240 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1241 perror("get_voltage()");
1248 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1251 /* use the current cpu frequency to find the corresponding voltage */
1252 f = fopen(current_freq_file, "r");
1258 if (fgets(line, 255, f) == NULL) {
1261 sscanf(line, "%d %d", &freq_comp, &voltage);
1262 if (freq_comp == freq) {
1268 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1269 perror("get_voltage()");
1275 snprintf(p_client_buffer, client_buffer_size, p_format,
1276 (float) voltage / divisor);
1280 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1282 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1289 if (!p_client_buffer || client_buffer_size <= 0) {
1293 /* yeah, slow... :/ */
1294 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1295 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1299 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1301 fp = open_file(buf2, &rep);
1303 snprintf(p_client_buffer, client_buffer_size,
1304 "can't open fan's state file");
1307 memset(buf, 0, sizeof(buf));
1308 fscanf(fp, "%*s %99s", buf);
1311 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1314 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1315 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1316 /* Linux 2.6.25 onwards ac adapter info is in
1317 /sys/class/power_supply/AC/
1318 On my system I get the following.
1319 /sys/class/power_supply/AC/uevent:
1320 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1323 POWER_SUPPLY_NAME=AC
1324 POWER_SUPPLY_TYPE=Mains
1325 POWER_SUPPLY_ONLINE=1
1328 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1336 if (!p_client_buffer || client_buffer_size <= 0) {
1340 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1341 fp = open_file(buf2, &rep);
1343 /* sysfs processing */
1345 if (fgets(buf, sizeof(buf), fp) == NULL)
1348 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1350 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1351 snprintf(p_client_buffer, client_buffer_size,
1352 "%s-line", (online ? "on" : "off"));
1358 /* yeah, slow... :/ */
1359 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1360 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1364 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1366 fp = open_file(buf2, &rep);
1368 snprintf(p_client_buffer, client_buffer_size,
1369 "No ac adapter found.... where is it?");
1372 memset(buf, 0, sizeof(buf));
1373 fscanf(fp, "%*s %99s", buf);
1376 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1381 /proc/acpi/thermal_zone/THRM/cooling_mode
1382 cooling mode: active
1383 /proc/acpi/thermal_zone/THRM/polling_frequency
1385 /proc/acpi/thermal_zone/THRM/state
1387 /proc/acpi/thermal_zone/THRM/temperature
1389 /proc/acpi/thermal_zone/THRM/trip_points
1391 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1394 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1395 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1397 int open_acpi_temperature(const char *name)
1403 if (name == NULL || strcmp(name, "*") == 0) {
1406 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1412 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1414 fd = open(path, O_RDONLY);
1416 NORM_ERR("can't open '%s': %s", path, strerror(errno));
1422 static double last_acpi_temp;
1423 static double last_acpi_temp_time;
1425 double get_acpi_temperature(int fd)
1431 /* don't update acpi temperature too often */
1432 if (current_update_time - last_acpi_temp_time < 11.32) {
1433 return last_acpi_temp;
1435 last_acpi_temp_time = current_update_time;
1437 /* seek to beginning */
1438 lseek(fd, 0, SEEK_SET);
1445 n = read(fd, buf, 255);
1447 NORM_ERR("can't read fd %d: %s", fd, strerror(errno));
1450 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1454 return last_acpi_temp;
1458 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1460 design capacity: 4400 mAh
1461 last full capacity: 4064 mAh
1462 battery technology: rechargeable
1463 design voltage: 14800 mV
1464 design capacity warning: 300 mAh
1465 design capacity low: 200 mAh
1466 capacity granularity 1: 32 mAh
1467 capacity granularity 2: 32 mAh
1469 serial number: 16922
1475 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1478 charging state: unknown
1480 remaining capacity: 4064 mAh
1481 present voltage: 16608 mV
1485 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1486 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1487 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1488 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1489 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1491 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1492 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1494 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1495 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1498 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1499 Linux 2.6.24 onwards battery info is in
1500 /sys/class/power_supply/BAT0/
1501 On my system I get the following.
1502 /sys/class/power_supply/BAT0/uevent:
1503 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1505 PHYSDEVDRIVER=battery
1506 POWER_SUPPLY_NAME=BAT0
1507 POWER_SUPPLY_TYPE=Battery
1508 POWER_SUPPLY_STATUS=Discharging
1509 POWER_SUPPLY_PRESENT=1
1510 POWER_SUPPLY_TECHNOLOGY=Li-ion
1511 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1512 POWER_SUPPLY_VOLTAGE_NOW=10780000
1513 POWER_SUPPLY_CURRENT_NOW=13970000
1514 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1515 POWER_SUPPLY_ENERGY_FULL=27370000
1516 POWER_SUPPLY_ENERGY_NOW=11810000
1517 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1518 POWER_SUPPLY_MANUFACTURER=Panasonic
1519 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1522 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1523 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1524 #define APM_PATH "/proc/apm"
1525 #define MAX_BATTERY_COUNT 4
1527 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1528 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1529 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1531 static int batteries_initialized = 0;
1532 static char batteries[MAX_BATTERY_COUNT][32];
1534 static int acpi_last_full[MAX_BATTERY_COUNT];
1535 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1537 /* e.g. "charging 75%" */
1538 static char last_battery_str[MAX_BATTERY_COUNT][64];
1540 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1542 static double last_battery_time[MAX_BATTERY_COUNT];
1544 static int last_battery_perct[MAX_BATTERY_COUNT];
1545 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1547 void init_batteries(void)
1551 if (batteries_initialized) {
1555 #pragma omp parallel for schedule(dynamic,10)
1556 #endif /* HAVE_OPENMP */
1557 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1558 batteries[idx][0] = '\0';
1560 batteries_initialized = 1;
1563 int get_battery_idx(const char *bat)
1567 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1568 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1573 /* if not found, enter a new entry */
1574 if (!strlen(batteries[idx])) {
1575 snprintf(batteries[idx], 31, "%s", bat);
1581 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1583 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1585 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1586 char acpi_path[128];
1587 char sysfs_path[128];
1589 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1590 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1594 idx = get_battery_idx(bat);
1596 /* don't update battery too often */
1597 if (current_update_time - last_battery_time[idx] < 29.5) {
1598 set_return_value(buffer, n, item, idx);
1602 last_battery_time[idx] = current_update_time;
1604 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1605 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1607 /* first try SYSFS if that fails try ACPI */
1609 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1610 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1613 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1614 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1617 if (sysfs_bat_fp[idx] != NULL) {
1619 int present_rate = -1;
1620 int remaining_capacity = -1;
1621 char charging_state[64];
1624 strcpy(charging_state, "unknown");
1626 while (!feof(sysfs_bat_fp[idx])) {
1628 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1631 /* let's just hope units are ok */
1632 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1633 strcpy(present, "yes");
1634 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1635 strcpy(present, "no");
1636 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1637 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1638 /* present_rate is not the same as the
1639 current flowing now but it is the same value
1640 which was used in the past. so we continue
1642 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1643 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1644 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1645 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1646 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1647 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1648 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1649 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1650 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1651 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1654 fclose(sysfs_bat_fp[idx]);
1655 sysfs_bat_fp[idx] = NULL;
1657 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1658 if (remaining_capacity > acpi_last_full[idx])
1659 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1662 if (strcmp(present, "No") == 0) {
1663 strncpy(last_battery_str[idx], "not present", 64);
1666 else if (strcmp(charging_state, "Charging") == 0) {
1667 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1668 /* e.g. charging 75% */
1669 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1670 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1672 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1673 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1674 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1675 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1676 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1677 snprintf(last_battery_time_str[idx],
1678 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1680 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1681 snprintf(last_battery_time_str[idx],
1682 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1686 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1687 if (present_rate > 0) {
1688 /* e.g. discharging 35% */
1689 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1690 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1692 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1693 (long) (((float) remaining_capacity / present_rate) * 3600));
1694 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1695 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1696 snprintf(last_battery_time_str[idx],
1697 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1699 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1701 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1702 snprintf(last_battery_time_str[idx],
1703 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1707 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1708 else if (strncmp(charging_state, "Charged", 64) == 0 || strncmp(charging_state, "Full", 64) == 0) {
1709 /* Below happens with the second battery on my X40,
1710 * when the second one is empty and the first one
1712 if (remaining_capacity == 0)
1713 strcpy(last_battery_str[idx], "empty");
1715 strcpy(last_battery_str[idx], "charged");
1717 /* unknown, probably full / AC */
1719 if (acpi_last_full[idx] != 0
1720 && remaining_capacity != acpi_last_full[idx])
1721 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1722 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1724 strncpy(last_battery_str[idx], "AC", 64);
1726 } else if (acpi_bat_fp[idx] != NULL) {
1728 int present_rate = -1;
1729 int remaining_capacity = -1;
1730 char charging_state[64];
1733 /* read last full capacity if it's zero */
1734 if (acpi_last_full[idx] == 0) {
1735 static int rep3 = 0;
1739 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1740 fp = open_file(path, &rep3);
1745 if (fgets(b, 256, fp) == NULL) {
1748 if (sscanf(b, "last full capacity: %d",
1749 &acpi_last_full[idx]) != 0) {
1758 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1760 strcpy(charging_state, "unknown");
1762 while (!feof(acpi_bat_fp[idx])) {
1765 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1769 /* let's just hope units are ok */
1770 if (strncmp(buf, "present:", 8) == 0) {
1771 sscanf(buf, "present: %4s", present);
1772 } else if (strncmp(buf, "charging state:", 15) == 0) {
1773 sscanf(buf, "charging state: %63s", charging_state);
1774 } else if (strncmp(buf, "present rate:", 13) == 0) {
1775 sscanf(buf, "present rate: %d", &present_rate);
1776 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1777 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1780 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1781 if (remaining_capacity > acpi_last_full[idx]) {
1782 /* normalize to 100% */
1783 acpi_last_full[idx] = remaining_capacity;
1787 if (strcmp(present, "no") == 0) {
1788 strncpy(last_battery_str[idx], "not present", 64);
1790 } else if (strcmp(charging_state, "charging") == 0) {
1791 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1792 /* e.g. charging 75% */
1793 snprintf(last_battery_str[idx],
1794 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1795 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1797 format_seconds(last_battery_time_str[idx],
1798 sizeof(last_battery_time_str[idx]) - 1,
1799 (long) (((acpi_last_full[idx] - remaining_capacity) *
1800 3600) / present_rate));
1801 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1802 snprintf(last_battery_str[idx],
1803 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1804 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1805 snprintf(last_battery_time_str[idx],
1806 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1808 strncpy(last_battery_str[idx], "charging",
1809 sizeof(last_battery_str[idx]) - 1);
1810 snprintf(last_battery_time_str[idx],
1811 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1814 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1815 if (present_rate > 0) {
1816 /* e.g. discharging 35% */
1817 snprintf(last_battery_str[idx],
1818 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1819 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1821 format_seconds(last_battery_time_str[idx],
1822 sizeof(last_battery_time_str[idx]) - 1,
1823 (long) ((remaining_capacity * 3600) / present_rate));
1824 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1825 snprintf(last_battery_str[idx],
1826 sizeof(last_battery_str[idx]) - 1, "full");
1827 snprintf(last_battery_time_str[idx],
1828 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1830 snprintf(last_battery_str[idx],
1831 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1832 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1833 snprintf(last_battery_time_str[idx],
1834 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1837 } else if (strncmp(charging_state, "charged", 64) == 0) {
1838 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1839 /* Below happens with the second battery on my X40,
1840 * when the second one is empty and the first one being charged. */
1841 if (remaining_capacity == 0) {
1842 strcpy(last_battery_str[idx], "empty");
1844 strcpy(last_battery_str[idx], "charged");
1846 /* unknown, probably full / AC */
1848 if (strncmp(charging_state, "Full", 64) == 0) {
1849 strncpy(last_battery_str[idx], "full", 64);
1850 } else if (acpi_last_full[idx] != 0
1851 && remaining_capacity != acpi_last_full[idx]) {
1852 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1853 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1855 strncpy(last_battery_str[idx], "AC", 64);
1858 fclose(acpi_bat_fp[idx]);
1859 acpi_bat_fp[idx] = NULL;
1862 if (apm_bat_fp[idx] == NULL) {
1863 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1866 if (apm_bat_fp[idx] != NULL) {
1867 unsigned int ac, status, flag;
1870 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1871 &ac, &status, &flag, &life);
1874 /* could check now that there is ac */
1875 snprintf(last_battery_str[idx], 64, "AC");
1877 /* could check that status == 3 here? */
1878 } else if (ac && life != 100) {
1879 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1881 snprintf(last_battery_str[idx], 64, "%d%%", life);
1884 /* it seemed to buffer it so file must be closed (or could use
1885 * syscalls directly but I don't feel like coding it now) */
1886 fclose(apm_bat_fp[idx]);
1887 apm_bat_fp[idx] = NULL;
1890 set_return_value(buffer, n, item, idx);
1893 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1896 case BATTERY_STATUS:
1897 snprintf(buffer, n, "%s", last_battery_str[idx]);
1900 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1907 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1909 get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1910 if (0 == strncmp("charging", buffer, 8)) {
1912 memmove(buffer + 1, buffer + 8, n - 8);
1913 } else if (0 == strncmp("discharging", buffer, 11)) {
1915 memmove(buffer + 1, buffer + 11, n - 11);
1916 } else if (0 == strncmp("charged", buffer, 7)) {
1918 memmove(buffer + 1, buffer + 7, n - 7);
1919 } else if (0 == strncmp("not present", buffer, 11)) {
1921 memmove(buffer + 1, buffer + 11, n - 11);
1922 } else if (0 == strncmp("empty", buffer, 5)) {
1924 memmove(buffer + 1, buffer + 5, n - 5);
1925 } else if (0 != strncmp("AC", buffer, 2)) {
1927 memmove(buffer + 1, buffer + 11, n - 11);
1931 int get_battery_perct(const char *bat)
1935 char acpi_path[128];
1936 char sysfs_path[128];
1937 int remaining_capacity = -1;
1939 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1940 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1944 idx = get_battery_idx(bat);
1946 /* don't update battery too often */
1947 if (current_update_time - last_battery_perct_time[idx] < 30) {
1948 return last_battery_perct[idx];
1950 last_battery_perct_time[idx] = current_update_time;
1952 /* Only check for SYSFS or ACPI */
1954 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1955 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1959 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1960 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1963 if (sysfs_bat_fp[idx] != NULL) {
1965 while (!feof(sysfs_bat_fp[idx])) {
1967 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1970 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1971 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1972 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1973 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1974 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1975 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1976 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1977 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1981 fclose(sysfs_bat_fp[idx]);
1982 sysfs_bat_fp[idx] = NULL;
1984 } else if (acpi_bat_fp[idx] != NULL) {
1986 /* read last full capacity if it's zero */
1987 if (acpi_design_capacity[idx] == 0) {
1992 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1993 fp = open_file(path, &rep2);
1998 if (fgets(b, 256, fp) == NULL) {
2001 if (sscanf(b, "last full capacity: %d",
2002 &acpi_design_capacity[idx]) != 0) {
2010 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
2012 while (!feof(acpi_bat_fp[idx])) {
2015 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
2019 if (buf[0] == 'r') {
2020 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
2024 if (remaining_capacity < 0) {
2027 /* compute the battery percentage */
2028 last_battery_perct[idx] =
2029 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
2030 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
2031 return last_battery_perct[idx];
2034 int get_battery_perct_bar(const char *bar)
2038 get_battery_perct(bar);
2039 idx = get_battery_idx(bar);
2040 return (int) (last_battery_perct[idx] * 2.56 - 1);
2043 /* On Apple powerbook and ibook:
2044 $ cat /proc/pmu/battery_0
2051 $ cat /proc/pmu/info
2052 PMU driver version : 2
2053 PMU firmware version : 0c
2058 /* defines as in <linux/pmu.h> */
2059 #define PMU_BATT_PRESENT 0x00000001
2060 #define PMU_BATT_CHARGING 0x00000002
2062 static FILE *pmu_battery_fp;
2063 static FILE *pmu_info_fp;
2064 static char pb_battery_info[3][32];
2065 static double pb_battery_info_update;
2067 #define PMU_PATH "/proc/pmu"
2068 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2071 const char *batt_path = PMU_PATH "/battery_0";
2072 const char *info_path = PMU_PATH "/info";
2074 int charge, max_charge, ac = -1;
2077 /* don't update battery too often */
2078 if (current_update_time - pb_battery_info_update < 29.5) {
2079 snprintf(buffer, n, "%s", pb_battery_info[i]);
2082 pb_battery_info_update = current_update_time;
2084 if (pmu_battery_fp == NULL) {
2085 pmu_battery_fp = open_file(batt_path, &rep);
2086 if (pmu_battery_fp == NULL) {
2091 if (pmu_battery_fp != NULL) {
2092 rewind(pmu_battery_fp);
2093 while (!feof(pmu_battery_fp)) {
2096 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2100 if (buf[0] == 'f') {
2101 sscanf(buf, "flags : %8x", &flags);
2102 } else if (buf[0] == 'c' && buf[1] == 'h') {
2103 sscanf(buf, "charge : %d", &charge);
2104 } else if (buf[0] == 'm') {
2105 sscanf(buf, "max_charge : %d", &max_charge);
2106 } else if (buf[0] == 't') {
2107 sscanf(buf, "time rem. : %ld", &timeval);
2111 if (pmu_info_fp == NULL) {
2112 pmu_info_fp = open_file(info_path, &rep);
2113 if (pmu_info_fp == NULL) {
2118 if (pmu_info_fp != NULL) {
2119 rewind(pmu_info_fp);
2120 while (!feof(pmu_info_fp)) {
2123 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2126 if (buf[0] == 'A') {
2127 sscanf(buf, "AC Power : %d", &ac);
2131 /* update status string */
2132 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2133 strncpy(pb_battery_info[PB_BATT_STATUS], "AC", sizeof(pb_battery_info[PB_BATT_STATUS]));
2134 } else if (ac && (flags & PMU_BATT_PRESENT)
2135 && !(flags & PMU_BATT_CHARGING)) {
2136 strncpy(pb_battery_info[PB_BATT_STATUS], "charged", sizeof(pb_battery_info[PB_BATT_STATUS]));
2137 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2138 strncpy(pb_battery_info[PB_BATT_STATUS], "charging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2140 strncpy(pb_battery_info[PB_BATT_STATUS], "discharging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2143 /* update percentage string */
2144 if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT)
2145 && !(flags & PMU_BATT_CHARGING)) {
2146 snprintf(pb_battery_info[PB_BATT_PERCENT],
2147 sizeof(pb_battery_info[PB_BATT_PERCENT]), "100%%");
2148 } else if (timeval == 0) {
2149 snprintf(pb_battery_info[PB_BATT_PERCENT],
2150 sizeof(pb_battery_info[PB_BATT_PERCENT]), "unknown");
2152 snprintf(pb_battery_info[PB_BATT_PERCENT],
2153 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2154 (charge * 100) / max_charge);
2157 /* update time string */
2158 if (timeval == 0) { /* fully charged or battery not present */
2159 snprintf(pb_battery_info[PB_BATT_TIME],
2160 sizeof(pb_battery_info[PB_BATT_TIME]), "unknown");
2161 } else if (timeval < 60 * 60) { /* don't show secs */
2162 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2163 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2165 format_seconds(pb_battery_info[PB_BATT_TIME],
2166 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2169 snprintf(buffer, n, "%s", pb_battery_info[i]);
2172 void update_top(void)
2174 process_find_top(info.cpu, info.memu, info.time
2179 info.first_process = get_first_process();
2182 void update_entropy(void)
2185 const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2186 const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2189 info.entropy.entropy_avail = 0;
2190 info.entropy.poolsize = 0;
2192 if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2196 if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2201 fscanf(fp1, "%u", &info.entropy.entropy_avail);
2202 fscanf(fp2, "%u", &info.entropy.poolsize);
2208 const char *get_disk_protect_queue(const char *disk)
2214 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2215 if (access(path, F_OK)) {
2216 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2218 if ((fp = fopen(path, "r")) == NULL)
2220 if (fscanf(fp, "%d\n", &state) != 1) {
2225 return (state > 0) ? "frozen" : "free ";
2228 void update_diskio(void)
2232 char buf[512], devbuf[64];
2233 unsigned int major, minor;
2235 struct diskio_stat *cur;
2236 unsigned int reads, writes;
2237 unsigned int total_reads = 0, total_writes = 0;
2240 stats.current_read = 0;
2241 stats.current_write = 0;
2243 if (!(fp = open_file("/proc/diskstats", &rep))) {
2247 /* read reads and writes from all disks (minor = 0), including cd-roms
2248 * and floppies, and sum them up */
2249 while (fgets(buf, 512, fp)) {
2250 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
2251 &minor, devbuf, &reads, &writes);
2252 /* ignore subdevices (they have only 3 matching entries in their line)
2253 * and virtual devices (LVM, network block devices, RAM disks, Loopback)
2255 * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
2256 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
2257 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
2258 total_reads += reads;
2259 total_writes += writes;
2261 col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
2262 &major, &minor, devbuf, &reads, &writes);
2263 if (col_count != 5) {
2268 while (cur && strcmp(devbuf, cur->dev))
2272 update_diskio_values(cur, reads, writes);
2274 update_diskio_values(&stats, total_reads, total_writes);