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-2010 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/>.
38 #include "temphelper.h"
43 #include <sys/types.h>
44 #include <sys/sysinfo.h>
46 #ifndef HAVE_CLOCK_GETTIME
51 // #include <assert.h>
55 #include <sys/ioctl.h>
56 #include <sys/socket.h>
57 #include <netinet/in.h>
58 #include <linux/sockios.h>
60 #include <arpa/inet.h>
64 #include <linux/route.h>
68 /* The following ifdefs were adapted from gkrellm */
69 #include <linux/major.h>
71 #if !defined(MD_MAJOR)
75 #if !defined(LVM_BLK_MAJOR)
76 #define LVM_BLK_MAJOR 58
79 #if !defined(NBD_MAJOR)
95 #define SHORTSTAT_TEMPL "%*s %llu %llu %llu"
96 #define LONGSTAT_TEMPL "%*s %llu %llu %llu "
98 /* This flag tells the linux routines to use the /proc system where possible,
99 * even if other api's are available, e.g. sysinfo() or getloadavg().
100 * the reason for this is to allow for /proc-based distributed monitoring.
101 * using a flag in this manner creates less confusing code. */
102 static int prefer_proc = 0;
104 void prepare_update(void)
108 void update_uptime(void)
112 struct sysinfo s_info;
115 info.uptime = (double) s_info.uptime;
122 if (!(fp = open_file("/proc/uptime", &rep))) {
126 fscanf(fp, "%lf", &info.uptime);
131 int check_mount(char *s)
134 FILE *mtab = fopen("/etc/mtab", "r");
137 char buf1[256], buf2[128];
139 while (fgets(buf1, 256, mtab)) {
140 sscanf(buf1, "%*s %128s", buf2);
141 if (!strcmp(s, buf2)) {
148 NORM_ERR("Could not open mtab");
153 /* these things are also in sysinfo except Buffers:
154 * (that's why I'm reading them from proc) */
156 void update_meminfo(void)
161 /* unsigned int a; */
164 info.mem = info.memmax = info.swap = info.swapfree = info.swapmax = info.bufmem =
165 info.buffers = info.cached = info.memfree = info.memeasyfree = 0;
167 if (!(meminfo_fp = open_file("/proc/meminfo", &rep))) {
171 while (!feof(meminfo_fp)) {
172 if (fgets(buf, 255, meminfo_fp) == NULL) {
176 if (strncmp(buf, "MemTotal:", 9) == 0) {
177 sscanf(buf, "%*s %llu", &info.memmax);
178 } else if (strncmp(buf, "MemFree:", 8) == 0) {
179 sscanf(buf, "%*s %llu", &info.memfree);
180 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
181 sscanf(buf, "%*s %llu", &info.swapmax);
182 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
183 sscanf(buf, "%*s %llu", &info.swapfree);
184 } else if (strncmp(buf, "Buffers:", 8) == 0) {
185 sscanf(buf, "%*s %llu", &info.buffers);
186 } else if (strncmp(buf, "Cached:", 7) == 0) {
187 sscanf(buf, "%*s %llu", &info.cached);
191 info.mem = info.memmax - info.memfree;
192 info.memeasyfree = info.memfree;
193 info.swap = info.swapmax - info.swapfree;
195 info.bufmem = info.cached + info.buffers;
200 int get_laptop_mode(void)
205 if ((fp = fopen("/proc/sys/vm/laptop_mode", "r")) != NULL)
206 fscanf(fp, "%d\n", &val);
212 * # cat /sys/block/sda/queue/scheduler
213 * noop [anticipatory] cfq
215 char *get_ioscheduler(char *disk)
221 return strndup("n/a", text_buffer_size);
223 snprintf(buf, 127, "/sys/block/%s/queue/scheduler", disk);
224 if ((fp = fopen(buf, "r")) == NULL) {
225 return strndup("n/a", text_buffer_size);
228 fscanf(fp, "%127s", buf);
230 buf[strlen(buf) - 1] = '\0';
232 return strndup(buf + 1, text_buffer_size);
236 return strndup("n/a", text_buffer_size);
245 #define COND_FREE(x) if(x) free(x); x = 0
246 #define SAVE_SET_STRING(x, y) \
247 if (x && strcmp((char *)x, (char *)y)) { \
249 x = strndup("multiple", text_buffer_size); \
251 x = strndup(y, text_buffer_size); \
254 void update_gateway_info_failure(const char *reason)
259 //2 pointers to 1 location causes a crash when we try to free them both
260 gw_info.iface = strndup("failed", text_buffer_size);
261 gw_info.ip = strndup("failed", text_buffer_size);
265 /* Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT */
266 #define RT_ENTRY_FORMAT "%63s %lx %lx %x %*d %*d %*d %lx %*d %*d %*d\n"
268 void update_gateway_info(void)
273 unsigned long dest, gate, mask;
276 COND_FREE(gw_info.iface);
277 COND_FREE(gw_info.ip);
280 if ((fp = fopen("/proc/net/route", "r")) == NULL) {
281 update_gateway_info_failure("fopen()");
285 /* skip over the table header line, which is always present */
286 fscanf(fp, "%*[^\n]\n");
289 if(fscanf(fp, RT_ENTRY_FORMAT,
290 iface, &dest, &gate, &flags, &mask) != 5) {
291 update_gateway_info_failure("fscanf()");
294 if (!(dest || mask) && ((flags & RTF_GATEWAY) || !gate) ) {
296 SAVE_SET_STRING(gw_info.iface, iface)
298 SAVE_SET_STRING(gw_info.ip, inet_ntoa(ina))
305 void free_gateway_info(void)
311 memset(&gw_info, 0, sizeof(gw_info));
314 int gateway_exists(void)
316 return !!gw_info.count;
319 void print_gateway_iface(char *p, int p_max_size)
321 snprintf(p, p_max_size, "%s", gw_info.iface);
324 void print_gateway_ip(char *p, int p_max_size)
326 snprintf(p, p_max_size, "%s", gw_info.ip);
329 void update_net_stats(void)
333 static char first = 1;
335 // FIXME: arbitrary size chosen to keep code simple.
337 unsigned int curtmp1, curtmp2;
344 // wireless info variables
345 int skfd, has_bitrate = 0;
346 struct wireless_info *winfo;
351 delta = current_update_time - last_update_time;
352 if (delta <= 0.0001) {
356 /* open file and ignore first two lines */
357 if (!(net_dev_fp = open_file("/proc/net/dev", &rep))) {
362 fgets(buf, 255, net_dev_fp); /* garbage */
363 fgets(buf, 255, net_dev_fp); /* garbage (field names) */
365 /* read each interface */
366 for (i2 = 0; i2 < MAX_NET_INTERFACES; i2++) {
370 long long r, t, last_recv, last_trans;
372 if (fgets(buf, 255, net_dev_fp) == NULL) {
376 while (isspace((int) *p)) {
382 while (*p && *p != ':') {
391 ns = get_net_stat(s, NULL, NULL);
393 memset(&(ns->addr.sa_data), 0, 14);
395 memset(ns->addrs, 0, 17 * MAX_NET_INTERFACES + 1); /* Up to 17 chars per ip, max MAX_NET_INTERFACES interfaces. Nasty memory usage... */
397 last_recv = ns->recv;
398 last_trans = ns->trans;
400 /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
401 sscanf(p, "%lld %*d %*d %*d %*d %*d %*d %*d %lld",
404 /* if recv or trans is less than last time, an overflow happened */
405 if (r < ns->last_read_recv) {
408 ns->recv += (r - ns->last_read_recv);
410 ns->last_read_recv = r;
412 if (t < ns->last_read_trans) {
415 ns->trans += (t - ns->last_read_trans);
417 ns->last_read_trans = t;
419 /*** ip addr patch ***/
420 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
422 conf.ifc_buf = malloc(sizeof(struct ifreq) * MAX_NET_INTERFACES);
423 conf.ifc_len = sizeof(struct ifreq) * MAX_NET_INTERFACES;
424 memset(conf.ifc_buf, 0, conf.ifc_len);
426 ioctl((long) i, SIOCGIFCONF, &conf);
428 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
429 struct net_stat *ns2;
431 if (!(((struct ifreq *) conf.ifc_buf) + k))
435 ((struct ifreq *) conf.ifc_buf)[k].ifr_ifrn.ifrn_name, NULL, NULL);
436 ns2->addr = ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.ifru_addr;
437 sprintf(temp_addr, "%u.%u.%u.%u, ",
438 ns2->addr.sa_data[2] & 255,
439 ns2->addr.sa_data[3] & 255,
440 ns2->addr.sa_data[4] & 255,
441 ns2->addr.sa_data[5] & 255);
442 if(NULL == strstr(ns2->addrs, temp_addr))
443 strncpy(ns2->addrs + strlen(ns2->addrs), temp_addr, 17);
450 /*** end ip addr patch ***/
453 /* calculate speeds */
454 ns->net_rec[0] = (ns->recv - last_recv) / delta;
455 ns->net_trans[0] = (ns->trans - last_trans) / delta;
462 #pragma omp parallel for reduction(+:curtmp1, curtmp2) schedule(dynamic,10)
463 #endif /* HAVE_OPENMP */
464 for (i = 0; i < info.net_avg_samples; i++) {
465 curtmp1 = curtmp1 + ns->net_rec[i];
466 curtmp2 = curtmp2 + ns->net_trans[i];
468 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
469 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
470 if (info.net_avg_samples > 1) {
472 #pragma omp parallel for schedule(dynamic,10)
473 #endif /* HAVE_OPENMP */
474 for (i = info.net_avg_samples; i > 1; i--) {
475 ns->net_rec[i - 1] = ns->net_rec[i - 2];
476 ns->net_trans[i - 1] = ns->net_trans[i - 2];
481 /* update wireless info */
482 winfo = malloc(sizeof(struct wireless_info));
483 memset(winfo, 0, sizeof(struct wireless_info));
485 skfd = iw_sockets_open();
486 if (iw_get_basic_config(skfd, s, &(winfo->b)) > -1) {
488 // set present winfo variables
489 if (iw_get_stats(skfd, s, &(winfo->stats),
490 &winfo->range, winfo->has_range) >= 0) {
491 winfo->has_stats = 1;
493 if (iw_get_range_info(skfd, s, &(winfo->range)) >= 0) {
494 winfo->has_range = 1;
496 if (iw_get_ext(skfd, s, SIOCGIWAP, &wrq) >= 0) {
497 winfo->has_ap_addr = 1;
498 memcpy(&(winfo->ap_addr), &(wrq.u.ap_addr), sizeof(sockaddr));
502 if (iw_get_ext(skfd, s, SIOCGIWRATE, &wrq) >= 0) {
503 memcpy(&(winfo->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
504 iw_print_bitrate(ns->bitrate, 16, winfo->bitrate.value);
509 if (winfo->has_range && winfo->has_stats
510 && ((winfo->stats.qual.level != 0)
511 || (winfo->stats.qual.updated & IW_QUAL_DBM))) {
512 if (!(winfo->stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
513 ns->link_qual = winfo->stats.qual.qual;
514 ns->link_qual_max = winfo->range.max_qual.qual;
519 if (winfo->has_ap_addr) {
520 iw_sawap_ntop(&winfo->ap_addr, ns->ap);
524 if (winfo->b.has_essid) {
525 if (winfo->b.essid_on) {
526 snprintf(ns->essid, 32, "%s", winfo->b.essid);
528 snprintf(ns->essid, 32, "off/any");
532 snprintf(ns->mode, 16, "%s", iw_operation_mode[winfo->b.mode]);
534 iw_sockets_close(skfd);
545 void update_total_processes(void)
548 struct dirent *entry;
553 if (!(dir = opendir("/proc"))) {
556 while ((entry = readdir(dir))) {
558 /* Problem reading list of processes */
563 if (sscanf(entry->d_name, "%d%c", &ignore1, &ignore2) == 1) {
570 void update_threads(void)
574 struct sysinfo s_info;
577 info.threads = s_info.procs;
584 if (!(fp = open_file("/proc/loadavg", &rep))) {
588 fscanf(fp, "%*f %*f %*f %*d/%hu", &info.threads);
593 #define CPU_SAMPLE_COUNT 15
595 unsigned long long cpu_user;
596 unsigned long long cpu_system;
597 unsigned long long cpu_nice;
598 unsigned long long cpu_idle;
599 unsigned long long cpu_iowait;
600 unsigned long long cpu_irq;
601 unsigned long long cpu_softirq;
602 unsigned long long cpu_steal;
603 unsigned long long cpu_total;
604 unsigned long long cpu_active_total;
605 unsigned long long cpu_last_total;
606 unsigned long long cpu_last_active_total;
607 double cpu_val[CPU_SAMPLE_COUNT];
609 static short cpu_setup = 0;
611 /* Determine if this kernel gives us "extended" statistics information in
613 * Kernels around 2.5 and earlier only reported user, system, nice, and
614 * idle values in proc stat.
615 * Kernels around 2.6 and greater report these PLUS iowait, irq, softirq,
617 void determine_longstat(char *buf)
619 unsigned long long iowait = 0;
621 KFLAG_SETOFF(KFLAG_IS_LONGSTAT);
622 /* scanf will either return -1 or 1 because there is only 1 assignment */
623 if (sscanf(buf, "%*s %*d %*d %*d %*d %llu", &iowait) > 0) {
624 KFLAG_SETON(KFLAG_IS_LONGSTAT);
628 void get_cpu_count(void)
634 if (info.cpu_usage) {
638 if (!(stat_fp = open_file("/proc/stat", &rep))) {
644 while (!feof(stat_fp)) {
645 if (fgets(buf, 255, stat_fp) == NULL) {
649 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
650 if (info.cpu_count == 0) {
651 determine_longstat(buf);
656 info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
661 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
662 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
664 void update_stat(void)
668 static struct cpu_info *cpu = NULL;
673 const char *stat_template = NULL;
674 unsigned int malloc_cpu_size = 0;
675 extern void* global_cpu;
677 static pthread_mutex_t last_stat_update_mutex = PTHREAD_MUTEX_INITIALIZER;
678 static double last_stat_update = 0.0;
680 /* since we use wrappers for this function, the update machinery
681 * can't eliminate double invocations of this function. Check for
682 * them here, otherwise cpu_usage counters are freaking out. */
683 pthread_mutex_lock(&last_stat_update_mutex);
684 if (last_stat_update == current_update_time) {
685 pthread_mutex_unlock(&last_stat_update_mutex);
688 last_stat_update = current_update_time;
689 pthread_mutex_unlock(&last_stat_update_mutex);
691 /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
692 if (!cpu_setup || !info.cpu_usage) {
697 if (!stat_template) {
699 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
703 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
704 cpu = malloc(malloc_cpu_size);
705 memset(cpu, 0, malloc_cpu_size);
709 if (!(stat_fp = open_file("/proc/stat", &rep))) {
710 info.run_threads = 0;
711 if (info.cpu_usage) {
712 memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
718 while (!feof(stat_fp)) {
719 if (fgets(buf, 255, stat_fp) == NULL) {
723 if (strncmp(buf, "procs_running ", 14) == 0) {
724 sscanf(buf, "%*s %hu", &info.run_threads);
725 } else if (strncmp(buf, "cpu", 3) == 0) {
727 if (isdigit(buf[3])) {
728 idx = atoi(&buf[3]) + 1;
732 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
733 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
734 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
735 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
736 &(cpu[idx].cpu_steal));
738 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
739 cpu[idx].cpu_system + cpu[idx].cpu_idle +
740 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
741 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
743 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
744 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
746 delta = current_update_time - last_update_time;
748 if (delta <= 0.001) {
752 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
753 cpu[idx].cpu_last_active_total) /
754 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
757 #pragma omp parallel for reduction(+:curtmp) schedule(dynamic,10)
758 #endif /* HAVE_OPENMP */
759 for (i = 0; i < info.cpu_avg_samples; i++) {
760 curtmp = curtmp + cpu[idx].cpu_val[i];
762 /* TESTING -- I've removed this, because I don't think it is right.
763 * You shouldn't divide by the cpu count here ...
764 * removing for testing */
766 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
769 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
771 /* TESTING -- this line replaces the prev. "suspect" if/else */
772 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
774 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
775 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
777 #pragma omp parallel for schedule(dynamic,10)
778 #endif /* HAVE_OPENMP */
779 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
780 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
787 void update_running_processes(void)
792 void update_cpu_usage(void)
797 void update_load_average(void)
799 #ifdef HAVE_GETLOADAVG
804 info.loadavg[0] = (float) v[0];
805 info.loadavg[1] = (float) v[1];
806 info.loadavg[2] = (float) v[2];
813 if (!(fp = open_file("/proc/loadavg", &rep))) {
814 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
817 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
823 /***********************************************************/
824 /***********************************************************/
825 /***********************************************************/
827 static int no_dots(const struct dirent *d)
829 if (d->d_name[0] == '.') {
835 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
837 struct dirent **namelist;
840 n = scandir(dir, &namelist, no_dots, alphasort);
843 NORM_ERR("scandir for %s: %s", dir, strerror(errno));
854 strncpy(s, namelist[0]->d_name, 255);
858 #pragma omp parallel for schedule(dynamic,10)
859 #endif /* HAVE_OPENMP */
860 for (i = 0; i < n; i++) {
869 static int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
870 int *divisor, char *devtype)
877 memset(buf, 0, sizeof(buf));
879 /* if device is NULL or *, get first */
880 if (dev == NULL || strcmp(dev, "*") == 0) {
883 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
889 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
891 /* buf holds result from get_first_file_in_a_directory() above,
892 * e.g. "hwmon0" -- append "/device" */
893 strcat(buf, "/device");
895 /* dev holds device number N as a string,
896 * e.g. "0", -- convert to "hwmon0/device" */
897 sprintf(buf, "hwmon%s/device", dev);
902 /* change vol to in, tempf to temp */
903 if (strcmp(type, "vol") == 0) {
905 } else if (strcmp(type, "tempf") == 0) {
910 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
912 /* first, attempt to open file in /device */
913 fd = open(path, O_RDONLY);
916 /* if it fails, strip the /device from dev and attempt again */
917 buf[strlen(buf) - 7] = 0;
918 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
919 fd = open(path, O_RDONLY);
921 CRIT_ERR(NULL, NULL, "can't open '%s': %s\nplease check your device or remove this "
922 "var from "PACKAGE_NAME, path, strerror(errno));
926 strncpy(devtype, path, 255);
928 if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
929 || strcmp(type, "tempf") == 0) {
934 /* fan does not use *_div as a read divisor */
935 if (strcmp("fan", type) == 0) {
939 /* test if *_div file exist, open it and use it as divisor */
940 if (strcmp(type, "tempf") == 0) {
941 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
943 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
946 divfd = open(path, O_RDONLY);
952 divn = read(divfd, divbuf, 63);
953 /* should read until n == 0 but I doubt that kernel will give these
954 * in multiple pieces. :) */
956 NORM_ERR("open_sysfs_sensor(): can't read from sysfs");
959 *divisor = atoi(divbuf);
967 static double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
975 lseek(*fd, 0, SEEK_SET);
981 n = read(*fd, buf, 63);
982 /* should read until n == 0 but I doubt that kernel will give these
983 * in multiple pieces. :) */
985 NORM_ERR("get_sysfs_info(): read from %s failed\n", devtype);
994 *fd = open(devtype, O_RDONLY);
996 NORM_ERR("can't open '%s': %s", devtype, strerror(errno));
999 /* My dirty hack for computing CPU value
1000 * Filedil, from forums.gentoo.org */
1001 /* if (strstr(devtype, "temp1_input") != NULL) {
1002 return -15.096 + 1.4893 * (val / 1000.0);
1005 /* divide voltage and temperature by 1000 */
1006 /* or if any other divisor is given, use that */
1007 if (strcmp(type, "tempf") == 0) {
1009 return ((val / divisor + 40) * 9.0 / 5) - 40;
1010 } else if (divisor) {
1011 return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
1013 return ((val + 40) * 9.0 / 5) - 40;
1017 return val / divisor;
1018 } else if (divisor) {
1019 return val / 1000.0;
1026 #define HWMON_RESET() {\
1031 static void parse_sysfs_sensor(struct text_object *obj, const char *arg, const char *path, const char *type)
1033 char buf1[64], buf2[64];
1034 float factor, offset;
1038 if (sscanf(arg, "%63s %d %f %f", buf2, &n, &factor, &offset) == 4) found = 1; else HWMON_RESET();
1039 if (!found && sscanf(arg, "%63s %63s %d %f %f", buf1, buf2, &n, &factor, &offset) == 5) found = 1; else if (!found) HWMON_RESET();
1040 if (!found && sscanf(arg, "%63s %63s %d", buf1, buf2, &n) == 3) found = 1; else if (!found) HWMON_RESET();
1041 if (!found && sscanf(arg, "%63s %d", buf2, &n) == 2) found = 1; else if (!found) HWMON_RESET();
1044 NORM_ERR("i2c failed to parse arguments");
1045 obj->type = OBJ_text;
1048 DBGP("parsed %s args: '%s' '%s' %d %f %f\n", type, buf1, buf2, n, factor, offset);
1049 sf = malloc(sizeof(struct sysfs));
1050 memset(sf, 0, sizeof(struct sysfs));
1051 sf->fd = open_sysfs_sensor(path, (*buf1) ? buf1 : 0, buf2, n,
1052 &sf->arg, sf->devtype);
1053 strncpy(sf->type, buf2, 63);
1054 sf->factor = factor;
1055 sf->offset = offset;
1056 obj->data.opaque = sf;
1059 #define PARSER_GENERATOR(name, path) \
1060 void parse_##name##_sensor(struct text_object *obj, const char *arg) \
1062 parse_sysfs_sensor(obj, arg, path, #name); \
1065 PARSER_GENERATOR(i2c, "/sys/bus/i2c/devices/")
1066 PARSER_GENERATOR(hwmon, "/sys/class/hwmon/")
1067 PARSER_GENERATOR(platform, "/sys/bus/platform/devices/")
1069 void print_sysfs_sensor(struct text_object *obj, char *p, int p_max_size)
1072 struct sysfs *sf = obj->data.opaque;
1077 r = get_sysfs_info(&sf->fd, sf->arg,
1078 sf->devtype, sf->type);
1080 r = r * sf->factor + sf->offset;
1082 if (!strncmp(sf->type, "temp", 4)) {
1083 temp_print(p, p_max_size, r, TEMP_CELSIUS);
1084 } else if (r >= 100.0 || r == 0) {
1085 snprintf(p, p_max_size, "%d", (int) r);
1087 snprintf(p, p_max_size, "%.1f", r);
1091 void free_sysfs_sensor(struct text_object *obj)
1093 struct sysfs *sf = obj->data.opaque;
1099 free(obj->data.opaque);
1100 obj->data.opaque = NULL;
1103 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1104 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1106 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1107 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1108 const char *p_format, int divisor, unsigned int cpu)
1116 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1122 char current_freq_file[128];
1124 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1126 f = fopen(current_freq_file, "r");
1128 /* if there's a cpufreq /sys node, read the current frequency from
1129 * this node and divide by 1000 to get Mhz. */
1130 if (fgets(s, sizeof(s), f)) {
1131 s[strlen(s) - 1] = '\0';
1132 freq = strtod(s, NULL);
1135 snprintf(p_client_buffer, client_buffer_size, p_format,
1136 (freq / 1000) / divisor);
1141 // open the CPU information file
1142 f = open_file("/proc/cpuinfo", &rep);
1144 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1149 while (fgets(s, sizeof(s), f) != NULL) {
1151 #if defined(__i386) || defined(__x86_64)
1152 // and search for the cpu mhz
1153 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1155 #if defined(__alpha)
1156 // different on alpha
1157 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1159 // this is different on ppc for some reason
1160 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1161 #endif // defined(__alpha)
1162 #endif // defined(__i386) || defined(__x86_64)
1164 // copy just the number
1165 strcpy(frequency, strchr(s, ':') + 2);
1166 #if defined(__alpha)
1168 frequency[strlen(frequency) - 6] = '\0';
1169 // kernel reports in Hz
1170 freq = strtod(frequency, NULL) / 1000000;
1173 frequency[strlen(frequency) - 1] = '\0';
1174 freq = strtod(frequency, NULL);
1178 if (strncmp(s, "processor", 9) == 0) {
1185 snprintf(p_client_buffer, client_buffer_size, p_format,
1186 (float) freq / divisor);
1190 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1192 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1202 * Peter Tarjan (ptarjan@citromail.hu) */
1204 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1205 static char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1206 const char *p_format, int divisor, unsigned int cpu)
1212 char current_freq_file[128];
1215 /* build the voltage file name */
1217 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1220 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1225 /* read the current cpu frequency from the /sys node */
1226 f = fopen(current_freq_file, "r");
1228 if (fgets(s, sizeof(s), f)) {
1229 s[strlen(s) - 1] = '\0';
1230 freq = strtod(s, NULL);
1234 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1235 perror("get_voltage()");
1242 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1245 /* use the current cpu frequency to find the corresponding voltage */
1246 f = fopen(current_freq_file, "r");
1252 if (fgets(line, 255, f) == NULL) {
1255 sscanf(line, "%d %d", &freq_comp, &voltage);
1256 if (freq_comp == freq) {
1262 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1263 perror("get_voltage()");
1269 snprintf(p_client_buffer, client_buffer_size, p_format,
1270 (float) voltage / divisor);
1274 void print_voltage_mv(struct text_object *obj, char *p, int p_max_size)
1278 ok = get_voltage(p, p_max_size, "%.0f", 1, obj->data.i);
1282 void print_voltage_v(struct text_object *obj, char *p, int p_max_size)
1286 ok = get_voltage(p, p_max_size, "%'.3f", 1000, obj->data.i);
1290 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1292 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1299 if (!p_client_buffer || client_buffer_size <= 0) {
1303 /* yeah, slow... :/ */
1304 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1305 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1309 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1311 fp = open_file(buf2, &rep);
1313 snprintf(p_client_buffer, client_buffer_size,
1314 "can't open fan's state file");
1317 memset(buf, 0, sizeof(buf));
1318 fscanf(fp, "%*s %99s", buf);
1321 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1324 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply"
1325 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1326 /* Linux 2.6.25 onwards ac adapter info is in
1327 /sys/class/power_supply/AC/
1328 On my system I get the following.
1329 /sys/class/power_supply/AC/uevent:
1330 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1333 POWER_SUPPLY_NAME=AC
1334 POWER_SUPPLY_TYPE=Mains
1335 POWER_SUPPLY_ONLINE=1
1337 Update: it seems the folder name is hardware-dependent. We add an aditional adapter
1338 argument, specifying the folder name.
1341 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size, const char *adapter)
1349 if (!p_client_buffer || client_buffer_size <= 0) {
1353 snprintf(buf2, sizeof(buf2), "%s/%s/uevent", SYSFS_AC_ADAPTER_DIR, adapter);
1354 fp = open_file(buf2, &rep);
1356 /* sysfs processing */
1358 if (fgets(buf, sizeof(buf), fp) == NULL)
1361 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1363 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1364 snprintf(p_client_buffer, client_buffer_size,
1365 "%s-line", (online ? "on" : "off"));
1371 /* yeah, slow... :/ */
1372 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1373 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1377 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1379 fp = open_file(buf2, &rep);
1381 snprintf(p_client_buffer, client_buffer_size,
1382 "No ac adapter found.... where is it?");
1385 memset(buf, 0, sizeof(buf));
1386 fscanf(fp, "%*s %99s", buf);
1389 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1394 /proc/acpi/thermal_zone/THRM/cooling_mode
1395 cooling mode: active
1396 /proc/acpi/thermal_zone/THRM/polling_frequency
1398 /proc/acpi/thermal_zone/THRM/state
1400 /proc/acpi/thermal_zone/THRM/temperature
1402 /proc/acpi/thermal_zone/THRM/trip_points
1404 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1407 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1408 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1410 int open_acpi_temperature(const char *name)
1416 if (name == NULL || strcmp(name, "*") == 0) {
1419 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1425 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1427 fd = open(path, O_RDONLY);
1429 NORM_ERR("can't open '%s': %s", path, strerror(errno));
1435 static double last_acpi_temp;
1436 static double last_acpi_temp_time;
1438 double get_acpi_temperature(int fd)
1444 /* don't update acpi temperature too often */
1445 if (current_update_time - last_acpi_temp_time < 11.32) {
1446 return last_acpi_temp;
1448 last_acpi_temp_time = current_update_time;
1450 /* seek to beginning */
1451 lseek(fd, 0, SEEK_SET);
1458 n = read(fd, buf, 255);
1460 NORM_ERR("can't read fd %d: %s", fd, strerror(errno));
1463 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1467 return last_acpi_temp;
1471 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1473 design capacity: 4400 mAh
1474 last full capacity: 4064 mAh
1475 battery technology: rechargeable
1476 design voltage: 14800 mV
1477 design capacity warning: 300 mAh
1478 design capacity low: 200 mAh
1479 capacity granularity 1: 32 mAh
1480 capacity granularity 2: 32 mAh
1482 serial number: 16922
1488 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1491 charging state: unknown
1493 remaining capacity: 4064 mAh
1494 present voltage: 16608 mV
1498 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1499 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1500 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1501 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1502 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1504 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1505 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1507 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1508 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1511 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1512 Linux 2.6.24 onwards battery info is in
1513 /sys/class/power_supply/BAT0/
1514 On my system I get the following.
1515 /sys/class/power_supply/BAT0/uevent:
1516 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1518 PHYSDEVDRIVER=battery
1519 POWER_SUPPLY_NAME=BAT0
1520 POWER_SUPPLY_TYPE=Battery
1521 POWER_SUPPLY_STATUS=Discharging
1522 POWER_SUPPLY_PRESENT=1
1523 POWER_SUPPLY_TECHNOLOGY=Li-ion
1524 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1525 POWER_SUPPLY_VOLTAGE_NOW=10780000
1526 POWER_SUPPLY_CURRENT_NOW=13970000
1527 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1528 POWER_SUPPLY_ENERGY_FULL=27370000
1529 POWER_SUPPLY_ENERGY_NOW=11810000
1530 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1531 POWER_SUPPLY_MANUFACTURER=Panasonic
1532 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1535 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1536 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1537 #define APM_PATH "/proc/apm"
1538 #define MAX_BATTERY_COUNT 4
1540 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1541 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1542 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1544 static int batteries_initialized = 0;
1545 static char batteries[MAX_BATTERY_COUNT][32];
1547 static int acpi_last_full[MAX_BATTERY_COUNT];
1548 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1550 /* e.g. "charging 75%" */
1551 static char last_battery_str[MAX_BATTERY_COUNT][64];
1553 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1555 static double last_battery_time[MAX_BATTERY_COUNT];
1557 static int last_battery_perct[MAX_BATTERY_COUNT];
1558 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1560 void init_batteries(void)
1564 if (batteries_initialized) {
1568 #pragma omp parallel for schedule(dynamic,10)
1569 #endif /* HAVE_OPENMP */
1570 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1571 batteries[idx][0] = '\0';
1573 batteries_initialized = 1;
1576 int get_battery_idx(const char *bat)
1580 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1581 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1586 /* if not found, enter a new entry */
1587 if (!strlen(batteries[idx])) {
1588 snprintf(batteries[idx], 31, "%s", bat);
1594 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1596 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1598 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1599 char acpi_path[128];
1600 char sysfs_path[128];
1602 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1603 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1607 idx = get_battery_idx(bat);
1609 /* don't update battery too often */
1610 if (current_update_time - last_battery_time[idx] < 29.5) {
1611 set_return_value(buffer, n, item, idx);
1615 last_battery_time[idx] = current_update_time;
1617 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1618 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1620 /* first try SYSFS if that fails try ACPI */
1622 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1623 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1626 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1627 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1630 if (sysfs_bat_fp[idx] != NULL) {
1632 int present_rate = -1;
1633 int remaining_capacity = -1;
1634 char charging_state[64];
1637 strcpy(charging_state, "unknown");
1639 while (!feof(sysfs_bat_fp[idx])) {
1641 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1644 /* let's just hope units are ok */
1645 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1646 strcpy(present, "yes");
1647 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1648 strcpy(present, "no");
1649 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1650 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1651 /* present_rate is not the same as the
1652 current flowing now but it is the same value
1653 which was used in the past. so we continue
1655 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1656 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1657 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1658 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1659 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1660 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1661 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1662 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1663 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1664 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1667 fclose(sysfs_bat_fp[idx]);
1668 sysfs_bat_fp[idx] = NULL;
1670 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1671 if (remaining_capacity > acpi_last_full[idx])
1672 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1675 if (strcmp(present, "No") == 0) {
1676 strncpy(last_battery_str[idx], "not present", 64);
1679 else if (strcmp(charging_state, "Charging") == 0) {
1680 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1681 /* e.g. charging 75% */
1682 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1683 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1685 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1686 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1687 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1688 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1689 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1690 snprintf(last_battery_time_str[idx],
1691 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1693 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1694 snprintf(last_battery_time_str[idx],
1695 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1699 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1700 if (present_rate > 0) {
1701 /* e.g. discharging 35% */
1702 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1703 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1705 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1706 (long) (((float) remaining_capacity / present_rate) * 3600));
1707 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1708 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1709 snprintf(last_battery_time_str[idx],
1710 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1712 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1714 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1715 snprintf(last_battery_time_str[idx],
1716 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1720 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1721 else if (strncmp(charging_state, "Charged", 64) == 0 || strncmp(charging_state, "Full", 64) == 0) {
1722 /* Below happens with the second battery on my X40,
1723 * when the second one is empty and the first one
1725 if (remaining_capacity == 0)
1726 strcpy(last_battery_str[idx], "empty");
1728 strcpy(last_battery_str[idx], "charged");
1730 /* unknown, probably full / AC */
1732 if (acpi_last_full[idx] != 0
1733 && remaining_capacity != acpi_last_full[idx])
1734 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1735 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1737 strncpy(last_battery_str[idx], "AC", 64);
1739 } else if (acpi_bat_fp[idx] != NULL) {
1741 int present_rate = -1;
1742 int remaining_capacity = -1;
1743 char charging_state[64];
1746 /* read last full capacity if it's zero */
1747 if (acpi_last_full[idx] == 0) {
1748 static int rep3 = 0;
1752 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1753 fp = open_file(path, &rep3);
1758 if (fgets(b, 256, fp) == NULL) {
1761 if (sscanf(b, "last full capacity: %d",
1762 &acpi_last_full[idx]) != 0) {
1771 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1773 strcpy(charging_state, "unknown");
1775 while (!feof(acpi_bat_fp[idx])) {
1778 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1782 /* let's just hope units are ok */
1783 if (strncmp(buf, "present:", 8) == 0) {
1784 sscanf(buf, "present: %4s", present);
1785 } else if (strncmp(buf, "charging state:", 15) == 0) {
1786 sscanf(buf, "charging state: %63s", charging_state);
1787 } else if (strncmp(buf, "present rate:", 13) == 0) {
1788 sscanf(buf, "present rate: %d", &present_rate);
1789 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1790 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1793 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1794 if (remaining_capacity > acpi_last_full[idx]) {
1795 /* normalize to 100% */
1796 acpi_last_full[idx] = remaining_capacity;
1800 if (strcmp(present, "no") == 0) {
1801 strncpy(last_battery_str[idx], "not present", 64);
1803 } else if (strcmp(charging_state, "charging") == 0) {
1804 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1805 /* e.g. charging 75% */
1806 snprintf(last_battery_str[idx],
1807 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1808 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1810 format_seconds(last_battery_time_str[idx],
1811 sizeof(last_battery_time_str[idx]) - 1,
1812 (long) (((acpi_last_full[idx] - remaining_capacity) *
1813 3600) / present_rate));
1814 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1815 snprintf(last_battery_str[idx],
1816 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1817 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1818 snprintf(last_battery_time_str[idx],
1819 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1821 strncpy(last_battery_str[idx], "charging",
1822 sizeof(last_battery_str[idx]) - 1);
1823 snprintf(last_battery_time_str[idx],
1824 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1827 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1828 if (present_rate > 0) {
1829 /* e.g. discharging 35% */
1830 snprintf(last_battery_str[idx],
1831 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1832 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1834 format_seconds(last_battery_time_str[idx],
1835 sizeof(last_battery_time_str[idx]) - 1,
1836 (long) ((remaining_capacity * 3600) / present_rate));
1837 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1838 snprintf(last_battery_str[idx],
1839 sizeof(last_battery_str[idx]) - 1, "full");
1840 snprintf(last_battery_time_str[idx],
1841 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1843 snprintf(last_battery_str[idx],
1844 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1845 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1846 snprintf(last_battery_time_str[idx],
1847 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1850 } else if (strncmp(charging_state, "charged", 64) == 0) {
1851 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1852 /* Below happens with the second battery on my X40,
1853 * when the second one is empty and the first one being charged. */
1854 if (remaining_capacity == 0) {
1855 strcpy(last_battery_str[idx], "empty");
1857 strcpy(last_battery_str[idx], "charged");
1859 /* unknown, probably full / AC */
1861 if (strncmp(charging_state, "Full", 64) == 0) {
1862 strncpy(last_battery_str[idx], "full", 64);
1863 } else if (acpi_last_full[idx] != 0
1864 && remaining_capacity != acpi_last_full[idx]) {
1865 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1866 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1868 strncpy(last_battery_str[idx], "AC", 64);
1871 fclose(acpi_bat_fp[idx]);
1872 acpi_bat_fp[idx] = NULL;
1875 if (apm_bat_fp[idx] == NULL) {
1876 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1879 if (apm_bat_fp[idx] != NULL) {
1880 unsigned int ac, status, flag;
1883 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1884 &ac, &status, &flag, &life);
1887 /* could check now that there is ac */
1888 snprintf(last_battery_str[idx], 64, "AC");
1890 /* could check that status == 3 here? */
1891 } else if (ac && life != 100) {
1892 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1894 snprintf(last_battery_str[idx], 64, "%d%%", life);
1897 /* it seemed to buffer it so file must be closed (or could use
1898 * syscalls directly but I don't feel like coding it now) */
1899 fclose(apm_bat_fp[idx]);
1900 apm_bat_fp[idx] = NULL;
1903 set_return_value(buffer, n, item, idx);
1906 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1909 case BATTERY_STATUS:
1910 snprintf(buffer, n, "%s", last_battery_str[idx]);
1913 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1920 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1922 get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1923 if (0 == strncmp("charging", buffer, 8)) {
1925 memmove(buffer + 1, buffer + 8, n - 8);
1926 } else if (0 == strncmp("discharging", buffer, 11)) {
1928 memmove(buffer + 1, buffer + 11, n - 11);
1929 } else if (0 == strncmp("charged", buffer, 7)) {
1931 memmove(buffer + 1, buffer + 7, n - 7);
1932 } else if (0 == strncmp("not present", buffer, 11)) {
1934 memmove(buffer + 1, buffer + 11, n - 11);
1935 } else if (0 == strncmp("empty", buffer, 5)) {
1937 memmove(buffer + 1, buffer + 5, n - 5);
1938 } else if (0 != strncmp("AC", buffer, 2)) {
1940 memmove(buffer + 1, buffer + 11, n - 11);
1944 int get_battery_perct(const char *bat)
1948 char acpi_path[128];
1949 char sysfs_path[128];
1950 int remaining_capacity = -1;
1952 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1953 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1957 idx = get_battery_idx(bat);
1959 /* don't update battery too often */
1960 if (current_update_time - last_battery_perct_time[idx] < 30) {
1961 return last_battery_perct[idx];
1963 last_battery_perct_time[idx] = current_update_time;
1965 /* Only check for SYSFS or ACPI */
1967 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1968 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1972 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1973 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1976 if (sysfs_bat_fp[idx] != NULL) {
1978 while (!feof(sysfs_bat_fp[idx])) {
1980 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1983 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1984 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1985 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1986 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1987 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1988 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1989 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1990 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1994 fclose(sysfs_bat_fp[idx]);
1995 sysfs_bat_fp[idx] = NULL;
1997 } else if (acpi_bat_fp[idx] != NULL) {
1999 /* read last full capacity if it's zero */
2000 if (acpi_design_capacity[idx] == 0) {
2005 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
2006 fp = open_file(path, &rep2);
2011 if (fgets(b, 256, fp) == NULL) {
2014 if (sscanf(b, "last full capacity: %d",
2015 &acpi_design_capacity[idx]) != 0) {
2023 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
2025 while (!feof(acpi_bat_fp[idx])) {
2028 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
2032 if (buf[0] == 'r') {
2033 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
2037 if (remaining_capacity < 0) {
2040 /* compute the battery percentage */
2041 last_battery_perct[idx] =
2042 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
2043 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
2044 return last_battery_perct[idx];
2047 int get_battery_perct_bar(const char *bar)
2051 get_battery_perct(bar);
2052 idx = get_battery_idx(bar);
2053 return (int) (last_battery_perct[idx] * 2.56 - 1);
2056 /* On Apple powerbook and ibook:
2057 $ cat /proc/pmu/battery_0
2064 $ cat /proc/pmu/info
2065 PMU driver version : 2
2066 PMU firmware version : 0c
2071 /* defines as in <linux/pmu.h> */
2072 #define PMU_BATT_PRESENT 0x00000001
2073 #define PMU_BATT_CHARGING 0x00000002
2075 static FILE *pmu_battery_fp;
2076 static FILE *pmu_info_fp;
2077 static char pb_battery_info[3][32];
2078 static double pb_battery_info_update;
2080 #define PMU_PATH "/proc/pmu"
2081 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2084 const char *batt_path = PMU_PATH "/battery_0";
2085 const char *info_path = PMU_PATH "/info";
2087 int charge, max_charge, ac = -1;
2090 /* don't update battery too often */
2091 if (current_update_time - pb_battery_info_update < 29.5) {
2092 snprintf(buffer, n, "%s", pb_battery_info[i]);
2095 pb_battery_info_update = current_update_time;
2097 if (pmu_battery_fp == NULL) {
2098 pmu_battery_fp = open_file(batt_path, &rep);
2099 if (pmu_battery_fp == NULL) {
2104 if (pmu_battery_fp != NULL) {
2105 rewind(pmu_battery_fp);
2106 while (!feof(pmu_battery_fp)) {
2109 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2113 if (buf[0] == 'f') {
2114 sscanf(buf, "flags : %8x", &flags);
2115 } else if (buf[0] == 'c' && buf[1] == 'h') {
2116 sscanf(buf, "charge : %d", &charge);
2117 } else if (buf[0] == 'm') {
2118 sscanf(buf, "max_charge : %d", &max_charge);
2119 } else if (buf[0] == 't') {
2120 sscanf(buf, "time rem. : %ld", &timeval);
2124 if (pmu_info_fp == NULL) {
2125 pmu_info_fp = open_file(info_path, &rep);
2126 if (pmu_info_fp == NULL) {
2131 if (pmu_info_fp != NULL) {
2132 rewind(pmu_info_fp);
2133 while (!feof(pmu_info_fp)) {
2136 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2139 if (buf[0] == 'A') {
2140 sscanf(buf, "AC Power : %d", &ac);
2144 /* update status string */
2145 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2146 strncpy(pb_battery_info[PB_BATT_STATUS], "AC", sizeof(pb_battery_info[PB_BATT_STATUS]));
2147 } else if (ac && (flags & PMU_BATT_PRESENT)
2148 && !(flags & PMU_BATT_CHARGING)) {
2149 strncpy(pb_battery_info[PB_BATT_STATUS], "charged", sizeof(pb_battery_info[PB_BATT_STATUS]));
2150 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2151 strncpy(pb_battery_info[PB_BATT_STATUS], "charging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2153 strncpy(pb_battery_info[PB_BATT_STATUS], "discharging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2156 /* update percentage string */
2157 if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT)
2158 && !(flags & PMU_BATT_CHARGING)) {
2159 snprintf(pb_battery_info[PB_BATT_PERCENT],
2160 sizeof(pb_battery_info[PB_BATT_PERCENT]), "100%%");
2161 } else if (timeval == 0) {
2162 snprintf(pb_battery_info[PB_BATT_PERCENT],
2163 sizeof(pb_battery_info[PB_BATT_PERCENT]), "unknown");
2165 snprintf(pb_battery_info[PB_BATT_PERCENT],
2166 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2167 (charge * 100) / max_charge);
2170 /* update time string */
2171 if (timeval == 0) { /* fully charged or battery not present */
2172 snprintf(pb_battery_info[PB_BATT_TIME],
2173 sizeof(pb_battery_info[PB_BATT_TIME]), "unknown");
2174 } else if (timeval < 60 * 60) { /* don't show secs */
2175 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2176 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2178 format_seconds(pb_battery_info[PB_BATT_TIME],
2179 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2182 snprintf(buffer, n, "%s", pb_battery_info[i]);
2185 void update_top(void)
2187 process_find_top(info.cpu, info.memu, info.time
2192 info.first_process = get_first_process();
2195 #define ENTROPY_AVAIL_PATH "/proc/sys/kernel/random/entropy_avail"
2197 int get_entropy_avail(unsigned int *val)
2202 if (!(fp = open_file(ENTROPY_AVAIL_PATH, &rep)))
2205 if (fscanf(fp, "%u", val) != 1)
2212 #define ENTROPY_POOLSIZE_PATH "/proc/sys/kernel/random/poolsize"
2214 int get_entropy_poolsize(unsigned int *val)
2219 if (!(fp = open_file(ENTROPY_POOLSIZE_PATH, &rep)))
2222 if (fscanf(fp, "%u", val) != 1)
2229 const char *get_disk_protect_queue(const char *disk)
2235 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2236 if (access(path, F_OK)) {
2237 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2239 if ((fp = fopen(path, "r")) == NULL)
2241 if (fscanf(fp, "%d\n", &state) != 1) {
2246 return (state > 0) ? "frozen" : "free ";
2249 void update_diskio(void)
2253 char buf[512], devbuf[64];
2254 unsigned int major, minor;
2256 struct diskio_stat *cur;
2257 unsigned int reads, writes;
2258 unsigned int total_reads = 0, total_writes = 0;
2261 stats.current_read = 0;
2262 stats.current_write = 0;
2264 if (!(fp = open_file("/proc/diskstats", &rep))) {
2268 /* read reads and writes from all disks (minor = 0), including cd-roms
2269 * and floppies, and sum them up */
2270 while (fgets(buf, 512, fp)) {
2271 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
2272 &minor, devbuf, &reads, &writes);
2273 /* ignore subdevices (they have only 3 matching entries in their line)
2274 * and virtual devices (LVM, network block devices, RAM disks, Loopback)
2276 * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
2277 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
2278 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
2279 total_reads += reads;
2280 total_writes += writes;
2282 col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
2283 &major, &minor, devbuf, &reads, &writes);
2284 if (col_count != 5) {
2289 while (cur && strcmp(devbuf, cur->dev))
2293 update_diskio_values(cur, reads, writes);
2295 update_diskio_values(&stats, total_reads, total_writes);