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.
1340 Update: on some systems it's /sys/class/power_supply/ADP1 instead of /sys/class/power_supply/AC
1343 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size, const char *adapter)
1352 if (!p_client_buffer || client_buffer_size <= 0) {
1357 snprintf(buf2, sizeof(buf2), "%s/%s/uevent", SYSFS_AC_ADAPTER_DIR, adapter);
1359 snprintf(buf2, sizeof(buf2), "%s/AC/uevent", SYSFS_AC_ADAPTER_DIR);
1360 if(stat(buf2, &sb) == -1) snprintf(buf2, sizeof(buf2), "%s/ADP1/uevent", SYSFS_AC_ADAPTER_DIR);
1362 if(stat(buf2, &sb) == 0) fp = open_file(buf2, &rep); else fp = 0;
1364 /* sysfs processing */
1366 if (fgets(buf, sizeof(buf), fp) == NULL)
1369 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1371 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1372 snprintf(p_client_buffer, client_buffer_size,
1373 "%s-line", (online ? "on" : "off"));
1379 /* yeah, slow... :/ */
1380 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1381 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1385 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1387 fp = open_file(buf2, &rep);
1389 snprintf(p_client_buffer, client_buffer_size,
1390 "No ac adapter found.... where is it?");
1393 memset(buf, 0, sizeof(buf));
1394 fscanf(fp, "%*s %99s", buf);
1397 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1402 /proc/acpi/thermal_zone/THRM/cooling_mode
1403 cooling mode: active
1404 /proc/acpi/thermal_zone/THRM/polling_frequency
1406 /proc/acpi/thermal_zone/THRM/state
1408 /proc/acpi/thermal_zone/THRM/temperature
1410 /proc/acpi/thermal_zone/THRM/trip_points
1412 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1415 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1416 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1418 int open_acpi_temperature(const char *name)
1424 if (name == NULL || strcmp(name, "*") == 0) {
1427 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1433 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1435 fd = open(path, O_RDONLY);
1437 NORM_ERR("can't open '%s': %s", path, strerror(errno));
1443 static double last_acpi_temp;
1444 static double last_acpi_temp_time;
1446 double get_acpi_temperature(int fd)
1452 /* don't update acpi temperature too often */
1453 if (current_update_time - last_acpi_temp_time < 11.32) {
1454 return last_acpi_temp;
1456 last_acpi_temp_time = current_update_time;
1458 /* seek to beginning */
1459 lseek(fd, 0, SEEK_SET);
1466 n = read(fd, buf, 255);
1468 NORM_ERR("can't read fd %d: %s", fd, strerror(errno));
1471 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1475 return last_acpi_temp;
1479 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1481 design capacity: 4400 mAh
1482 last full capacity: 4064 mAh
1483 battery technology: rechargeable
1484 design voltage: 14800 mV
1485 design capacity warning: 300 mAh
1486 design capacity low: 200 mAh
1487 capacity granularity 1: 32 mAh
1488 capacity granularity 2: 32 mAh
1490 serial number: 16922
1496 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1499 charging state: unknown
1501 remaining capacity: 4064 mAh
1502 present voltage: 16608 mV
1506 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1507 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1508 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1509 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1510 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1512 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1513 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1515 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1516 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1519 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1520 Linux 2.6.24 onwards battery info is in
1521 /sys/class/power_supply/BAT0/
1522 On my system I get the following.
1523 /sys/class/power_supply/BAT0/uevent:
1524 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1526 PHYSDEVDRIVER=battery
1527 POWER_SUPPLY_NAME=BAT0
1528 POWER_SUPPLY_TYPE=Battery
1529 POWER_SUPPLY_STATUS=Discharging
1530 POWER_SUPPLY_PRESENT=1
1531 POWER_SUPPLY_TECHNOLOGY=Li-ion
1532 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1533 POWER_SUPPLY_VOLTAGE_NOW=10780000
1534 POWER_SUPPLY_CURRENT_NOW=13970000
1535 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1536 POWER_SUPPLY_ENERGY_FULL=27370000
1537 POWER_SUPPLY_ENERGY_NOW=11810000
1538 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1539 POWER_SUPPLY_MANUFACTURER=Panasonic
1540 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1543 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1544 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1545 #define APM_PATH "/proc/apm"
1546 #define MAX_BATTERY_COUNT 4
1548 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1549 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1550 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1552 static int batteries_initialized = 0;
1553 static char batteries[MAX_BATTERY_COUNT][32];
1555 static int acpi_last_full[MAX_BATTERY_COUNT];
1556 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1558 /* e.g. "charging 75%" */
1559 static char last_battery_str[MAX_BATTERY_COUNT][64];
1561 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1563 static double last_battery_time[MAX_BATTERY_COUNT];
1565 static int last_battery_perct[MAX_BATTERY_COUNT];
1566 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1568 void init_batteries(void)
1572 if (batteries_initialized) {
1576 #pragma omp parallel for schedule(dynamic,10)
1577 #endif /* HAVE_OPENMP */
1578 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1579 batteries[idx][0] = '\0';
1581 batteries_initialized = 1;
1584 int get_battery_idx(const char *bat)
1588 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1589 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1594 /* if not found, enter a new entry */
1595 if (!strlen(batteries[idx])) {
1596 snprintf(batteries[idx], 31, "%s", bat);
1602 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1604 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1606 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1607 char acpi_path[128];
1608 char sysfs_path[128];
1610 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1611 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1615 idx = get_battery_idx(bat);
1617 /* don't update battery too often */
1618 if (current_update_time - last_battery_time[idx] < 29.5) {
1619 set_return_value(buffer, n, item, idx);
1623 last_battery_time[idx] = current_update_time;
1625 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1626 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1628 /* first try SYSFS if that fails try ACPI */
1630 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1631 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1634 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1635 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1638 if (sysfs_bat_fp[idx] != NULL) {
1640 int present_rate = -1;
1641 int remaining_capacity = -1;
1642 char charging_state[64];
1645 strcpy(charging_state, "unknown");
1647 while (!feof(sysfs_bat_fp[idx])) {
1649 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1652 /* let's just hope units are ok */
1653 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1654 strcpy(present, "yes");
1655 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1656 strcpy(present, "no");
1657 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1658 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1659 /* present_rate is not the same as the
1660 current flowing now but it is the same value
1661 which was used in the past. so we continue
1663 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1664 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1665 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1666 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1667 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1668 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1669 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1670 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1671 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1672 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1675 fclose(sysfs_bat_fp[idx]);
1676 sysfs_bat_fp[idx] = NULL;
1678 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1679 if (remaining_capacity > acpi_last_full[idx])
1680 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1683 if (strcmp(present, "No") == 0) {
1684 strncpy(last_battery_str[idx], "not present", 64);
1687 else if (strcmp(charging_state, "Charging") == 0) {
1688 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1689 /* e.g. charging 75% */
1690 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1691 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1693 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1694 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1695 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1696 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1697 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1698 snprintf(last_battery_time_str[idx],
1699 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1701 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1702 snprintf(last_battery_time_str[idx],
1703 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1707 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1708 if (present_rate > 0) {
1709 /* e.g. discharging 35% */
1710 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1711 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1713 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1714 (long) (((float) remaining_capacity / present_rate) * 3600));
1715 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1716 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1717 snprintf(last_battery_time_str[idx],
1718 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1720 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1722 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1723 snprintf(last_battery_time_str[idx],
1724 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1728 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1729 else if (strncmp(charging_state, "Charged", 64) == 0 || strncmp(charging_state, "Full", 64) == 0) {
1730 /* Below happens with the second battery on my X40,
1731 * when the second one is empty and the first one
1733 if (remaining_capacity == 0)
1734 strcpy(last_battery_str[idx], "empty");
1736 strcpy(last_battery_str[idx], "charged");
1738 /* unknown, probably full / AC */
1740 if (acpi_last_full[idx] != 0
1741 && remaining_capacity != acpi_last_full[idx])
1742 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1743 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1745 strncpy(last_battery_str[idx], "AC", 64);
1747 } else if (acpi_bat_fp[idx] != NULL) {
1749 int present_rate = -1;
1750 int remaining_capacity = -1;
1751 char charging_state[64];
1754 /* read last full capacity if it's zero */
1755 if (acpi_last_full[idx] == 0) {
1756 static int rep3 = 0;
1760 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1761 fp = open_file(path, &rep3);
1766 if (fgets(b, 256, fp) == NULL) {
1769 if (sscanf(b, "last full capacity: %d",
1770 &acpi_last_full[idx]) != 0) {
1779 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1781 strcpy(charging_state, "unknown");
1783 while (!feof(acpi_bat_fp[idx])) {
1786 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1790 /* let's just hope units are ok */
1791 if (strncmp(buf, "present:", 8) == 0) {
1792 sscanf(buf, "present: %4s", present);
1793 } else if (strncmp(buf, "charging state:", 15) == 0) {
1794 sscanf(buf, "charging state: %63s", charging_state);
1795 } else if (strncmp(buf, "present rate:", 13) == 0) {
1796 sscanf(buf, "present rate: %d", &present_rate);
1797 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1798 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1801 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1802 if (remaining_capacity > acpi_last_full[idx]) {
1803 /* normalize to 100% */
1804 acpi_last_full[idx] = remaining_capacity;
1808 if (strcmp(present, "no") == 0) {
1809 strncpy(last_battery_str[idx], "not present", 64);
1811 } else if (strcmp(charging_state, "charging") == 0) {
1812 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1813 /* e.g. charging 75% */
1814 snprintf(last_battery_str[idx],
1815 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1816 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1818 format_seconds(last_battery_time_str[idx],
1819 sizeof(last_battery_time_str[idx]) - 1,
1820 (long) (((acpi_last_full[idx] - remaining_capacity) *
1821 3600) / present_rate));
1822 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1823 snprintf(last_battery_str[idx],
1824 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1825 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1826 snprintf(last_battery_time_str[idx],
1827 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1829 strncpy(last_battery_str[idx], "charging",
1830 sizeof(last_battery_str[idx]) - 1);
1831 snprintf(last_battery_time_str[idx],
1832 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1835 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1836 if (present_rate > 0) {
1837 /* e.g. discharging 35% */
1838 snprintf(last_battery_str[idx],
1839 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1840 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1842 format_seconds(last_battery_time_str[idx],
1843 sizeof(last_battery_time_str[idx]) - 1,
1844 (long) ((remaining_capacity * 3600) / present_rate));
1845 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1846 snprintf(last_battery_str[idx],
1847 sizeof(last_battery_str[idx]) - 1, "full");
1848 snprintf(last_battery_time_str[idx],
1849 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1851 snprintf(last_battery_str[idx],
1852 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1853 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1854 snprintf(last_battery_time_str[idx],
1855 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1858 } else if (strncmp(charging_state, "charged", 64) == 0) {
1859 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1860 /* Below happens with the second battery on my X40,
1861 * when the second one is empty and the first one being charged. */
1862 if (remaining_capacity == 0) {
1863 strcpy(last_battery_str[idx], "empty");
1865 strcpy(last_battery_str[idx], "charged");
1867 /* unknown, probably full / AC */
1869 if (strncmp(charging_state, "Full", 64) == 0) {
1870 strncpy(last_battery_str[idx], "full", 64);
1871 } else if (acpi_last_full[idx] != 0
1872 && remaining_capacity != acpi_last_full[idx]) {
1873 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1874 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1876 strncpy(last_battery_str[idx], "AC", 64);
1879 fclose(acpi_bat_fp[idx]);
1880 acpi_bat_fp[idx] = NULL;
1883 if (apm_bat_fp[idx] == NULL) {
1884 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1887 if (apm_bat_fp[idx] != NULL) {
1888 unsigned int ac, status, flag;
1891 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1892 &ac, &status, &flag, &life);
1895 /* could check now that there is ac */
1896 snprintf(last_battery_str[idx], 64, "AC");
1898 /* could check that status == 3 here? */
1899 } else if (ac && life != 100) {
1900 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1902 snprintf(last_battery_str[idx], 64, "%d%%", life);
1905 /* it seemed to buffer it so file must be closed (or could use
1906 * syscalls directly but I don't feel like coding it now) */
1907 fclose(apm_bat_fp[idx]);
1908 apm_bat_fp[idx] = NULL;
1911 set_return_value(buffer, n, item, idx);
1914 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1917 case BATTERY_STATUS:
1918 snprintf(buffer, n, "%s", last_battery_str[idx]);
1921 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1928 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1930 get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1931 if (0 == strncmp("charging", buffer, 8)) {
1933 memmove(buffer + 1, buffer + 8, n - 8);
1934 } else if (0 == strncmp("discharging", buffer, 11)) {
1936 memmove(buffer + 1, buffer + 11, n - 11);
1937 } else if (0 == strncmp("charged", buffer, 7)) {
1939 memmove(buffer + 1, buffer + 7, n - 7);
1940 } else if (0 == strncmp("not present", buffer, 11)) {
1942 memmove(buffer + 1, buffer + 11, n - 11);
1943 } else if (0 == strncmp("empty", buffer, 5)) {
1945 memmove(buffer + 1, buffer + 5, n - 5);
1946 } else if (0 != strncmp("AC", buffer, 2)) {
1948 memmove(buffer + 1, buffer + 11, n - 11);
1952 int get_battery_perct(const char *bat)
1956 char acpi_path[128];
1957 char sysfs_path[128];
1958 int remaining_capacity = -1;
1960 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1961 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1965 idx = get_battery_idx(bat);
1967 /* don't update battery too often */
1968 if (current_update_time - last_battery_perct_time[idx] < 30) {
1969 return last_battery_perct[idx];
1971 last_battery_perct_time[idx] = current_update_time;
1973 /* Only check for SYSFS or ACPI */
1975 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1976 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1980 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1981 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1984 if (sysfs_bat_fp[idx] != NULL) {
1986 while (!feof(sysfs_bat_fp[idx])) {
1988 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1991 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1992 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1993 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1994 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1995 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1996 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1997 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1998 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
2002 fclose(sysfs_bat_fp[idx]);
2003 sysfs_bat_fp[idx] = NULL;
2005 } else if (acpi_bat_fp[idx] != NULL) {
2007 /* read last full capacity if it's zero */
2008 if (acpi_design_capacity[idx] == 0) {
2013 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
2014 fp = open_file(path, &rep2);
2019 if (fgets(b, 256, fp) == NULL) {
2022 if (sscanf(b, "last full capacity: %d",
2023 &acpi_design_capacity[idx]) != 0) {
2031 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
2033 while (!feof(acpi_bat_fp[idx])) {
2036 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
2040 if (buf[0] == 'r') {
2041 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
2045 if (remaining_capacity < 0) {
2048 /* compute the battery percentage */
2049 last_battery_perct[idx] =
2050 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
2051 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
2052 return last_battery_perct[idx];
2055 int get_battery_perct_bar(const char *bar)
2059 get_battery_perct(bar);
2060 idx = get_battery_idx(bar);
2061 return (int) (last_battery_perct[idx] * 2.56 - 1);
2064 /* On Apple powerbook and ibook:
2065 $ cat /proc/pmu/battery_0
2072 $ cat /proc/pmu/info
2073 PMU driver version : 2
2074 PMU firmware version : 0c
2079 /* defines as in <linux/pmu.h> */
2080 #define PMU_BATT_PRESENT 0x00000001
2081 #define PMU_BATT_CHARGING 0x00000002
2083 static FILE *pmu_battery_fp;
2084 static FILE *pmu_info_fp;
2085 static char pb_battery_info[3][32];
2086 static double pb_battery_info_update;
2088 #define PMU_PATH "/proc/pmu"
2089 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2092 const char *batt_path = PMU_PATH "/battery_0";
2093 const char *info_path = PMU_PATH "/info";
2095 int charge, max_charge, ac = -1;
2098 /* don't update battery too often */
2099 if (current_update_time - pb_battery_info_update < 29.5) {
2100 snprintf(buffer, n, "%s", pb_battery_info[i]);
2103 pb_battery_info_update = current_update_time;
2105 if (pmu_battery_fp == NULL) {
2106 pmu_battery_fp = open_file(batt_path, &rep);
2107 if (pmu_battery_fp == NULL) {
2112 if (pmu_battery_fp != NULL) {
2113 rewind(pmu_battery_fp);
2114 while (!feof(pmu_battery_fp)) {
2117 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2121 if (buf[0] == 'f') {
2122 sscanf(buf, "flags : %8x", &flags);
2123 } else if (buf[0] == 'c' && buf[1] == 'h') {
2124 sscanf(buf, "charge : %d", &charge);
2125 } else if (buf[0] == 'm') {
2126 sscanf(buf, "max_charge : %d", &max_charge);
2127 } else if (buf[0] == 't') {
2128 sscanf(buf, "time rem. : %ld", &timeval);
2132 if (pmu_info_fp == NULL) {
2133 pmu_info_fp = open_file(info_path, &rep);
2134 if (pmu_info_fp == NULL) {
2139 if (pmu_info_fp != NULL) {
2140 rewind(pmu_info_fp);
2141 while (!feof(pmu_info_fp)) {
2144 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2147 if (buf[0] == 'A') {
2148 sscanf(buf, "AC Power : %d", &ac);
2152 /* update status string */
2153 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2154 strncpy(pb_battery_info[PB_BATT_STATUS], "AC", sizeof(pb_battery_info[PB_BATT_STATUS]));
2155 } else if (ac && (flags & PMU_BATT_PRESENT)
2156 && !(flags & PMU_BATT_CHARGING)) {
2157 strncpy(pb_battery_info[PB_BATT_STATUS], "charged", sizeof(pb_battery_info[PB_BATT_STATUS]));
2158 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2159 strncpy(pb_battery_info[PB_BATT_STATUS], "charging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2161 strncpy(pb_battery_info[PB_BATT_STATUS], "discharging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2164 /* update percentage string */
2165 if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT)
2166 && !(flags & PMU_BATT_CHARGING)) {
2167 snprintf(pb_battery_info[PB_BATT_PERCENT],
2168 sizeof(pb_battery_info[PB_BATT_PERCENT]), "100%%");
2169 } else if (timeval == 0) {
2170 snprintf(pb_battery_info[PB_BATT_PERCENT],
2171 sizeof(pb_battery_info[PB_BATT_PERCENT]), "unknown");
2173 snprintf(pb_battery_info[PB_BATT_PERCENT],
2174 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2175 (charge * 100) / max_charge);
2178 /* update time string */
2179 if (timeval == 0) { /* fully charged or battery not present */
2180 snprintf(pb_battery_info[PB_BATT_TIME],
2181 sizeof(pb_battery_info[PB_BATT_TIME]), "unknown");
2182 } else if (timeval < 60 * 60) { /* don't show secs */
2183 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2184 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2186 format_seconds(pb_battery_info[PB_BATT_TIME],
2187 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2190 snprintf(buffer, n, "%s", pb_battery_info[i]);
2193 void update_top(void)
2195 process_find_top(info.cpu, info.memu, info.time
2200 info.first_process = get_first_process();
2203 #define ENTROPY_AVAIL_PATH "/proc/sys/kernel/random/entropy_avail"
2205 int get_entropy_avail(unsigned int *val)
2210 if (!(fp = open_file(ENTROPY_AVAIL_PATH, &rep)))
2213 if (fscanf(fp, "%u", val) != 1)
2220 #define ENTROPY_POOLSIZE_PATH "/proc/sys/kernel/random/poolsize"
2222 int get_entropy_poolsize(unsigned int *val)
2227 if (!(fp = open_file(ENTROPY_POOLSIZE_PATH, &rep)))
2230 if (fscanf(fp, "%u", val) != 1)
2237 const char *get_disk_protect_queue(const char *disk)
2243 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2244 if (access(path, F_OK)) {
2245 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2247 if ((fp = fopen(path, "r")) == NULL)
2249 if (fscanf(fp, "%d\n", &state) != 1) {
2254 return (state > 0) ? "frozen" : "free ";
2257 typedef struct DEV_LIST_TYPE
2261 struct DEV_LIST_TYPE *next;
2263 } DEV_LIST, *DEV_LIST_PTR;
2265 /* Same as sf #2942117 but memoized using a linked list */
2266 int is_disk(char *dev)
2268 char syspath[PATH_MAX];
2270 static DEV_LIST_PTR dev_head = NULL;
2271 DEV_LIST_PTR dev_cur, dev_last;
2276 if (strcmp(dev_cur->dev_name, dev) == 0)
2277 return dev_cur->memoized;
2279 dev_cur = dev_cur->next;
2282 dev_cur = (DEV_LIST_PTR)malloc(sizeof(DEV_LIST));
2283 dev_cur->dev_name = (char *)malloc((strlen(dev)+1)*sizeof(char));
2284 strcpy(dev_cur->dev_name,dev);
2285 dev_cur->next = NULL;
2287 while ((slash = strchr(dev, '/')))
2289 snprintf(syspath, sizeof(syspath), "/sys/block/%s", dev);
2290 dev_cur->memoized = !(access(syspath, F_OK));
2293 dev_last->next = dev_cur;
2297 return dev_cur->memoized;
2300 void update_diskio(void)
2304 char buf[512], devbuf[64];
2305 unsigned int major, minor;
2307 struct diskio_stat *cur;
2308 unsigned int reads, writes;
2309 unsigned int total_reads = 0, total_writes = 0;
2312 stats.current_read = 0;
2313 stats.current_write = 0;
2315 if (!(fp = open_file("/proc/diskstats", &rep))) {
2319 /* read reads and writes from all disks (minor = 0), including cd-roms
2320 * and floppies, and sum them up */
2321 while (fgets(buf, 512, fp)) {
2322 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
2323 &minor, devbuf, &reads, &writes);
2324 /* ignore subdevices (they have only 3 matching entries in their line)
2325 * and virtual devices (LVM, network block devices, RAM disks, Loopback)
2327 * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
2328 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
2329 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
2330 /* check needed for kernel >= 2.6.31, see sf #2942117 */
2331 if (is_disk(devbuf)) {
2332 total_reads += reads;
2333 total_writes += writes;
2336 col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
2337 &major, &minor, devbuf, &reads, &writes);
2338 if (col_count != 5) {
2343 while (cur && strcmp(devbuf, cur->dev))
2347 update_diskio_values(cur, reads, writes);
2349 update_diskio_values(&stats, total_reads, total_writes);