1 /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
2 * vim: ts=4 sw=4 noet ai cindent syntax=c
4 * Conky, a system monitor, based on torsmo
6 * Any original torsmo code is licensed under the BSD license
8 * All code written since the fork of torsmo is licensed under the GPL
10 * Please see COPYING for details
12 * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
13 * Copyright (c) 2007 Toni Spets
14 * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
16 * All rights reserved.
18 * This program is free software: you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation, either version 3 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
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>
67 /* The following ifdefs were adapted from gkrellm */
68 #include <linux/major.h>
70 #if !defined(MD_MAJOR)
74 #if !defined(LVM_BLK_MAJOR)
75 #define LVM_BLK_MAJOR 58
78 #if !defined(NBD_MAJOR)
94 #define SHORTSTAT_TEMPL "%*s %llu %llu %llu"
95 #define LONGSTAT_TEMPL "%*s %llu %llu %llu "
97 /* This flag tells the linux routines to use the /proc system where possible,
98 * even if other api's are available, e.g. sysinfo() or getloadavg().
99 * the reason for this is to allow for /proc-based distributed monitoring.
100 * using a flag in this manner creates less confusing code. */
101 static int prefer_proc = 0;
103 void prepare_update(void)
107 void update_uptime(void)
111 struct sysinfo s_info;
114 info.uptime = (double) s_info.uptime;
121 if (!(fp = open_file("/proc/uptime", &rep))) {
125 fscanf(fp, "%lf", &info.uptime);
130 int check_mount(char *s)
133 FILE *mtab = fopen("/etc/mtab", "r");
136 char buf1[256], buf2[128];
138 while (fgets(buf1, 256, mtab)) {
139 sscanf(buf1, "%*s %128s", buf2);
140 if (!strcmp(s, buf2)) {
147 NORM_ERR("Could not open mtab");
152 /* these things are also in sysinfo except Buffers:
153 * (that's why I'm reading them from proc) */
155 void update_meminfo(void)
160 /* unsigned int a; */
163 info.mem = info.memmax = info.swap = info.swapfree = info.swapmax = info.bufmem =
164 info.buffers = info.cached = info.memfree = info.memeasyfree = 0;
166 if (!(meminfo_fp = open_file("/proc/meminfo", &rep))) {
170 while (!feof(meminfo_fp)) {
171 if (fgets(buf, 255, meminfo_fp) == NULL) {
175 if (strncmp(buf, "MemTotal:", 9) == 0) {
176 sscanf(buf, "%*s %llu", &info.memmax);
177 } else if (strncmp(buf, "MemFree:", 8) == 0) {
178 sscanf(buf, "%*s %llu", &info.memfree);
179 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
180 sscanf(buf, "%*s %llu", &info.swapmax);
181 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
182 sscanf(buf, "%*s %llu", &info.swapfree);
183 } else if (strncmp(buf, "Buffers:", 8) == 0) {
184 sscanf(buf, "%*s %llu", &info.buffers);
185 } else if (strncmp(buf, "Cached:", 7) == 0) {
186 sscanf(buf, "%*s %llu", &info.cached);
190 info.mem = info.memmax - info.memfree;
191 info.memeasyfree = info.memfree;
192 info.swap = info.swapmax - info.swapfree;
194 info.bufmem = info.cached + info.buffers;
199 int get_laptop_mode(void)
204 if ((fp = fopen("/proc/sys/vm/laptop_mode", "r")) != NULL)
205 fscanf(fp, "%d\n", &val);
211 * # cat /sys/block/sda/queue/scheduler
212 * noop [anticipatory] cfq
214 char *get_ioscheduler(char *disk)
220 return strndup("n/a", text_buffer_size);
222 snprintf(buf, 127, "/sys/block/%s/queue/scheduler", disk);
223 if ((fp = fopen(buf, "r")) == NULL) {
224 return strndup("n/a", text_buffer_size);
227 fscanf(fp, "%127s", buf);
229 buf[strlen(buf) - 1] = '\0';
231 return strndup(buf + 1, text_buffer_size);
235 return strndup("n/a", text_buffer_size);
244 #define COND_FREE(x) if(x) free(x); x = 0
245 #define SAVE_SET_STRING(x, y) \
246 if (x && strcmp((char *)x, (char *)y)) { \
248 x = strndup("multiple", text_buffer_size); \
250 x = strndup(y, text_buffer_size); \
253 void update_gateway_info_failure(const char *reason)
258 //2 pointers to 1 location causes a crash when we try to free them both
259 gw_info.iface = strndup("failed", text_buffer_size);
260 gw_info.ip = strndup("failed", text_buffer_size);
264 /* Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT */
265 #define RT_ENTRY_FORMAT "%63s %lx %lx %x %*d %*d %*d %lx %*d %*d %*d\n"
267 void update_gateway_info(void)
272 unsigned long dest, gate, mask;
275 COND_FREE(gw_info.iface);
276 COND_FREE(gw_info.ip);
279 if ((fp = fopen("/proc/net/route", "r")) == NULL) {
280 update_gateway_info_failure("fopen()");
284 /* skip over the table header line, which is always present */
285 fscanf(fp, "%*[^\n]\n");
288 if(fscanf(fp, RT_ENTRY_FORMAT,
289 iface, &dest, &gate, &flags, &mask) != 5) {
290 update_gateway_info_failure("fscanf()");
293 if (!(dest || mask) && ((flags & RTF_GATEWAY) || !gate) ) {
295 SAVE_SET_STRING(gw_info.iface, iface)
297 SAVE_SET_STRING(gw_info.ip, inet_ntoa(ina))
304 void free_gateway_info(void)
310 memset(&gw_info, 0, sizeof(gw_info));
313 int gateway_exists(void)
315 return !!gw_info.count;
318 void print_gateway_iface(char *p, int p_max_size)
320 snprintf(p, p_max_size, "%s", gw_info.iface);
323 void print_gateway_ip(char *p, int p_max_size)
325 snprintf(p, p_max_size, "%s", gw_info.ip);
328 void update_net_stats(void)
332 static char first = 1;
334 // FIXME: arbitrary size chosen to keep code simple.
336 unsigned int curtmp1, curtmp2;
343 // wireless info variables
344 int skfd, has_bitrate = 0;
345 struct wireless_info *winfo;
350 delta = current_update_time - last_update_time;
351 if (delta <= 0.0001) {
355 /* open file and ignore first two lines */
356 if (!(net_dev_fp = open_file("/proc/net/dev", &rep))) {
361 fgets(buf, 255, net_dev_fp); /* garbage */
362 fgets(buf, 255, net_dev_fp); /* garbage (field names) */
364 /* read each interface */
365 for (i2 = 0; i2 < MAX_NET_INTERFACES; i2++) {
369 long long r, t, last_recv, last_trans;
371 if (fgets(buf, 255, net_dev_fp) == NULL) {
375 while (isspace((int) *p)) {
381 while (*p && *p != ':') {
390 ns = get_net_stat(s, NULL, NULL);
392 memset(&(ns->addr.sa_data), 0, 14);
394 memset(ns->addrs, 0, 17 * MAX_NET_INTERFACES + 1); /* Up to 17 chars per ip, max MAX_NET_INTERFACES interfaces. Nasty memory usage... */
396 last_recv = ns->recv;
397 last_trans = ns->trans;
399 /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
400 sscanf(p, "%lld %*d %*d %*d %*d %*d %*d %*d %lld",
403 /* if recv or trans is less than last time, an overflow happened */
404 if (r < ns->last_read_recv) {
407 ns->recv += (r - ns->last_read_recv);
409 ns->last_read_recv = r;
411 if (t < ns->last_read_trans) {
414 ns->trans += (t - ns->last_read_trans);
416 ns->last_read_trans = t;
418 /*** ip addr patch ***/
419 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
421 conf.ifc_buf = malloc(sizeof(struct ifreq) * MAX_NET_INTERFACES);
422 conf.ifc_len = sizeof(struct ifreq) * MAX_NET_INTERFACES;
423 memset(conf.ifc_buf, 0, conf.ifc_len);
425 ioctl((long) i, SIOCGIFCONF, &conf);
427 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
428 struct net_stat *ns2;
430 if (!(((struct ifreq *) conf.ifc_buf) + k))
434 ((struct ifreq *) conf.ifc_buf)[k].ifr_ifrn.ifrn_name, NULL, NULL);
435 ns2->addr = ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.ifru_addr;
436 sprintf(temp_addr, "%u.%u.%u.%u, ",
437 ns2->addr.sa_data[2] & 255,
438 ns2->addr.sa_data[3] & 255,
439 ns2->addr.sa_data[4] & 255,
440 ns2->addr.sa_data[5] & 255);
441 if(NULL == strstr(ns2->addrs, temp_addr))
442 strncpy(ns2->addrs + strlen(ns2->addrs), temp_addr, 17);
449 /*** end ip addr patch ***/
452 /* calculate speeds */
453 ns->net_rec[0] = (ns->recv - last_recv) / delta;
454 ns->net_trans[0] = (ns->trans - last_trans) / delta;
461 #pragma omp parallel for reduction(+:curtmp1, curtmp2) schedule(dynamic,10)
462 #endif /* HAVE_OPENMP */
463 for (i = 0; i < info.net_avg_samples; i++) {
464 curtmp1 = curtmp1 + ns->net_rec[i];
465 curtmp2 = curtmp2 + ns->net_trans[i];
473 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
474 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
475 if (info.net_avg_samples > 1) {
477 #pragma omp parallel for schedule(dynamic,10)
478 #endif /* HAVE_OPENMP */
479 for (i = info.net_avg_samples; i > 1; i--) {
480 ns->net_rec[i - 1] = ns->net_rec[i - 2];
481 ns->net_trans[i - 1] = ns->net_trans[i - 2];
486 /* update wireless info */
487 winfo = malloc(sizeof(struct wireless_info));
488 memset(winfo, 0, sizeof(struct wireless_info));
490 skfd = iw_sockets_open();
491 if (iw_get_basic_config(skfd, s, &(winfo->b)) > -1) {
493 // set present winfo variables
494 if (iw_get_stats(skfd, s, &(winfo->stats),
495 &winfo->range, winfo->has_range) >= 0) {
496 winfo->has_stats = 1;
498 if (iw_get_range_info(skfd, s, &(winfo->range)) >= 0) {
499 winfo->has_range = 1;
501 if (iw_get_ext(skfd, s, SIOCGIWAP, &wrq) >= 0) {
502 winfo->has_ap_addr = 1;
503 memcpy(&(winfo->ap_addr), &(wrq.u.ap_addr), sizeof(sockaddr));
507 if (iw_get_ext(skfd, s, SIOCGIWRATE, &wrq) >= 0) {
508 memcpy(&(winfo->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
509 iw_print_bitrate(ns->bitrate, 16, winfo->bitrate.value);
514 if (winfo->has_range && winfo->has_stats
515 && ((winfo->stats.qual.level != 0)
516 || (winfo->stats.qual.updated & IW_QUAL_DBM))) {
517 if (!(winfo->stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
518 ns->link_qual = winfo->stats.qual.qual;
519 ns->link_qual_max = winfo->range.max_qual.qual;
524 if (winfo->has_ap_addr) {
525 iw_sawap_ntop(&winfo->ap_addr, ns->ap);
529 if (winfo->b.has_essid) {
530 if (winfo->b.essid_on) {
531 snprintf(ns->essid, 32, "%s", winfo->b.essid);
533 snprintf(ns->essid, 32, "off/any");
537 snprintf(ns->mode, 16, "%s", iw_operation_mode[winfo->b.mode]);
539 iw_sockets_close(skfd);
550 void update_total_processes(void)
553 struct dirent *entry;
558 if (!(dir = opendir("/proc"))) {
561 while ((entry = readdir(dir))) {
563 /* Problem reading list of processes */
568 if (sscanf(entry->d_name, "%d%c", &ignore1, &ignore2) == 1) {
575 void update_threads(void)
579 struct sysinfo s_info;
582 info.threads = s_info.procs;
589 if (!(fp = open_file("/proc/loadavg", &rep))) {
593 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 void 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;
681 static double last_stat_update = 0.0;
683 /* since we use wrappers for this function, the update machinery
684 * can't eliminate double invocations of this function. Check for
685 * them here, otherwise cpu_usage counters are freaking out. */
686 if (last_stat_update == current_update_time)
688 last_stat_update = current_update_time;
690 /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
691 if (!cpu_setup || !info.cpu_usage) {
696 if (!stat_template) {
698 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
702 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
703 cpu = malloc(malloc_cpu_size);
704 memset(cpu, 0, malloc_cpu_size);
708 if (!(stat_fp = open_file("/proc/stat", &rep))) {
709 info.run_threads = 0;
710 if (info.cpu_usage) {
711 memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
717 while (!feof(stat_fp)) {
718 if (fgets(buf, 255, stat_fp) == NULL) {
722 if (strncmp(buf, "procs_running ", 14) == 0) {
723 sscanf(buf, "%*s %hu", &info.run_threads);
724 } else if (strncmp(buf, "cpu", 3) == 0) {
726 if (isdigit(buf[3])) {
727 idx = atoi(&buf[3]) + 1;
731 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
732 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
733 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
734 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
735 &(cpu[idx].cpu_steal));
737 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
738 cpu[idx].cpu_system + cpu[idx].cpu_idle +
739 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
740 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
742 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
743 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
745 delta = current_update_time - last_update_time;
747 if (delta <= 0.001) {
751 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
752 cpu[idx].cpu_last_active_total) /
753 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
756 #pragma omp parallel for reduction(+:curtmp) schedule(dynamic,10)
757 #endif /* HAVE_OPENMP */
758 for (i = 0; i < info.cpu_avg_samples; i++) {
759 curtmp = curtmp + cpu[idx].cpu_val[i];
761 /* TESTING -- I've removed this, because I don't think it is right.
762 * You shouldn't divide by the cpu count here ...
763 * removing for testing */
765 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
768 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
770 /* TESTING -- this line replaces the prev. "suspect" if/else */
771 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
773 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
774 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
776 #pragma omp parallel for schedule(dynamic,10)
777 #endif /* HAVE_OPENMP */
778 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
779 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
786 void update_running_processes(void)
791 void update_cpu_usage(void)
796 void update_load_average(void)
798 #ifdef HAVE_GETLOADAVG
803 info.loadavg[0] = (float) v[0];
804 info.loadavg[1] = (float) v[1];
805 info.loadavg[2] = (float) v[2];
812 if (!(fp = open_file("/proc/loadavg", &rep))) {
813 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
816 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
822 /***********************************************************/
823 /***********************************************************/
824 /***********************************************************/
826 static int no_dots(const struct dirent *d)
828 if (d->d_name[0] == '.') {
834 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
836 struct dirent **namelist;
839 n = scandir(dir, &namelist, no_dots, alphasort);
842 NORM_ERR("scandir for %s: %s", dir, strerror(errno));
853 strncpy(s, namelist[0]->d_name, 255);
857 #pragma omp parallel for schedule(dynamic,10)
858 #endif /* HAVE_OPENMP */
859 for (i = 0; i < n; i++) {
868 static int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
869 int *divisor, char *devtype)
876 memset(buf, 0, sizeof(buf));
878 /* if device is NULL or *, get first */
879 if (dev == NULL || strcmp(dev, "*") == 0) {
882 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
888 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
890 /* buf holds result from get_first_file_in_a_directory() above,
891 * e.g. "hwmon0" -- append "/device" */
892 strcat(buf, "/device");
894 /* dev holds device number N as a string,
895 * e.g. "0", -- convert to "hwmon0/device" */
896 sprintf(buf, "hwmon%s/device", dev);
901 /* change vol to in, tempf to temp */
902 if (strcmp(type, "vol") == 0) {
904 } else if (strcmp(type, "tempf") == 0) {
909 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
911 /* first, attempt to open file in /device */
912 fd = open(path, O_RDONLY);
915 /* if it fails, strip the /device from dev and attempt again */
916 buf[strlen(buf) - 7] = 0;
917 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
918 fd = open(path, O_RDONLY);
920 CRIT_ERR(NULL, NULL, "can't open '%s': %s\nplease check your device or remove this "
921 "var from "PACKAGE_NAME, path, strerror(errno));
925 strncpy(devtype, path, 255);
927 if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
928 || strcmp(type, "tempf") == 0) {
933 /* fan does not use *_div as a read divisor */
934 if (strcmp("fan", type) == 0) {
938 /* test if *_div file exist, open it and use it as divisor */
939 if (strcmp(type, "tempf") == 0) {
940 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
942 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
945 divfd = open(path, O_RDONLY);
951 divn = read(divfd, divbuf, 63);
952 /* should read until n == 0 but I doubt that kernel will give these
953 * in multiple pieces. :) */
955 NORM_ERR("open_sysfs_sensor(): can't read from sysfs");
958 *divisor = atoi(divbuf);
966 static double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
974 lseek(*fd, 0, SEEK_SET);
980 n = read(*fd, buf, 63);
981 /* should read until n == 0 but I doubt that kernel will give these
982 * in multiple pieces. :) */
984 NORM_ERR("get_sysfs_info(): read from %s failed\n", devtype);
993 *fd = open(devtype, O_RDONLY);
995 NORM_ERR("can't open '%s': %s", devtype, strerror(errno));
998 /* My dirty hack for computing CPU value
999 * Filedil, from forums.gentoo.org */
1000 /* if (strstr(devtype, "temp1_input") != NULL) {
1001 return -15.096 + 1.4893 * (val / 1000.0);
1004 /* divide voltage and temperature by 1000 */
1005 /* or if any other divisor is given, use that */
1006 if (strcmp(type, "tempf") == 0) {
1008 return ((val / divisor + 40) * 9.0 / 5) - 40;
1009 } else if (divisor) {
1010 return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
1012 return ((val + 40) * 9.0 / 5) - 40;
1016 return val / divisor;
1017 } else if (divisor) {
1018 return val / 1000.0;
1025 #define HWMON_RESET() {\
1030 static void parse_sysfs_sensor(struct text_object *obj, const char *arg, const char *path, const char *type)
1032 char buf1[64], buf2[64];
1033 float factor, offset;
1037 if (sscanf(arg, "%63s %d %f %f", buf2, &n, &factor, &offset) == 4) found = 1; else HWMON_RESET();
1038 if (!found && sscanf(arg, "%63s %63s %d %f %f", buf1, buf2, &n, &factor, &offset) == 5) found = 1; else if (!found) HWMON_RESET();
1039 if (!found && sscanf(arg, "%63s %63s %d", buf1, buf2, &n) == 3) found = 1; else if (!found) HWMON_RESET();
1040 if (!found && sscanf(arg, "%63s %d", buf2, &n) == 2) found = 1; else if (!found) HWMON_RESET();
1043 NORM_ERR("i2c failed to parse arguments");
1044 obj->type = OBJ_text;
1047 DBGP("parsed %s args: '%s' '%s' %d %f %f\n", type, buf1, buf2, n, factor, offset);
1048 sf = malloc(sizeof(struct sysfs));
1049 memset(sf, 0, sizeof(struct sysfs));
1050 sf->fd = open_sysfs_sensor(path, (*buf1) ? buf1 : 0, buf2, n,
1051 &sf->arg, sf->devtype);
1052 strncpy(sf->type, buf2, 63);
1053 sf->factor = factor;
1054 sf->offset = offset;
1055 obj->data.opaque = sf;
1058 #define PARSER_GENERATOR(name, path) \
1059 void parse_##name##_sensor(struct text_object *obj, const char *arg) \
1061 parse_sysfs_sensor(obj, arg, path, #name); \
1064 PARSER_GENERATOR(i2c, "/sys/bus/i2c/devices/")
1065 PARSER_GENERATOR(hwmon, "/sys/class/hwmon/")
1066 PARSER_GENERATOR(platform, "/sys/bus/platform/devices/")
1068 void print_sysfs_sensor(struct text_object *obj, char *p, int p_max_size)
1071 struct sysfs *sf = obj->data.opaque;
1076 r = get_sysfs_info(&sf->fd, sf->arg,
1077 sf->devtype, sf->type);
1079 r = r * sf->factor + sf->offset;
1081 if (!strncmp(sf->type, "temp", 4)) {
1082 temp_print(p, p_max_size, r, TEMP_CELSIUS);
1083 } else if (r >= 100.0 || r == 0) {
1084 snprintf(p, p_max_size, "%d", (int) r);
1086 snprintf(p, p_max_size, "%.1f", r);
1090 void free_sysfs_sensor(struct text_object *obj)
1092 struct sysfs *sf = obj->data.opaque;
1098 free(obj->data.opaque);
1099 obj->data.opaque = NULL;
1102 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1103 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1105 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1106 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1107 const char *p_format, int divisor, unsigned int cpu)
1115 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1121 char current_freq_file[128];
1123 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1125 f = fopen(current_freq_file, "r");
1127 /* if there's a cpufreq /sys node, read the current frequency from
1128 * this node and divide by 1000 to get Mhz. */
1129 if (fgets(s, sizeof(s), f)) {
1130 s[strlen(s) - 1] = '\0';
1131 freq = strtod(s, NULL);
1134 snprintf(p_client_buffer, client_buffer_size, p_format,
1135 (freq / 1000) / divisor);
1140 // open the CPU information file
1141 f = open_file("/proc/cpuinfo", &rep);
1143 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1148 while (fgets(s, sizeof(s), f) != NULL) {
1150 #if defined(__i386) || defined(__x86_64)
1151 // and search for the cpu mhz
1152 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1154 #if defined(__alpha)
1155 // different on alpha
1156 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1158 // this is different on ppc for some reason
1159 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1160 #endif // defined(__alpha)
1161 #endif // defined(__i386) || defined(__x86_64)
1163 // copy just the number
1164 strcpy(frequency, strchr(s, ':') + 2);
1165 #if defined(__alpha)
1167 frequency[strlen(frequency) - 6] = '\0';
1168 // kernel reports in Hz
1169 freq = strtod(frequency, NULL) / 1000000;
1172 frequency[strlen(frequency) - 1] = '\0';
1173 freq = strtod(frequency, NULL);
1177 if (strncmp(s, "processor", 9) == 0) {
1184 snprintf(p_client_buffer, client_buffer_size, p_format,
1185 (float) freq / divisor);
1189 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1191 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1201 * Peter Tarjan (ptarjan@citromail.hu) */
1203 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1204 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1205 const char *p_format, int divisor, unsigned int cpu)
1211 char current_freq_file[128];
1214 /* build the voltage file name */
1216 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1219 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1224 /* read the current cpu frequency from the /sys node */
1225 f = fopen(current_freq_file, "r");
1227 if (fgets(s, sizeof(s), f)) {
1228 s[strlen(s) - 1] = '\0';
1229 freq = strtod(s, NULL);
1233 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1234 perror("get_voltage()");
1241 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1244 /* use the current cpu frequency to find the corresponding voltage */
1245 f = fopen(current_freq_file, "r");
1251 if (fgets(line, 255, f) == NULL) {
1254 sscanf(line, "%d %d", &freq_comp, &voltage);
1255 if (freq_comp == freq) {
1261 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1262 perror("get_voltage()");
1268 snprintf(p_client_buffer, client_buffer_size, p_format,
1269 (float) voltage / divisor);
1273 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1275 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1282 if (!p_client_buffer || client_buffer_size <= 0) {
1286 /* yeah, slow... :/ */
1287 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1288 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1292 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1294 fp = open_file(buf2, &rep);
1296 snprintf(p_client_buffer, client_buffer_size,
1297 "can't open fan's state file");
1300 memset(buf, 0, sizeof(buf));
1301 fscanf(fp, "%*s %99s", buf);
1304 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1307 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1308 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1309 /* Linux 2.6.25 onwards ac adapter info is in
1310 /sys/class/power_supply/AC/
1311 On my system I get the following.
1312 /sys/class/power_supply/AC/uevent:
1313 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1316 POWER_SUPPLY_NAME=AC
1317 POWER_SUPPLY_TYPE=Mains
1318 POWER_SUPPLY_ONLINE=1
1321 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1329 if (!p_client_buffer || client_buffer_size <= 0) {
1333 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1334 fp = open_file(buf2, &rep);
1336 /* sysfs processing */
1338 if (fgets(buf, sizeof(buf), fp) == NULL)
1341 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1343 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1344 snprintf(p_client_buffer, client_buffer_size,
1345 "%s-line", (online ? "on" : "off"));
1351 /* yeah, slow... :/ */
1352 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1353 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1357 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1359 fp = open_file(buf2, &rep);
1361 snprintf(p_client_buffer, client_buffer_size,
1362 "No ac adapter found.... where is it?");
1365 memset(buf, 0, sizeof(buf));
1366 fscanf(fp, "%*s %99s", buf);
1369 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1374 /proc/acpi/thermal_zone/THRM/cooling_mode
1375 cooling mode: active
1376 /proc/acpi/thermal_zone/THRM/polling_frequency
1378 /proc/acpi/thermal_zone/THRM/state
1380 /proc/acpi/thermal_zone/THRM/temperature
1382 /proc/acpi/thermal_zone/THRM/trip_points
1384 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1387 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1388 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1390 int open_acpi_temperature(const char *name)
1396 if (name == NULL || strcmp(name, "*") == 0) {
1399 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1405 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1407 fd = open(path, O_RDONLY);
1409 NORM_ERR("can't open '%s': %s", path, strerror(errno));
1415 static double last_acpi_temp;
1416 static double last_acpi_temp_time;
1418 double get_acpi_temperature(int fd)
1424 /* don't update acpi temperature too often */
1425 if (current_update_time - last_acpi_temp_time < 11.32) {
1426 return last_acpi_temp;
1428 last_acpi_temp_time = current_update_time;
1430 /* seek to beginning */
1431 lseek(fd, 0, SEEK_SET);
1438 n = read(fd, buf, 255);
1440 NORM_ERR("can't read fd %d: %s", fd, strerror(errno));
1443 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1447 return last_acpi_temp;
1451 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1453 design capacity: 4400 mAh
1454 last full capacity: 4064 mAh
1455 battery technology: rechargeable
1456 design voltage: 14800 mV
1457 design capacity warning: 300 mAh
1458 design capacity low: 200 mAh
1459 capacity granularity 1: 32 mAh
1460 capacity granularity 2: 32 mAh
1462 serial number: 16922
1468 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1471 charging state: unknown
1473 remaining capacity: 4064 mAh
1474 present voltage: 16608 mV
1478 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1479 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1480 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1481 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1482 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1484 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1485 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1487 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1488 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1491 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1492 Linux 2.6.24 onwards battery info is in
1493 /sys/class/power_supply/BAT0/
1494 On my system I get the following.
1495 /sys/class/power_supply/BAT0/uevent:
1496 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1498 PHYSDEVDRIVER=battery
1499 POWER_SUPPLY_NAME=BAT0
1500 POWER_SUPPLY_TYPE=Battery
1501 POWER_SUPPLY_STATUS=Discharging
1502 POWER_SUPPLY_PRESENT=1
1503 POWER_SUPPLY_TECHNOLOGY=Li-ion
1504 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1505 POWER_SUPPLY_VOLTAGE_NOW=10780000
1506 POWER_SUPPLY_CURRENT_NOW=13970000
1507 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1508 POWER_SUPPLY_ENERGY_FULL=27370000
1509 POWER_SUPPLY_ENERGY_NOW=11810000
1510 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1511 POWER_SUPPLY_MANUFACTURER=Panasonic
1512 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1515 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1516 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1517 #define APM_PATH "/proc/apm"
1518 #define MAX_BATTERY_COUNT 4
1520 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1521 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1522 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1524 static int batteries_initialized = 0;
1525 static char batteries[MAX_BATTERY_COUNT][32];
1527 static int acpi_last_full[MAX_BATTERY_COUNT];
1528 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1530 /* e.g. "charging 75%" */
1531 static char last_battery_str[MAX_BATTERY_COUNT][64];
1533 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1535 static double last_battery_time[MAX_BATTERY_COUNT];
1537 static int last_battery_perct[MAX_BATTERY_COUNT];
1538 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1540 void init_batteries(void)
1544 if (batteries_initialized) {
1548 #pragma omp parallel for schedule(dynamic,10)
1549 #endif /* HAVE_OPENMP */
1550 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1551 batteries[idx][0] = '\0';
1553 batteries_initialized = 1;
1556 int get_battery_idx(const char *bat)
1560 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1561 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1566 /* if not found, enter a new entry */
1567 if (!strlen(batteries[idx])) {
1568 snprintf(batteries[idx], 31, "%s", bat);
1574 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1576 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1578 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1579 char acpi_path[128];
1580 char sysfs_path[128];
1582 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1583 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1587 idx = get_battery_idx(bat);
1589 /* don't update battery too often */
1590 if (current_update_time - last_battery_time[idx] < 29.5) {
1591 set_return_value(buffer, n, item, idx);
1595 last_battery_time[idx] = current_update_time;
1597 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1598 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1600 /* first try SYSFS if that fails try ACPI */
1602 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1603 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1606 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1607 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1610 if (sysfs_bat_fp[idx] != NULL) {
1612 int present_rate = -1;
1613 int remaining_capacity = -1;
1614 char charging_state[64];
1617 strcpy(charging_state, "unknown");
1619 while (!feof(sysfs_bat_fp[idx])) {
1621 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1624 /* let's just hope units are ok */
1625 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1626 strcpy(present, "yes");
1627 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1628 strcpy(present, "no");
1629 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1630 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1631 /* present_rate is not the same as the
1632 current flowing now but it is the same value
1633 which was used in the past. so we continue
1635 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1636 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1637 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1638 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1639 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1640 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1641 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1642 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1643 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1644 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1647 fclose(sysfs_bat_fp[idx]);
1648 sysfs_bat_fp[idx] = NULL;
1650 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1651 if (remaining_capacity > acpi_last_full[idx])
1652 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1655 if (strcmp(present, "No") == 0) {
1656 strncpy(last_battery_str[idx], "not present", 64);
1659 else if (strcmp(charging_state, "Charging") == 0) {
1660 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1661 /* e.g. charging 75% */
1662 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1663 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1665 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1666 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1667 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1668 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1669 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1670 snprintf(last_battery_time_str[idx],
1671 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1673 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1674 snprintf(last_battery_time_str[idx],
1675 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1679 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1680 if (present_rate > 0) {
1681 /* e.g. discharging 35% */
1682 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1683 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1685 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1686 (long) (((float) remaining_capacity / present_rate) * 3600));
1687 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1688 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1689 snprintf(last_battery_time_str[idx],
1690 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1692 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1694 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1695 snprintf(last_battery_time_str[idx],
1696 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1700 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1701 else if (strncmp(charging_state, "Charged", 64) == 0 || strncmp(charging_state, "Full", 64) == 0) {
1702 /* Below happens with the second battery on my X40,
1703 * when the second one is empty and the first one
1705 if (remaining_capacity == 0)
1706 strcpy(last_battery_str[idx], "empty");
1708 strcpy(last_battery_str[idx], "charged");
1710 /* unknown, probably full / AC */
1712 if (acpi_last_full[idx] != 0
1713 && remaining_capacity != acpi_last_full[idx])
1714 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1715 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1717 strncpy(last_battery_str[idx], "AC", 64);
1719 } else if (acpi_bat_fp[idx] != NULL) {
1721 int present_rate = -1;
1722 int remaining_capacity = -1;
1723 char charging_state[64];
1726 /* read last full capacity if it's zero */
1727 if (acpi_last_full[idx] == 0) {
1728 static int rep3 = 0;
1732 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1733 fp = open_file(path, &rep3);
1738 if (fgets(b, 256, fp) == NULL) {
1741 if (sscanf(b, "last full capacity: %d",
1742 &acpi_last_full[idx]) != 0) {
1751 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1753 strcpy(charging_state, "unknown");
1755 while (!feof(acpi_bat_fp[idx])) {
1758 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1762 /* let's just hope units are ok */
1763 if (strncmp(buf, "present:", 8) == 0) {
1764 sscanf(buf, "present: %4s", present);
1765 } else if (strncmp(buf, "charging state:", 15) == 0) {
1766 sscanf(buf, "charging state: %63s", charging_state);
1767 } else if (strncmp(buf, "present rate:", 13) == 0) {
1768 sscanf(buf, "present rate: %d", &present_rate);
1769 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1770 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1773 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1774 if (remaining_capacity > acpi_last_full[idx]) {
1775 /* normalize to 100% */
1776 acpi_last_full[idx] = remaining_capacity;
1780 if (strcmp(present, "no") == 0) {
1781 strncpy(last_battery_str[idx], "not present", 64);
1783 } else if (strcmp(charging_state, "charging") == 0) {
1784 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1785 /* e.g. charging 75% */
1786 snprintf(last_battery_str[idx],
1787 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1788 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1790 format_seconds(last_battery_time_str[idx],
1791 sizeof(last_battery_time_str[idx]) - 1,
1792 (long) (((acpi_last_full[idx] - remaining_capacity) *
1793 3600) / present_rate));
1794 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1795 snprintf(last_battery_str[idx],
1796 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1797 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1798 snprintf(last_battery_time_str[idx],
1799 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1801 strncpy(last_battery_str[idx], "charging",
1802 sizeof(last_battery_str[idx]) - 1);
1803 snprintf(last_battery_time_str[idx],
1804 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1807 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1808 if (present_rate > 0) {
1809 /* e.g. discharging 35% */
1810 snprintf(last_battery_str[idx],
1811 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1812 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1814 format_seconds(last_battery_time_str[idx],
1815 sizeof(last_battery_time_str[idx]) - 1,
1816 (long) ((remaining_capacity * 3600) / present_rate));
1817 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1818 snprintf(last_battery_str[idx],
1819 sizeof(last_battery_str[idx]) - 1, "full");
1820 snprintf(last_battery_time_str[idx],
1821 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1823 snprintf(last_battery_str[idx],
1824 sizeof(last_battery_str[idx]) - 1, "discharging %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");
1830 } else if (strncmp(charging_state, "charged", 64) == 0) {
1831 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1832 /* Below happens with the second battery on my X40,
1833 * when the second one is empty and the first one being charged. */
1834 if (remaining_capacity == 0) {
1835 strcpy(last_battery_str[idx], "empty");
1837 strcpy(last_battery_str[idx], "charged");
1839 /* unknown, probably full / AC */
1841 if (strncmp(charging_state, "Full", 64) == 0) {
1842 strncpy(last_battery_str[idx], "full", 64);
1843 } else if (acpi_last_full[idx] != 0
1844 && remaining_capacity != acpi_last_full[idx]) {
1845 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1846 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1848 strncpy(last_battery_str[idx], "AC", 64);
1851 fclose(acpi_bat_fp[idx]);
1852 acpi_bat_fp[idx] = NULL;
1855 if (apm_bat_fp[idx] == NULL) {
1856 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1859 if (apm_bat_fp[idx] != NULL) {
1860 unsigned int ac, status, flag;
1863 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1864 &ac, &status, &flag, &life);
1867 /* could check now that there is ac */
1868 snprintf(last_battery_str[idx], 64, "AC");
1870 /* could check that status == 3 here? */
1871 } else if (ac && life != 100) {
1872 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1874 snprintf(last_battery_str[idx], 64, "%d%%", life);
1877 /* it seemed to buffer it so file must be closed (or could use
1878 * syscalls directly but I don't feel like coding it now) */
1879 fclose(apm_bat_fp[idx]);
1880 apm_bat_fp[idx] = NULL;
1883 set_return_value(buffer, n, item, idx);
1886 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1889 case BATTERY_STATUS:
1890 snprintf(buffer, n, "%s", last_battery_str[idx]);
1893 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1900 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1902 get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1903 if (0 == strncmp("charging", buffer, 8)) {
1905 memmove(buffer + 1, buffer + 8, n - 8);
1906 } else if (0 == strncmp("discharging", buffer, 11)) {
1908 memmove(buffer + 1, buffer + 11, n - 11);
1909 } else if (0 == strncmp("charged", buffer, 7)) {
1911 memmove(buffer + 1, buffer + 7, n - 7);
1912 } else if (0 == strncmp("not present", buffer, 11)) {
1914 memmove(buffer + 1, buffer + 11, n - 11);
1915 } else if (0 == strncmp("empty", buffer, 5)) {
1917 memmove(buffer + 1, buffer + 5, n - 5);
1918 } else if (0 != strncmp("AC", buffer, 2)) {
1920 memmove(buffer + 1, buffer + 11, n - 11);
1924 int get_battery_perct(const char *bat)
1928 char acpi_path[128];
1929 char sysfs_path[128];
1930 int remaining_capacity = -1;
1932 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1933 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1937 idx = get_battery_idx(bat);
1939 /* don't update battery too often */
1940 if (current_update_time - last_battery_perct_time[idx] < 30) {
1941 return last_battery_perct[idx];
1943 last_battery_perct_time[idx] = current_update_time;
1945 /* Only check for SYSFS or ACPI */
1947 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1948 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1952 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1953 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1956 if (sysfs_bat_fp[idx] != NULL) {
1958 while (!feof(sysfs_bat_fp[idx])) {
1960 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1963 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1964 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1965 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1966 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1967 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1968 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1969 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1970 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1974 fclose(sysfs_bat_fp[idx]);
1975 sysfs_bat_fp[idx] = NULL;
1977 } else if (acpi_bat_fp[idx] != NULL) {
1979 /* read last full capacity if it's zero */
1980 if (acpi_design_capacity[idx] == 0) {
1985 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1986 fp = open_file(path, &rep2);
1991 if (fgets(b, 256, fp) == NULL) {
1994 if (sscanf(b, "last full capacity: %d",
1995 &acpi_design_capacity[idx]) != 0) {
2003 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
2005 while (!feof(acpi_bat_fp[idx])) {
2008 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
2012 if (buf[0] == 'r') {
2013 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
2017 if (remaining_capacity < 0) {
2020 /* compute the battery percentage */
2021 last_battery_perct[idx] =
2022 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
2023 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
2024 return last_battery_perct[idx];
2027 int get_battery_perct_bar(const char *bar)
2031 get_battery_perct(bar);
2032 idx = get_battery_idx(bar);
2033 return (int) (last_battery_perct[idx] * 2.56 - 1);
2036 /* On Apple powerbook and ibook:
2037 $ cat /proc/pmu/battery_0
2044 $ cat /proc/pmu/info
2045 PMU driver version : 2
2046 PMU firmware version : 0c
2051 /* defines as in <linux/pmu.h> */
2052 #define PMU_BATT_PRESENT 0x00000001
2053 #define PMU_BATT_CHARGING 0x00000002
2055 static FILE *pmu_battery_fp;
2056 static FILE *pmu_info_fp;
2057 static char pb_battery_info[3][32];
2058 static double pb_battery_info_update;
2060 #define PMU_PATH "/proc/pmu"
2061 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2064 const char *batt_path = PMU_PATH "/battery_0";
2065 const char *info_path = PMU_PATH "/info";
2067 int charge, max_charge, ac = -1;
2070 /* don't update battery too often */
2071 if (current_update_time - pb_battery_info_update < 29.5) {
2072 snprintf(buffer, n, "%s", pb_battery_info[i]);
2075 pb_battery_info_update = current_update_time;
2077 if (pmu_battery_fp == NULL) {
2078 pmu_battery_fp = open_file(batt_path, &rep);
2079 if (pmu_battery_fp == NULL) {
2084 if (pmu_battery_fp != NULL) {
2085 rewind(pmu_battery_fp);
2086 while (!feof(pmu_battery_fp)) {
2089 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2093 if (buf[0] == 'f') {
2094 sscanf(buf, "flags : %8x", &flags);
2095 } else if (buf[0] == 'c' && buf[1] == 'h') {
2096 sscanf(buf, "charge : %d", &charge);
2097 } else if (buf[0] == 'm') {
2098 sscanf(buf, "max_charge : %d", &max_charge);
2099 } else if (buf[0] == 't') {
2100 sscanf(buf, "time rem. : %ld", &timeval);
2104 if (pmu_info_fp == NULL) {
2105 pmu_info_fp = open_file(info_path, &rep);
2106 if (pmu_info_fp == NULL) {
2111 if (pmu_info_fp != NULL) {
2112 rewind(pmu_info_fp);
2113 while (!feof(pmu_info_fp)) {
2116 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2119 if (buf[0] == 'A') {
2120 sscanf(buf, "AC Power : %d", &ac);
2124 /* update status string */
2125 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2126 strncpy(pb_battery_info[PB_BATT_STATUS], "AC", sizeof(pb_battery_info[PB_BATT_STATUS]));
2127 } else if (ac && (flags & PMU_BATT_PRESENT)
2128 && !(flags & PMU_BATT_CHARGING)) {
2129 strncpy(pb_battery_info[PB_BATT_STATUS], "charged", sizeof(pb_battery_info[PB_BATT_STATUS]));
2130 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2131 strncpy(pb_battery_info[PB_BATT_STATUS], "charging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2133 strncpy(pb_battery_info[PB_BATT_STATUS], "discharging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2136 /* update percentage string */
2137 if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT)
2138 && !(flags & PMU_BATT_CHARGING)) {
2139 snprintf(pb_battery_info[PB_BATT_PERCENT],
2140 sizeof(pb_battery_info[PB_BATT_PERCENT]), "100%%");
2141 } else if (timeval == 0) {
2142 snprintf(pb_battery_info[PB_BATT_PERCENT],
2143 sizeof(pb_battery_info[PB_BATT_PERCENT]), "unknown");
2145 snprintf(pb_battery_info[PB_BATT_PERCENT],
2146 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2147 (charge * 100) / max_charge);
2150 /* update time string */
2151 if (timeval == 0) { /* fully charged or battery not present */
2152 snprintf(pb_battery_info[PB_BATT_TIME],
2153 sizeof(pb_battery_info[PB_BATT_TIME]), "unknown");
2154 } else if (timeval < 60 * 60) { /* don't show secs */
2155 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2156 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2158 format_seconds(pb_battery_info[PB_BATT_TIME],
2159 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2162 snprintf(buffer, n, "%s", pb_battery_info[i]);
2165 void update_top(void)
2167 process_find_top(info.cpu, info.memu, info.time
2172 info.first_process = get_first_process();
2175 #define ENTROPY_AVAIL_PATH "/proc/sys/kernel/random/entropy_avail"
2177 int get_entropy_avail(unsigned int *val)
2182 if (!(fp = open_file(ENTROPY_AVAIL_PATH, &rep)))
2185 if (fscanf(fp, "%u", val) != 1)
2192 #define ENTROPY_POOLSIZE_PATH "/proc/sys/kernel/random/poolsize"
2194 int get_entropy_poolsize(unsigned int *val)
2199 if (!(fp = open_file(ENTROPY_POOLSIZE_PATH, &rep)))
2202 if (fscanf(fp, "%u", val) != 1)
2209 const char *get_disk_protect_queue(const char *disk)
2215 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2216 if (access(path, F_OK)) {
2217 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2219 if ((fp = fopen(path, "r")) == NULL)
2221 if (fscanf(fp, "%d\n", &state) != 1) {
2226 return (state > 0) ? "frozen" : "free ";
2229 void update_diskio(void)
2233 char buf[512], devbuf[64];
2234 unsigned int major, minor;
2236 struct diskio_stat *cur;
2237 unsigned int reads, writes;
2238 unsigned int total_reads = 0, total_writes = 0;
2241 stats.current_read = 0;
2242 stats.current_write = 0;
2244 if (!(fp = open_file("/proc/diskstats", &rep))) {
2248 /* read reads and writes from all disks (minor = 0), including cd-roms
2249 * and floppies, and sum them up */
2250 while (fgets(buf, 512, fp)) {
2251 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
2252 &minor, devbuf, &reads, &writes);
2253 /* ignore subdevices (they have only 3 matching entries in their line)
2254 * and virtual devices (LVM, network block devices, RAM disks, Loopback)
2256 * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
2257 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
2258 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
2259 total_reads += reads;
2260 total_writes += writes;
2262 col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
2263 &major, &minor, devbuf, &reads, &writes);
2264 if (col_count != 5) {
2269 while (cur && strcmp(devbuf, cur->dev))
2273 update_diskio_values(cur, reads, writes);
2275 update_diskio_values(&stats, total_reads, total_writes);