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 int 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);
132 int check_mount(char *s)
135 FILE *mtab = fopen("/etc/mtab", "r");
138 char buf1[256], buf2[128];
140 while (fgets(buf1, 256, mtab)) {
141 sscanf(buf1, "%*s %128s", buf2);
142 if (!strcmp(s, buf2)) {
149 NORM_ERR("Could not open mtab");
154 /* these things are also in sysinfo except Buffers:
155 * (that's why I'm reading them from proc) */
157 int update_meminfo(void)
162 /* unsigned int a; */
165 info.mem = info.memmax = info.swap = info.swapfree = info.swapmax = info.bufmem =
166 info.buffers = info.cached = info.memfree = info.memeasyfree = 0;
168 if (!(meminfo_fp = open_file("/proc/meminfo", &rep))) {
172 while (!feof(meminfo_fp)) {
173 if (fgets(buf, 255, meminfo_fp) == NULL) {
177 if (strncmp(buf, "MemTotal:", 9) == 0) {
178 sscanf(buf, "%*s %llu", &info.memmax);
179 } else if (strncmp(buf, "MemFree:", 8) == 0) {
180 sscanf(buf, "%*s %llu", &info.memfree);
181 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
182 sscanf(buf, "%*s %llu", &info.swapmax);
183 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
184 sscanf(buf, "%*s %llu", &info.swapfree);
185 } else if (strncmp(buf, "Buffers:", 8) == 0) {
186 sscanf(buf, "%*s %llu", &info.buffers);
187 } else if (strncmp(buf, "Cached:", 7) == 0) {
188 sscanf(buf, "%*s %llu", &info.cached);
192 info.mem = info.memmax - info.memfree;
193 info.memeasyfree = info.memfree;
194 info.swap = info.swapmax - info.swapfree;
196 info.bufmem = info.cached + info.buffers;
202 int get_laptop_mode(void)
207 if ((fp = fopen("/proc/sys/vm/laptop_mode", "r")) != NULL)
208 fscanf(fp, "%d\n", &val);
214 * # cat /sys/block/sda/queue/scheduler
215 * noop [anticipatory] cfq
217 char *get_ioscheduler(char *disk)
223 return strndup("n/a", text_buffer_size);
225 snprintf(buf, 127, "/sys/block/%s/queue/scheduler", disk);
226 if ((fp = fopen(buf, "r")) == NULL) {
227 return strndup("n/a", text_buffer_size);
230 fscanf(fp, "%127s", buf);
232 buf[strlen(buf) - 1] = '\0';
234 return strndup(buf + 1, text_buffer_size);
238 return strndup("n/a", text_buffer_size);
247 #define COND_FREE(x) if(x) free(x); x = 0
248 #define SAVE_SET_STRING(x, y) \
249 if (x && strcmp((char *)x, (char *)y)) { \
251 x = strndup("multiple", text_buffer_size); \
253 x = strndup(y, text_buffer_size); \
256 void update_gateway_info_failure(const char *reason)
261 //2 pointers to 1 location causes a crash when we try to free them both
262 gw_info.iface = strndup("failed", text_buffer_size);
263 gw_info.ip = strndup("failed", text_buffer_size);
267 /* Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT */
268 #define RT_ENTRY_FORMAT "%63s %lx %lx %x %*d %*d %*d %lx %*d %*d %*d\n"
270 int update_gateway_info(void)
275 unsigned long dest, gate, mask;
278 COND_FREE(gw_info.iface);
279 COND_FREE(gw_info.ip);
282 if ((fp = fopen("/proc/net/route", "r")) == NULL) {
283 update_gateway_info_failure("fopen()");
287 /* skip over the table header line, which is always present */
288 fscanf(fp, "%*[^\n]\n");
291 if(fscanf(fp, RT_ENTRY_FORMAT,
292 iface, &dest, &gate, &flags, &mask) != 5) {
293 update_gateway_info_failure("fscanf()");
296 if (!(dest || mask) && ((flags & RTF_GATEWAY) || !gate) ) {
298 SAVE_SET_STRING(gw_info.iface, iface)
300 SAVE_SET_STRING(gw_info.ip, inet_ntoa(ina))
307 void free_gateway_info(void)
313 memset(&gw_info, 0, sizeof(gw_info));
316 int gateway_exists(void)
318 return !!gw_info.count;
321 void print_gateway_iface(char *p, int p_max_size)
323 snprintf(p, p_max_size, "%s", gw_info.iface);
326 void print_gateway_ip(char *p, int p_max_size)
328 snprintf(p, p_max_size, "%s", gw_info.ip);
331 int update_net_stats(void)
335 static char first = 1;
337 // FIXME: arbitrary size chosen to keep code simple.
339 unsigned int curtmp1, curtmp2;
346 // wireless info variables
347 int skfd, has_bitrate = 0;
348 struct wireless_info *winfo;
353 delta = current_update_time - last_update_time;
354 if (delta <= 0.0001) {
358 /* open file and ignore first two lines */
359 if (!(net_dev_fp = open_file("/proc/net/dev", &rep))) {
364 fgets(buf, 255, net_dev_fp); /* garbage */
365 fgets(buf, 255, net_dev_fp); /* garbage (field names) */
367 /* read each interface */
368 for (i2 = 0; i2 < MAX_NET_INTERFACES; i2++) {
372 long long r, t, last_recv, last_trans;
374 if (fgets(buf, 255, net_dev_fp) == NULL) {
378 while (isspace((int) *p)) {
384 while (*p && *p != ':') {
393 ns = get_net_stat(s, NULL, NULL);
395 memset(&(ns->addr.sa_data), 0, 14);
397 memset(ns->addrs, 0, 17 * MAX_NET_INTERFACES + 1); /* Up to 17 chars per ip, max MAX_NET_INTERFACES interfaces. Nasty memory usage... */
399 last_recv = ns->recv;
400 last_trans = ns->trans;
402 /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
403 sscanf(p, "%lld %*d %*d %*d %*d %*d %*d %*d %lld",
406 /* if recv or trans is less than last time, an overflow happened */
407 if (r < ns->last_read_recv) {
410 ns->recv += (r - ns->last_read_recv);
412 ns->last_read_recv = r;
414 if (t < ns->last_read_trans) {
417 ns->trans += (t - ns->last_read_trans);
419 ns->last_read_trans = t;
421 /*** ip addr patch ***/
422 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
424 conf.ifc_buf = malloc(sizeof(struct ifreq) * MAX_NET_INTERFACES);
425 conf.ifc_len = sizeof(struct ifreq) * MAX_NET_INTERFACES;
426 memset(conf.ifc_buf, 0, conf.ifc_len);
428 ioctl((long) i, SIOCGIFCONF, &conf);
430 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
431 struct net_stat *ns2;
433 if (!(((struct ifreq *) conf.ifc_buf) + k))
437 ((struct ifreq *) conf.ifc_buf)[k].ifr_ifrn.ifrn_name, NULL, NULL);
438 ns2->addr = ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.ifru_addr;
439 sprintf(temp_addr, "%u.%u.%u.%u, ",
440 ns2->addr.sa_data[2] & 255,
441 ns2->addr.sa_data[3] & 255,
442 ns2->addr.sa_data[4] & 255,
443 ns2->addr.sa_data[5] & 255);
444 if(NULL == strstr(ns2->addrs, temp_addr))
445 strncpy(ns2->addrs + strlen(ns2->addrs), temp_addr, 17);
452 /*** end ip addr patch ***/
455 /* calculate speeds */
456 ns->net_rec[0] = (ns->recv - last_recv) / delta;
457 ns->net_trans[0] = (ns->trans - last_trans) / delta;
464 #pragma omp parallel for reduction(+:curtmp1, curtmp2) schedule(dynamic,10)
465 #endif /* HAVE_OPENMP */
466 for (i = 0; i < info.net_avg_samples; i++) {
467 curtmp1 = curtmp1 + ns->net_rec[i];
468 curtmp2 = curtmp2 + ns->net_trans[i];
470 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
471 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
472 if (info.net_avg_samples > 1) {
474 #pragma omp parallel for schedule(dynamic,10)
475 #endif /* HAVE_OPENMP */
476 for (i = info.net_avg_samples; i > 1; i--) {
477 ns->net_rec[i - 1] = ns->net_rec[i - 2];
478 ns->net_trans[i - 1] = ns->net_trans[i - 2];
483 /* update wireless info */
484 winfo = malloc(sizeof(struct wireless_info));
485 memset(winfo, 0, sizeof(struct wireless_info));
487 skfd = iw_sockets_open();
488 if (iw_get_basic_config(skfd, s, &(winfo->b)) > -1) {
490 // set present winfo variables
491 if (iw_get_stats(skfd, s, &(winfo->stats),
492 &winfo->range, winfo->has_range) >= 0) {
493 winfo->has_stats = 1;
495 if (iw_get_range_info(skfd, s, &(winfo->range)) >= 0) {
496 winfo->has_range = 1;
498 if (iw_get_ext(skfd, s, SIOCGIWAP, &wrq) >= 0) {
499 winfo->has_ap_addr = 1;
500 memcpy(&(winfo->ap_addr), &(wrq.u.ap_addr), sizeof(sockaddr));
504 if (iw_get_ext(skfd, s, SIOCGIWRATE, &wrq) >= 0) {
505 memcpy(&(winfo->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
506 iw_print_bitrate(ns->bitrate, 16, winfo->bitrate.value);
511 if (winfo->has_range && winfo->has_stats
512 && ((winfo->stats.qual.level != 0)
513 || (winfo->stats.qual.updated & IW_QUAL_DBM))) {
514 if (!(winfo->stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
515 ns->link_qual = winfo->stats.qual.qual;
516 ns->link_qual_max = winfo->range.max_qual.qual;
521 if (winfo->has_ap_addr) {
522 iw_sawap_ntop(&winfo->ap_addr, ns->ap);
526 if (winfo->b.has_essid) {
527 if (winfo->b.essid_on) {
528 snprintf(ns->essid, 32, "%s", winfo->b.essid);
530 snprintf(ns->essid, 32, "off/any");
534 snprintf(ns->mode, 16, "%s", iw_operation_mode[winfo->b.mode]);
536 iw_sockets_close(skfd);
548 int update_total_processes(void)
551 struct dirent *entry;
556 if (!(dir = opendir("/proc"))) {
559 while ((entry = readdir(dir))) {
561 /* Problem reading list of processes */
566 if (sscanf(entry->d_name, "%d%c", &ignore1, &ignore2) == 1) {
574 int update_threads(void)
578 struct sysinfo s_info;
581 info.threads = s_info.procs;
588 if (!(fp = open_file("/proc/loadavg", &rep))) {
592 fscanf(fp, "%*f %*f %*f %*d/%hu", &info.threads);
598 #define CPU_SAMPLE_COUNT 15
600 unsigned long long cpu_user;
601 unsigned long long cpu_system;
602 unsigned long long cpu_nice;
603 unsigned long long cpu_idle;
604 unsigned long long cpu_iowait;
605 unsigned long long cpu_irq;
606 unsigned long long cpu_softirq;
607 unsigned long long cpu_steal;
608 unsigned long long cpu_total;
609 unsigned long long cpu_active_total;
610 unsigned long long cpu_last_total;
611 unsigned long long cpu_last_active_total;
612 double cpu_val[CPU_SAMPLE_COUNT];
614 static short cpu_setup = 0;
616 /* Determine if this kernel gives us "extended" statistics information in
618 * Kernels around 2.5 and earlier only reported user, system, nice, and
619 * idle values in proc stat.
620 * Kernels around 2.6 and greater report these PLUS iowait, irq, softirq,
622 void determine_longstat(char *buf)
624 unsigned long long iowait = 0;
626 KFLAG_SETOFF(KFLAG_IS_LONGSTAT);
627 /* scanf will either return -1 or 1 because there is only 1 assignment */
628 if (sscanf(buf, "%*s %*d %*d %*d %*d %llu", &iowait) > 0) {
629 KFLAG_SETON(KFLAG_IS_LONGSTAT);
633 void get_cpu_count(void)
639 if (info.cpu_usage) {
643 if (!(stat_fp = open_file("/proc/stat", &rep))) {
649 while (!feof(stat_fp)) {
650 if (fgets(buf, 255, stat_fp) == NULL) {
654 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
655 if (info.cpu_count == 0) {
656 determine_longstat(buf);
661 info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
666 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
667 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
669 int update_stat(void)
673 static struct cpu_info *cpu = NULL;
678 const char *stat_template = NULL;
679 unsigned int malloc_cpu_size = 0;
680 extern void* global_cpu;
682 static pthread_mutex_t last_stat_update_mutex = PTHREAD_MUTEX_INITIALIZER;
683 static double last_stat_update = 0.0;
685 /* since we use wrappers for this function, the update machinery
686 * can't eliminate double invocations of this function. Check for
687 * them here, otherwise cpu_usage counters are freaking out. */
688 pthread_mutex_lock(&last_stat_update_mutex);
689 if (last_stat_update == current_update_time) {
690 pthread_mutex_unlock(&last_stat_update_mutex);
693 last_stat_update = current_update_time;
694 pthread_mutex_unlock(&last_stat_update_mutex);
696 /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
697 if (!cpu_setup || !info.cpu_usage) {
702 if (!stat_template) {
704 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
708 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
709 cpu = malloc(malloc_cpu_size);
710 memset(cpu, 0, malloc_cpu_size);
714 if (!(stat_fp = open_file("/proc/stat", &rep))) {
715 info.run_threads = 0;
716 if (info.cpu_usage) {
717 memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
723 while (!feof(stat_fp)) {
724 if (fgets(buf, 255, stat_fp) == NULL) {
728 if (strncmp(buf, "procs_running ", 14) == 0) {
729 sscanf(buf, "%*s %hu", &info.run_threads);
730 } else if (strncmp(buf, "cpu", 3) == 0) {
732 if (isdigit(buf[3])) {
733 idx = atoi(&buf[3]) + 1;
737 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
738 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
739 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
740 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
741 &(cpu[idx].cpu_steal));
743 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
744 cpu[idx].cpu_system + cpu[idx].cpu_idle +
745 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
746 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
748 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
749 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
751 delta = current_update_time - last_update_time;
753 if (delta <= 0.001) {
757 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
758 cpu[idx].cpu_last_active_total) /
759 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
762 #pragma omp parallel for reduction(+:curtmp) schedule(dynamic,10)
763 #endif /* HAVE_OPENMP */
764 for (i = 0; i < info.cpu_avg_samples; i++) {
765 curtmp = curtmp + cpu[idx].cpu_val[i];
767 /* TESTING -- I've removed this, because I don't think it is right.
768 * You shouldn't divide by the cpu count here ...
769 * removing for testing */
771 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
774 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
776 /* TESTING -- this line replaces the prev. "suspect" if/else */
777 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
779 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
780 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
782 #pragma omp parallel for schedule(dynamic,10)
783 #endif /* HAVE_OPENMP */
784 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
785 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
793 int update_running_processes(void)
799 int update_cpu_usage(void)
805 int update_load_average(void)
807 #ifdef HAVE_GETLOADAVG
812 info.loadavg[0] = (float) v[0];
813 info.loadavg[1] = (float) v[1];
814 info.loadavg[2] = (float) v[2];
821 if (!(fp = open_file("/proc/loadavg", &rep))) {
822 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
825 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
832 /***********************************************************/
833 /***********************************************************/
834 /***********************************************************/
836 static int no_dots(const struct dirent *d)
838 if (d->d_name[0] == '.') {
844 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
846 struct dirent **namelist;
849 n = scandir(dir, &namelist, no_dots, alphasort);
852 NORM_ERR("scandir for %s: %s", dir, strerror(errno));
863 strncpy(s, namelist[0]->d_name, 255);
867 #pragma omp parallel for schedule(dynamic,10)
868 #endif /* HAVE_OPENMP */
869 for (i = 0; i < n; i++) {
878 static int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
879 int *divisor, char *devtype)
886 memset(buf, 0, sizeof(buf));
888 /* if device is NULL or *, get first */
889 if (dev == NULL || strcmp(dev, "*") == 0) {
892 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
898 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
900 /* buf holds result from get_first_file_in_a_directory() above,
901 * e.g. "hwmon0" -- append "/device" */
902 strcat(buf, "/device");
904 /* dev holds device number N as a string,
905 * e.g. "0", -- convert to "hwmon0/device" */
906 sprintf(buf, "hwmon%s/device", dev);
911 /* change vol to in, tempf to temp */
912 if (strcmp(type, "vol") == 0) {
914 } else if (strcmp(type, "tempf") == 0) {
919 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
921 /* first, attempt to open file in /device */
922 fd = open(path, O_RDONLY);
925 /* if it fails, strip the /device from dev and attempt again */
926 buf[strlen(buf) - 7] = 0;
927 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
928 fd = open(path, O_RDONLY);
930 CRIT_ERR(NULL, NULL, "can't open '%s': %s\nplease check your device or remove this "
931 "var from "PACKAGE_NAME, path, strerror(errno));
935 strncpy(devtype, path, 255);
937 if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
938 || strcmp(type, "tempf") == 0) {
943 /* fan does not use *_div as a read divisor */
944 if (strcmp("fan", type) == 0) {
948 /* test if *_div file exist, open it and use it as divisor */
949 if (strcmp(type, "tempf") == 0) {
950 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
952 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
955 divfd = open(path, O_RDONLY);
961 divn = read(divfd, divbuf, 63);
962 /* should read until n == 0 but I doubt that kernel will give these
963 * in multiple pieces. :) */
965 NORM_ERR("open_sysfs_sensor(): can't read from sysfs");
968 *divisor = atoi(divbuf);
976 static double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
984 lseek(*fd, 0, SEEK_SET);
990 n = read(*fd, buf, 63);
991 /* should read until n == 0 but I doubt that kernel will give these
992 * in multiple pieces. :) */
994 NORM_ERR("get_sysfs_info(): read from %s failed\n", devtype);
1003 *fd = open(devtype, O_RDONLY);
1005 NORM_ERR("can't open '%s': %s", devtype, strerror(errno));
1008 /* My dirty hack for computing CPU value
1009 * Filedil, from forums.gentoo.org */
1010 /* if (strstr(devtype, "temp1_input") != NULL) {
1011 return -15.096 + 1.4893 * (val / 1000.0);
1014 /* divide voltage and temperature by 1000 */
1015 /* or if any other divisor is given, use that */
1016 if (strcmp(type, "tempf") == 0) {
1018 return ((val / divisor + 40) * 9.0 / 5) - 40;
1019 } else if (divisor) {
1020 return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
1022 return ((val + 40) * 9.0 / 5) - 40;
1026 return val / divisor;
1027 } else if (divisor) {
1028 return val / 1000.0;
1035 #define HWMON_RESET() {\
1040 static void parse_sysfs_sensor(struct text_object *obj, const char *arg, const char *path, const char *type)
1042 char buf1[64], buf2[64];
1043 float factor, offset;
1047 if (sscanf(arg, "%63s %d %f %f", buf2, &n, &factor, &offset) == 4) found = 1; else HWMON_RESET();
1048 if (!found && sscanf(arg, "%63s %63s %d %f %f", buf1, buf2, &n, &factor, &offset) == 5) found = 1; else if (!found) HWMON_RESET();
1049 if (!found && sscanf(arg, "%63s %63s %d", buf1, buf2, &n) == 3) found = 1; else if (!found) HWMON_RESET();
1050 if (!found && sscanf(arg, "%63s %d", buf2, &n) == 2) found = 1; else if (!found) HWMON_RESET();
1053 NORM_ERR("i2c failed to parse arguments");
1054 obj->type = OBJ_text;
1057 DBGP("parsed %s args: '%s' '%s' %d %f %f\n", type, buf1, buf2, n, factor, offset);
1058 sf = malloc(sizeof(struct sysfs));
1059 memset(sf, 0, sizeof(struct sysfs));
1060 sf->fd = open_sysfs_sensor(path, (*buf1) ? buf1 : 0, buf2, n,
1061 &sf->arg, sf->devtype);
1062 strncpy(sf->type, buf2, 63);
1063 sf->factor = factor;
1064 sf->offset = offset;
1065 obj->data.opaque = sf;
1068 #define PARSER_GENERATOR(name, path) \
1069 void parse_##name##_sensor(struct text_object *obj, const char *arg) \
1071 parse_sysfs_sensor(obj, arg, path, #name); \
1074 PARSER_GENERATOR(i2c, "/sys/bus/i2c/devices/")
1075 PARSER_GENERATOR(hwmon, "/sys/class/hwmon/")
1076 PARSER_GENERATOR(platform, "/sys/bus/platform/devices/")
1078 void print_sysfs_sensor(struct text_object *obj, char *p, int p_max_size)
1081 struct sysfs *sf = obj->data.opaque;
1086 r = get_sysfs_info(&sf->fd, sf->arg,
1087 sf->devtype, sf->type);
1089 r = r * sf->factor + sf->offset;
1091 if (!strncmp(sf->type, "temp", 4)) {
1092 temp_print(p, p_max_size, r, TEMP_CELSIUS);
1093 } else if (r >= 100.0 || r == 0) {
1094 snprintf(p, p_max_size, "%d", (int) r);
1096 snprintf(p, p_max_size, "%.1f", r);
1100 void free_sysfs_sensor(struct text_object *obj)
1102 struct sysfs *sf = obj->data.opaque;
1108 free(obj->data.opaque);
1109 obj->data.opaque = NULL;
1112 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1113 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1115 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1116 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1117 const char *p_format, int divisor, unsigned int cpu)
1125 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1131 char current_freq_file[128];
1133 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1135 f = fopen(current_freq_file, "r");
1137 /* if there's a cpufreq /sys node, read the current frequency from
1138 * this node and divide by 1000 to get Mhz. */
1139 if (fgets(s, sizeof(s), f)) {
1140 s[strlen(s) - 1] = '\0';
1141 freq = strtod(s, NULL);
1144 snprintf(p_client_buffer, client_buffer_size, p_format,
1145 (freq / 1000) / divisor);
1150 // open the CPU information file
1151 f = open_file("/proc/cpuinfo", &rep);
1153 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1158 while (fgets(s, sizeof(s), f) != NULL) {
1160 #if defined(__i386) || defined(__x86_64)
1161 // and search for the cpu mhz
1162 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1164 #if defined(__alpha)
1165 // different on alpha
1166 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1168 // this is different on ppc for some reason
1169 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1170 #endif // defined(__alpha)
1171 #endif // defined(__i386) || defined(__x86_64)
1173 // copy just the number
1174 strcpy(frequency, strchr(s, ':') + 2);
1175 #if defined(__alpha)
1177 frequency[strlen(frequency) - 6] = '\0';
1178 // kernel reports in Hz
1179 freq = strtod(frequency, NULL) / 1000000;
1182 frequency[strlen(frequency) - 1] = '\0';
1183 freq = strtod(frequency, NULL);
1187 if (strncmp(s, "processor", 9) == 0) {
1194 snprintf(p_client_buffer, client_buffer_size, p_format,
1195 (float) freq / divisor);
1199 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1201 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1211 * Peter Tarjan (ptarjan@citromail.hu) */
1213 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1214 static char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1215 const char *p_format, int divisor, unsigned int cpu)
1221 char current_freq_file[128];
1224 /* build the voltage file name */
1226 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1229 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1234 /* read the current cpu frequency from the /sys node */
1235 f = fopen(current_freq_file, "r");
1237 if (fgets(s, sizeof(s), f)) {
1238 s[strlen(s) - 1] = '\0';
1239 freq = strtod(s, NULL);
1243 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1244 perror("get_voltage()");
1251 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1254 /* use the current cpu frequency to find the corresponding voltage */
1255 f = fopen(current_freq_file, "r");
1261 if (fgets(line, 255, f) == NULL) {
1264 sscanf(line, "%d %d", &freq_comp, &voltage);
1265 if (freq_comp == freq) {
1271 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1272 perror("get_voltage()");
1278 snprintf(p_client_buffer, client_buffer_size, p_format,
1279 (float) voltage / divisor);
1283 void print_voltage_mv(struct text_object *obj, char *p, int p_max_size)
1287 ok = get_voltage(p, p_max_size, "%.0f", 1, obj->data.i);
1291 void print_voltage_v(struct text_object *obj, char *p, int p_max_size)
1295 ok = get_voltage(p, p_max_size, "%'.3f", 1000, obj->data.i);
1299 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1301 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1308 if (!p_client_buffer || client_buffer_size <= 0) {
1312 /* yeah, slow... :/ */
1313 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1314 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1318 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1320 fp = open_file(buf2, &rep);
1322 snprintf(p_client_buffer, client_buffer_size,
1323 "can't open fan's state file");
1326 memset(buf, 0, sizeof(buf));
1327 fscanf(fp, "%*s %99s", buf);
1330 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1333 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply"
1334 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1335 /* Linux 2.6.25 onwards ac adapter info is in
1336 /sys/class/power_supply/AC/
1337 On my system I get the following.
1338 /sys/class/power_supply/AC/uevent:
1339 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1342 POWER_SUPPLY_NAME=AC
1343 POWER_SUPPLY_TYPE=Mains
1344 POWER_SUPPLY_ONLINE=1
1346 Update: it seems the folder name is hardware-dependent. We add an aditional adapter
1347 argument, specifying the folder name.
1349 Update: on some systems it's /sys/class/power_supply/ADP1 instead of /sys/class/power_supply/AC
1352 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size, const char *adapter)
1361 if (!p_client_buffer || client_buffer_size <= 0) {
1366 snprintf(buf2, sizeof(buf2), "%s/%s/uevent", SYSFS_AC_ADAPTER_DIR, adapter);
1368 snprintf(buf2, sizeof(buf2), "%s/AC/uevent", SYSFS_AC_ADAPTER_DIR);
1369 if(stat(buf2, &sb) == -1) snprintf(buf2, sizeof(buf2), "%s/ADP1/uevent", SYSFS_AC_ADAPTER_DIR);
1371 if(stat(buf2, &sb) == 0) fp = open_file(buf2, &rep); else fp = 0;
1373 /* sysfs processing */
1375 if (fgets(buf, sizeof(buf), fp) == NULL)
1378 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1380 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1381 snprintf(p_client_buffer, client_buffer_size,
1382 "%s-line", (online ? "on" : "off"));
1388 /* yeah, slow... :/ */
1389 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1390 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1394 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1396 fp = open_file(buf2, &rep);
1398 snprintf(p_client_buffer, client_buffer_size,
1399 "No ac adapter found.... where is it?");
1402 memset(buf, 0, sizeof(buf));
1403 fscanf(fp, "%*s %99s", buf);
1406 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1411 /proc/acpi/thermal_zone/THRM/cooling_mode
1412 cooling mode: active
1413 /proc/acpi/thermal_zone/THRM/polling_frequency
1415 /proc/acpi/thermal_zone/THRM/state
1417 /proc/acpi/thermal_zone/THRM/temperature
1419 /proc/acpi/thermal_zone/THRM/trip_points
1421 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1424 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1425 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1427 int open_acpi_temperature(const char *name)
1433 if (name == NULL || strcmp(name, "*") == 0) {
1436 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1442 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1444 fd = open(path, O_RDONLY);
1446 NORM_ERR("can't open '%s': %s", path, strerror(errno));
1452 static double last_acpi_temp;
1453 static double last_acpi_temp_time;
1455 double get_acpi_temperature(int fd)
1461 /* don't update acpi temperature too often */
1462 if (current_update_time - last_acpi_temp_time < 11.32) {
1463 return last_acpi_temp;
1465 last_acpi_temp_time = current_update_time;
1467 /* seek to beginning */
1468 lseek(fd, 0, SEEK_SET);
1475 n = read(fd, buf, 255);
1477 NORM_ERR("can't read fd %d: %s", fd, strerror(errno));
1480 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1484 return last_acpi_temp;
1488 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1490 design capacity: 4400 mAh
1491 last full capacity: 4064 mAh
1492 battery technology: rechargeable
1493 design voltage: 14800 mV
1494 design capacity warning: 300 mAh
1495 design capacity low: 200 mAh
1496 capacity granularity 1: 32 mAh
1497 capacity granularity 2: 32 mAh
1499 serial number: 16922
1505 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1508 charging state: unknown
1510 remaining capacity: 4064 mAh
1511 present voltage: 16608 mV
1515 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1516 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1517 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1518 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1519 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1521 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1522 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1524 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1525 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1528 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1529 Linux 2.6.24 onwards battery info is in
1530 /sys/class/power_supply/BAT0/
1531 On my system I get the following.
1532 /sys/class/power_supply/BAT0/uevent:
1533 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1535 PHYSDEVDRIVER=battery
1536 POWER_SUPPLY_NAME=BAT0
1537 POWER_SUPPLY_TYPE=Battery
1538 POWER_SUPPLY_STATUS=Discharging
1539 POWER_SUPPLY_PRESENT=1
1540 POWER_SUPPLY_TECHNOLOGY=Li-ion
1541 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1542 POWER_SUPPLY_VOLTAGE_NOW=10780000
1543 POWER_SUPPLY_CURRENT_NOW=13970000
1544 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1545 POWER_SUPPLY_ENERGY_FULL=27370000
1546 POWER_SUPPLY_ENERGY_NOW=11810000
1547 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1548 POWER_SUPPLY_MANUFACTURER=Panasonic
1549 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1552 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1553 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1554 #define APM_PATH "/proc/apm"
1555 #define MAX_BATTERY_COUNT 4
1557 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1558 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1559 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1561 static int batteries_initialized = 0;
1562 static char batteries[MAX_BATTERY_COUNT][32];
1564 static int acpi_last_full[MAX_BATTERY_COUNT];
1565 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1567 /* e.g. "charging 75%" */
1568 static char last_battery_str[MAX_BATTERY_COUNT][64];
1570 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1572 static double last_battery_time[MAX_BATTERY_COUNT];
1574 static int last_battery_perct[MAX_BATTERY_COUNT];
1575 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1577 void init_batteries(void)
1581 if (batteries_initialized) {
1585 #pragma omp parallel for schedule(dynamic,10)
1586 #endif /* HAVE_OPENMP */
1587 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1588 batteries[idx][0] = '\0';
1590 batteries_initialized = 1;
1593 int get_battery_idx(const char *bat)
1597 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1598 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1603 /* if not found, enter a new entry */
1604 if (!strlen(batteries[idx])) {
1605 snprintf(batteries[idx], 31, "%s", bat);
1611 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1613 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1615 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1616 char acpi_path[128];
1617 char sysfs_path[128];
1619 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1620 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1624 idx = get_battery_idx(bat);
1626 /* don't update battery too often */
1627 if (current_update_time - last_battery_time[idx] < 29.5) {
1628 set_return_value(buffer, n, item, idx);
1632 last_battery_time[idx] = current_update_time;
1634 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1635 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1637 /* first try SYSFS if that fails try ACPI */
1639 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1640 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1643 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1644 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1647 if (sysfs_bat_fp[idx] != NULL) {
1649 int present_rate = -1;
1650 int remaining_capacity = -1;
1651 char charging_state[64];
1654 strcpy(charging_state, "unknown");
1656 while (!feof(sysfs_bat_fp[idx])) {
1658 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1661 /* let's just hope units are ok */
1662 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1663 strcpy(present, "yes");
1664 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1665 strcpy(present, "no");
1666 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1667 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1668 /* present_rate is not the same as the
1669 current flowing now but it is the same value
1670 which was used in the past. so we continue
1672 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1673 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1674 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1675 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1676 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1677 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1678 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1679 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1680 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1681 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1684 fclose(sysfs_bat_fp[idx]);
1685 sysfs_bat_fp[idx] = NULL;
1687 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1688 if (remaining_capacity > acpi_last_full[idx])
1689 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1692 if (strcmp(present, "No") == 0) {
1693 strncpy(last_battery_str[idx], "not present", 64);
1696 else if (strcmp(charging_state, "Charging") == 0) {
1697 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1698 /* e.g. charging 75% */
1699 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1700 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1702 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1703 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1704 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1705 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1706 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1707 snprintf(last_battery_time_str[idx],
1708 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1710 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1711 snprintf(last_battery_time_str[idx],
1712 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1716 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1717 if (present_rate > 0) {
1718 /* e.g. discharging 35% */
1719 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1720 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1722 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1723 (long) (((float) remaining_capacity / present_rate) * 3600));
1724 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1725 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1726 snprintf(last_battery_time_str[idx],
1727 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1729 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1731 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1732 snprintf(last_battery_time_str[idx],
1733 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1737 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1738 else if (strncmp(charging_state, "Charged", 64) == 0 || strncmp(charging_state, "Full", 64) == 0) {
1739 /* Below happens with the second battery on my X40,
1740 * when the second one is empty and the first one
1742 if (remaining_capacity == 0)
1743 strcpy(last_battery_str[idx], "empty");
1745 strcpy(last_battery_str[idx], "charged");
1747 /* unknown, probably full / AC */
1749 if (acpi_last_full[idx] != 0
1750 && remaining_capacity != acpi_last_full[idx])
1751 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1752 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1754 strncpy(last_battery_str[idx], "AC", 64);
1756 } else if (acpi_bat_fp[idx] != NULL) {
1758 int present_rate = -1;
1759 int remaining_capacity = -1;
1760 char charging_state[64];
1763 /* read last full capacity if it's zero */
1764 if (acpi_last_full[idx] == 0) {
1765 static int rep3 = 0;
1769 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1770 fp = open_file(path, &rep3);
1775 if (fgets(b, 256, fp) == NULL) {
1778 if (sscanf(b, "last full capacity: %d",
1779 &acpi_last_full[idx]) != 0) {
1788 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1790 strcpy(charging_state, "unknown");
1792 while (!feof(acpi_bat_fp[idx])) {
1795 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1799 /* let's just hope units are ok */
1800 if (strncmp(buf, "present:", 8) == 0) {
1801 sscanf(buf, "present: %4s", present);
1802 } else if (strncmp(buf, "charging state:", 15) == 0) {
1803 sscanf(buf, "charging state: %63s", charging_state);
1804 } else if (strncmp(buf, "present rate:", 13) == 0) {
1805 sscanf(buf, "present rate: %d", &present_rate);
1806 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1807 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1810 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1811 if (remaining_capacity > acpi_last_full[idx]) {
1812 /* normalize to 100% */
1813 acpi_last_full[idx] = remaining_capacity;
1817 if (strcmp(present, "no") == 0) {
1818 strncpy(last_battery_str[idx], "not present", 64);
1820 } else if (strcmp(charging_state, "charging") == 0) {
1821 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1822 /* e.g. charging 75% */
1823 snprintf(last_battery_str[idx],
1824 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1825 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1827 format_seconds(last_battery_time_str[idx],
1828 sizeof(last_battery_time_str[idx]) - 1,
1829 (long) (((acpi_last_full[idx] - remaining_capacity) *
1830 3600) / present_rate));
1831 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1832 snprintf(last_battery_str[idx],
1833 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1834 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1835 snprintf(last_battery_time_str[idx],
1836 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1838 strncpy(last_battery_str[idx], "charging",
1839 sizeof(last_battery_str[idx]) - 1);
1840 snprintf(last_battery_time_str[idx],
1841 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1844 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1845 if (present_rate > 0) {
1846 /* e.g. discharging 35% */
1847 snprintf(last_battery_str[idx],
1848 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1849 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1851 format_seconds(last_battery_time_str[idx],
1852 sizeof(last_battery_time_str[idx]) - 1,
1853 (long) ((remaining_capacity * 3600) / present_rate));
1854 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1855 snprintf(last_battery_str[idx],
1856 sizeof(last_battery_str[idx]) - 1, "full");
1857 snprintf(last_battery_time_str[idx],
1858 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1860 snprintf(last_battery_str[idx],
1861 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1862 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1863 snprintf(last_battery_time_str[idx],
1864 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1867 } else if (strncmp(charging_state, "charged", 64) == 0) {
1868 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1869 /* Below happens with the second battery on my X40,
1870 * when the second one is empty and the first one being charged. */
1871 if (remaining_capacity == 0) {
1872 strcpy(last_battery_str[idx], "empty");
1874 strcpy(last_battery_str[idx], "charged");
1876 /* unknown, probably full / AC */
1878 if (strncmp(charging_state, "Full", 64) == 0) {
1879 strncpy(last_battery_str[idx], "full", 64);
1880 } else if (acpi_last_full[idx] != 0
1881 && remaining_capacity != acpi_last_full[idx]) {
1882 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1883 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1885 strncpy(last_battery_str[idx], "AC", 64);
1888 fclose(acpi_bat_fp[idx]);
1889 acpi_bat_fp[idx] = NULL;
1892 if (apm_bat_fp[idx] == NULL) {
1893 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1896 if (apm_bat_fp[idx] != NULL) {
1897 unsigned int ac, status, flag;
1900 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1901 &ac, &status, &flag, &life);
1904 /* could check now that there is ac */
1905 snprintf(last_battery_str[idx], 64, "AC");
1907 /* could check that status == 3 here? */
1908 } else if (ac && life != 100) {
1909 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1911 snprintf(last_battery_str[idx], 64, "%d%%", life);
1914 /* it seemed to buffer it so file must be closed (or could use
1915 * syscalls directly but I don't feel like coding it now) */
1916 fclose(apm_bat_fp[idx]);
1917 apm_bat_fp[idx] = NULL;
1920 set_return_value(buffer, n, item, idx);
1923 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1926 case BATTERY_STATUS:
1927 snprintf(buffer, n, "%s", last_battery_str[idx]);
1930 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1937 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1939 get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1940 if (0 == strncmp("charging", buffer, 8)) {
1942 memmove(buffer + 1, buffer + 8, n - 8);
1943 } else if (0 == strncmp("discharging", buffer, 11)) {
1945 memmove(buffer + 1, buffer + 11, n - 11);
1946 } else if (0 == strncmp("charged", buffer, 7)) {
1948 memmove(buffer + 1, buffer + 7, n - 7);
1949 } else if (0 == strncmp("not present", buffer, 11)) {
1951 memmove(buffer + 1, buffer + 11, n - 11);
1952 } else if (0 == strncmp("empty", buffer, 5)) {
1954 memmove(buffer + 1, buffer + 5, n - 5);
1955 } else if (0 != strncmp("AC", buffer, 2)) {
1957 memmove(buffer + 1, buffer + 11, n - 11);
1961 int get_battery_perct(const char *bat)
1965 char acpi_path[128];
1966 char sysfs_path[128];
1967 int remaining_capacity = -1;
1969 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1970 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1974 idx = get_battery_idx(bat);
1976 /* don't update battery too often */
1977 if (current_update_time - last_battery_perct_time[idx] < 30) {
1978 return last_battery_perct[idx];
1980 last_battery_perct_time[idx] = current_update_time;
1982 /* Only check for SYSFS or ACPI */
1984 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1985 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1989 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1990 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1993 if (sysfs_bat_fp[idx] != NULL) {
1995 while (!feof(sysfs_bat_fp[idx])) {
1997 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
2000 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
2001 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
2002 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
2003 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
2004 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
2005 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
2006 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
2007 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
2011 fclose(sysfs_bat_fp[idx]);
2012 sysfs_bat_fp[idx] = NULL;
2014 } else if (acpi_bat_fp[idx] != NULL) {
2016 /* read last full capacity if it's zero */
2017 if (acpi_design_capacity[idx] == 0) {
2022 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
2023 fp = open_file(path, &rep2);
2028 if (fgets(b, 256, fp) == NULL) {
2031 if (sscanf(b, "last full capacity: %d",
2032 &acpi_design_capacity[idx]) != 0) {
2040 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
2042 while (!feof(acpi_bat_fp[idx])) {
2045 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
2049 if (buf[0] == 'r') {
2050 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
2054 if (remaining_capacity < 0) {
2057 /* compute the battery percentage */
2058 last_battery_perct[idx] =
2059 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
2060 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
2061 return last_battery_perct[idx];
2064 int get_battery_perct_bar(const char *bar)
2068 get_battery_perct(bar);
2069 idx = get_battery_idx(bar);
2070 return (int) (last_battery_perct[idx] * 2.56 - 1);
2073 /* On Apple powerbook and ibook:
2074 $ cat /proc/pmu/battery_0
2081 $ cat /proc/pmu/info
2082 PMU driver version : 2
2083 PMU firmware version : 0c
2088 /* defines as in <linux/pmu.h> */
2089 #define PMU_BATT_PRESENT 0x00000001
2090 #define PMU_BATT_CHARGING 0x00000002
2092 static FILE *pmu_battery_fp;
2093 static FILE *pmu_info_fp;
2094 static char pb_battery_info[3][32];
2095 static double pb_battery_info_update;
2097 #define PMU_PATH "/proc/pmu"
2098 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2101 const char *batt_path = PMU_PATH "/battery_0";
2102 const char *info_path = PMU_PATH "/info";
2104 int charge, max_charge, ac = -1;
2107 /* don't update battery too often */
2108 if (current_update_time - pb_battery_info_update < 29.5) {
2109 snprintf(buffer, n, "%s", pb_battery_info[i]);
2112 pb_battery_info_update = current_update_time;
2114 if (pmu_battery_fp == NULL) {
2115 pmu_battery_fp = open_file(batt_path, &rep);
2116 if (pmu_battery_fp == NULL) {
2121 if (pmu_battery_fp != NULL) {
2122 rewind(pmu_battery_fp);
2123 while (!feof(pmu_battery_fp)) {
2126 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2130 if (buf[0] == 'f') {
2131 sscanf(buf, "flags : %8x", &flags);
2132 } else if (buf[0] == 'c' && buf[1] == 'h') {
2133 sscanf(buf, "charge : %d", &charge);
2134 } else if (buf[0] == 'm') {
2135 sscanf(buf, "max_charge : %d", &max_charge);
2136 } else if (buf[0] == 't') {
2137 sscanf(buf, "time rem. : %ld", &timeval);
2141 if (pmu_info_fp == NULL) {
2142 pmu_info_fp = open_file(info_path, &rep);
2143 if (pmu_info_fp == NULL) {
2148 if (pmu_info_fp != NULL) {
2149 rewind(pmu_info_fp);
2150 while (!feof(pmu_info_fp)) {
2153 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2156 if (buf[0] == 'A') {
2157 sscanf(buf, "AC Power : %d", &ac);
2161 /* update status string */
2162 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2163 strncpy(pb_battery_info[PB_BATT_STATUS], "AC", sizeof(pb_battery_info[PB_BATT_STATUS]));
2164 } else if (ac && (flags & PMU_BATT_PRESENT)
2165 && !(flags & PMU_BATT_CHARGING)) {
2166 strncpy(pb_battery_info[PB_BATT_STATUS], "charged", sizeof(pb_battery_info[PB_BATT_STATUS]));
2167 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2168 strncpy(pb_battery_info[PB_BATT_STATUS], "charging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2170 strncpy(pb_battery_info[PB_BATT_STATUS], "discharging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2173 /* update percentage string */
2174 if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT)
2175 && !(flags & PMU_BATT_CHARGING)) {
2176 snprintf(pb_battery_info[PB_BATT_PERCENT],
2177 sizeof(pb_battery_info[PB_BATT_PERCENT]), "100%%");
2178 } else if (timeval == 0) {
2179 snprintf(pb_battery_info[PB_BATT_PERCENT],
2180 sizeof(pb_battery_info[PB_BATT_PERCENT]), "unknown");
2182 snprintf(pb_battery_info[PB_BATT_PERCENT],
2183 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2184 (charge * 100) / max_charge);
2187 /* update time string */
2188 if (timeval == 0) { /* fully charged or battery not present */
2189 snprintf(pb_battery_info[PB_BATT_TIME],
2190 sizeof(pb_battery_info[PB_BATT_TIME]), "unknown");
2191 } else if (timeval < 60 * 60) { /* don't show secs */
2192 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2193 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2195 format_seconds(pb_battery_info[PB_BATT_TIME],
2196 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2199 snprintf(buffer, n, "%s", pb_battery_info[i]);
2202 int update_top(void)
2204 process_find_top(info.cpu, info.memu, info.time
2209 info.first_process = get_first_process();
2213 #define ENTROPY_AVAIL_PATH "/proc/sys/kernel/random/entropy_avail"
2215 int get_entropy_avail(unsigned int *val)
2220 if (!(fp = open_file(ENTROPY_AVAIL_PATH, &rep)))
2223 if (fscanf(fp, "%u", val) != 1)
2230 #define ENTROPY_POOLSIZE_PATH "/proc/sys/kernel/random/poolsize"
2232 int get_entropy_poolsize(unsigned int *val)
2237 if (!(fp = open_file(ENTROPY_POOLSIZE_PATH, &rep)))
2240 if (fscanf(fp, "%u", val) != 1)
2247 const char *get_disk_protect_queue(const char *disk)
2253 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2254 if (access(path, F_OK)) {
2255 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2257 if ((fp = fopen(path, "r")) == NULL)
2259 if (fscanf(fp, "%d\n", &state) != 1) {
2264 return (state > 0) ? "frozen" : "free ";
2267 typedef struct DEV_LIST_TYPE
2271 struct DEV_LIST_TYPE *next;
2273 } DEV_LIST, *DEV_LIST_PTR;
2275 /* Same as sf #2942117 but memoized using a linked list */
2276 int is_disk(char *dev)
2278 char syspath[PATH_MAX];
2280 static DEV_LIST_PTR dev_head = NULL;
2281 DEV_LIST_PTR dev_cur, dev_last;
2286 if (strcmp(dev_cur->dev_name, dev) == 0)
2287 return dev_cur->memoized;
2289 dev_cur = dev_cur->next;
2292 dev_cur = (DEV_LIST_PTR)malloc(sizeof(DEV_LIST));
2293 dev_cur->dev_name = (char *)malloc((strlen(dev)+1)*sizeof(char));
2294 strcpy(dev_cur->dev_name,dev);
2295 dev_cur->next = NULL;
2297 while ((slash = strchr(dev, '/')))
2299 snprintf(syspath, sizeof(syspath), "/sys/block/%s", dev);
2300 dev_cur->memoized = !(access(syspath, F_OK));
2303 dev_last->next = dev_cur;
2307 return dev_cur->memoized;
2310 int update_diskio(void)
2314 char buf[512], devbuf[64];
2315 unsigned int major, minor;
2317 struct diskio_stat *cur;
2318 unsigned int reads, writes;
2319 unsigned int total_reads = 0, total_writes = 0;
2322 stats.current_read = 0;
2323 stats.current_write = 0;
2325 if (!(fp = open_file("/proc/diskstats", &rep))) {
2329 /* read reads and writes from all disks (minor = 0), including cd-roms
2330 * and floppies, and sum them up */
2331 while (fgets(buf, 512, fp)) {
2332 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
2333 &minor, devbuf, &reads, &writes);
2334 /* ignore subdevices (they have only 3 matching entries in their line)
2335 * and virtual devices (LVM, network block devices, RAM disks, Loopback)
2337 * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
2338 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
2339 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
2340 /* check needed for kernel >= 2.6.31, see sf #2942117 */
2341 if (is_disk(devbuf)) {
2342 total_reads += reads;
2343 total_writes += writes;
2346 col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
2347 &major, &minor, devbuf, &reads, &writes);
2348 if (col_count != 5) {
2353 while (cur && strcmp(devbuf, cur->dev))
2357 update_diskio_values(cur, reads, writes);
2359 update_diskio_values(&stats, total_reads, total_writes);