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)
86 #define SHORTSTAT_TEMPL "%*s %llu %llu %llu"
87 #define LONGSTAT_TEMPL "%*s %llu %llu %llu "
89 /* This flag tells the linux routines to use the /proc system where possible,
90 * even if other api's are available, e.g. sysinfo() or getloadavg().
91 * the reason for this is to allow for /proc-based distributed monitoring.
92 * using a flag in this manner creates less confusing code. */
93 static int prefer_proc = 0;
95 void prepare_update(void)
99 void update_uptime(void)
103 struct sysinfo s_info;
106 info.uptime = (double) s_info.uptime;
113 if (!(fp = open_file("/proc/uptime", &rep))) {
117 fscanf(fp, "%lf", &info.uptime);
122 int check_mount(char *s)
125 FILE *mtab = fopen("/etc/mtab", "r");
128 char buf1[256], buf2[128];
130 while (fgets(buf1, 256, mtab)) {
131 sscanf(buf1, "%*s %128s", buf2);
132 if (!strcmp(s, buf2)) {
139 NORM_ERR("Could not open mtab");
144 /* these things are also in sysinfo except Buffers:
145 * (that's why I'm reading them from proc) */
147 void update_meminfo(void)
152 /* unsigned int a; */
155 info.mem = info.memmax = info.swap = info.swapfree = info.swapmax = info.bufmem =
156 info.buffers = info.cached = info.memfree = info.memeasyfree = 0;
158 if (!(meminfo_fp = open_file("/proc/meminfo", &rep))) {
162 while (!feof(meminfo_fp)) {
163 if (fgets(buf, 255, meminfo_fp) == NULL) {
167 if (strncmp(buf, "MemTotal:", 9) == 0) {
168 sscanf(buf, "%*s %llu", &info.memmax);
169 } else if (strncmp(buf, "MemFree:", 8) == 0) {
170 sscanf(buf, "%*s %llu", &info.memfree);
171 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
172 sscanf(buf, "%*s %llu", &info.swapmax);
173 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
174 sscanf(buf, "%*s %llu", &info.swapfree);
175 } else if (strncmp(buf, "Buffers:", 8) == 0) {
176 sscanf(buf, "%*s %llu", &info.buffers);
177 } else if (strncmp(buf, "Cached:", 7) == 0) {
178 sscanf(buf, "%*s %llu", &info.cached);
182 info.mem = info.memmax - info.memfree;
183 info.memeasyfree = info.memfree;
184 info.swap = info.swapmax - info.swapfree;
186 info.bufmem = info.cached + info.buffers;
191 int get_laptop_mode(void)
196 if ((fp = fopen("/proc/sys/vm/laptop_mode", "r")) != NULL)
197 fscanf(fp, "%d\n", &val);
203 * # cat /sys/block/sda/queue/scheduler
204 * noop [anticipatory] cfq
206 char *get_ioscheduler(char *disk)
212 return strndup("n/a", text_buffer_size);
214 snprintf(buf, 127, "/sys/block/%s/queue/scheduler", disk);
215 if ((fp = fopen(buf, "r")) == NULL) {
216 return strndup("n/a", text_buffer_size);
219 fscanf(fp, "%127s", buf);
221 buf[strlen(buf) - 1] = '\0';
223 return strndup(buf + 1, text_buffer_size);
227 return strndup("n/a", text_buffer_size);
236 #define COND_FREE(x) if(x) free(x); x = 0
237 #define SAVE_SET_STRING(x, y) \
238 if (x && strcmp((char *)x, (char *)y)) { \
240 x = strndup("multiple", text_buffer_size); \
242 x = strndup(y, text_buffer_size); \
245 void update_gateway_info_failure(const char *reason)
250 //2 pointers to 1 location causes a crash when we try to free them both
251 gw_info.iface = strndup("failed", text_buffer_size);
252 gw_info.ip = strndup("failed", text_buffer_size);
256 /* Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT */
257 #define RT_ENTRY_FORMAT "%63s %lx %lx %x %*d %*d %*d %lx %*d %*d %*d\n"
259 void update_gateway_info(void)
264 unsigned long dest, gate, mask;
267 COND_FREE(gw_info.iface);
268 COND_FREE(gw_info.ip);
271 if ((fp = fopen("/proc/net/route", "r")) == NULL) {
272 update_gateway_info_failure("fopen()");
276 /* skip over the table header line, which is always present */
277 fscanf(fp, "%*[^\n]\n");
280 if(fscanf(fp, RT_ENTRY_FORMAT,
281 iface, &dest, &gate, &flags, &mask) != 5) {
282 update_gateway_info_failure("fscanf()");
285 if (!(dest || mask) && ((flags & RTF_GATEWAY) || !gate) ) {
287 SAVE_SET_STRING(gw_info.iface, iface)
289 SAVE_SET_STRING(gw_info.ip, inet_ntoa(ina))
296 void free_gateway_info(void)
302 memset(&gw_info, 0, sizeof(gw_info));
305 int gateway_exists(void)
307 return !!gw_info.count;
310 void print_gateway_iface(char *p, int p_max_size)
312 snprintf(p, p_max_size, "%s", gw_info.iface);
315 void print_gateway_ip(char *p, int p_max_size)
317 snprintf(p, p_max_size, "%s", gw_info.ip);
320 void update_net_stats(void)
324 static char first = 1;
326 // FIXME: arbitrary size chosen to keep code simple.
328 unsigned int curtmp1, curtmp2;
335 // wireless info variables
336 int skfd, has_bitrate = 0;
337 struct wireless_info *winfo;
342 delta = current_update_time - last_update_time;
343 if (delta <= 0.0001) {
347 /* open file and ignore first two lines */
348 if (!(net_dev_fp = open_file("/proc/net/dev", &rep))) {
353 fgets(buf, 255, net_dev_fp); /* garbage */
354 fgets(buf, 255, net_dev_fp); /* garbage (field names) */
356 /* read each interface */
357 for (i2 = 0; i2 < 16; i2++) {
361 long long r, t, last_recv, last_trans;
363 if (fgets(buf, 255, net_dev_fp) == NULL) {
367 while (isspace((int) *p)) {
373 while (*p && *p != ':') {
382 ns = get_net_stat(s, NULL, NULL);
384 memset(&(ns->addr.sa_data), 0, 14);
386 memset(ns->addrs, 0, 17 * 16 + 1); /* Up to 17 chars per ip, max 16 interfaces. Nasty memory usage... */
388 last_recv = ns->recv;
389 last_trans = ns->trans;
391 /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
392 sscanf(p, "%lld %*d %*d %*d %*d %*d %*d %*d %lld",
395 /* if recv or trans is less than last time, an overflow happened */
396 if (r < ns->last_read_recv) {
399 ns->recv += (r - ns->last_read_recv);
401 ns->last_read_recv = r;
403 if (t < ns->last_read_trans) {
406 ns->trans += (t - ns->last_read_trans);
408 ns->last_read_trans = t;
410 /*** ip addr patch ***/
411 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
413 conf.ifc_buf = malloc(sizeof(struct ifreq) * 16);
414 conf.ifc_len = sizeof(struct ifreq) * 16;
415 memset(conf.ifc_buf, 0, conf.ifc_len);
417 ioctl((long) i, SIOCGIFCONF, &conf);
419 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
420 struct net_stat *ns2;
422 if (!(((struct ifreq *) conf.ifc_buf) + k))
426 ((struct ifreq *) conf.ifc_buf)[k].ifr_ifrn.ifrn_name, NULL, NULL);
427 ns2->addr = ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.ifru_addr;
428 sprintf(temp_addr, "%u.%u.%u.%u, ",
429 ns2->addr.sa_data[2] & 255,
430 ns2->addr.sa_data[3] & 255,
431 ns2->addr.sa_data[4] & 255,
432 ns2->addr.sa_data[5] & 255);
433 if(NULL == strstr(ns2->addrs, temp_addr))
434 strncpy(ns2->addrs + strlen(ns2->addrs), temp_addr, 17);
441 /*** end ip addr patch ***/
444 /* calculate speeds */
445 ns->net_rec[0] = (ns->recv - last_recv) / delta;
446 ns->net_trans[0] = (ns->trans - last_trans) / delta;
453 #pragma omp parallel for reduction(+:curtmp1, curtmp2) schedule(dynamic,10)
454 #endif /* HAVE_OPENMP */
455 for (i = 0; i < info.net_avg_samples; i++) {
456 curtmp1 = curtmp1 + ns->net_rec[i];
457 curtmp2 = curtmp2 + ns->net_trans[i];
465 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
466 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
467 if (info.net_avg_samples > 1) {
469 #pragma omp parallel for schedule(dynamic,10)
470 #endif /* HAVE_OPENMP */
471 for (i = info.net_avg_samples; i > 1; i--) {
472 ns->net_rec[i - 1] = ns->net_rec[i - 2];
473 ns->net_trans[i - 1] = ns->net_trans[i - 2];
478 /* update wireless info */
479 winfo = malloc(sizeof(struct wireless_info));
480 memset(winfo, 0, sizeof(struct wireless_info));
482 skfd = iw_sockets_open();
483 if (iw_get_basic_config(skfd, s, &(winfo->b)) > -1) {
485 // set present winfo variables
486 if (iw_get_stats(skfd, s, &(winfo->stats),
487 &winfo->range, winfo->has_range) >= 0) {
488 winfo->has_stats = 1;
490 if (iw_get_range_info(skfd, s, &(winfo->range)) >= 0) {
491 winfo->has_range = 1;
493 if (iw_get_ext(skfd, s, SIOCGIWAP, &wrq) >= 0) {
494 winfo->has_ap_addr = 1;
495 memcpy(&(winfo->ap_addr), &(wrq.u.ap_addr), sizeof(sockaddr));
499 if (iw_get_ext(skfd, s, SIOCGIWRATE, &wrq) >= 0) {
500 memcpy(&(winfo->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
501 iw_print_bitrate(ns->bitrate, 16, winfo->bitrate.value);
506 if (winfo->has_range && winfo->has_stats
507 && ((winfo->stats.qual.level != 0)
508 || (winfo->stats.qual.updated & IW_QUAL_DBM))) {
509 if (!(winfo->stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
510 ns->link_qual = winfo->stats.qual.qual;
511 ns->link_qual_max = winfo->range.max_qual.qual;
516 if (winfo->has_ap_addr) {
517 iw_sawap_ntop(&winfo->ap_addr, ns->ap);
521 if (winfo->b.has_essid) {
522 if (winfo->b.essid_on) {
523 snprintf(ns->essid, 32, "%s", winfo->b.essid);
525 snprintf(ns->essid, 32, "off/any");
529 snprintf(ns->mode, 16, "%s", iw_operation_mode[winfo->b.mode]);
531 iw_sockets_close(skfd);
542 void update_total_processes(void)
546 struct sysinfo s_info;
549 info.procs = s_info.procs;
556 if (!(fp = open_file("/proc/loadavg", &rep))) {
560 fscanf(fp, "%*f %*f %*f %*d/%hu", &info.procs);
565 #define CPU_SAMPLE_COUNT 15
567 unsigned long long cpu_user;
568 unsigned long long cpu_system;
569 unsigned long long cpu_nice;
570 unsigned long long cpu_idle;
571 unsigned long long cpu_iowait;
572 unsigned long long cpu_irq;
573 unsigned long long cpu_softirq;
574 unsigned long long cpu_steal;
575 unsigned long long cpu_total;
576 unsigned long long cpu_active_total;
577 unsigned long long cpu_last_total;
578 unsigned long long cpu_last_active_total;
579 double cpu_val[CPU_SAMPLE_COUNT];
581 static short cpu_setup = 0;
583 /* Determine if this kernel gives us "extended" statistics information in
585 * Kernels around 2.5 and earlier only reported user, system, nice, and
586 * idle values in proc stat.
587 * Kernels around 2.6 and greater report these PLUS iowait, irq, softirq,
589 void determine_longstat(char *buf)
591 unsigned long long iowait = 0;
593 KFLAG_SETOFF(KFLAG_IS_LONGSTAT);
594 /* scanf will either return -1 or 1 because there is only 1 assignment */
595 if (sscanf(buf, "%*s %*d %*d %*d %*d %llu", &iowait) > 0) {
596 KFLAG_SETON(KFLAG_IS_LONGSTAT);
600 void get_cpu_count(void)
606 if (info.cpu_usage) {
610 if (!(stat_fp = open_file("/proc/stat", &rep))) {
616 while (!feof(stat_fp)) {
617 if (fgets(buf, 255, stat_fp) == NULL) {
621 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
622 if (info.cpu_count == 0) {
623 determine_longstat(buf);
628 info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
633 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
634 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
636 inline static void update_stat(void)
640 static struct cpu_info *cpu = NULL;
645 const char *stat_template = NULL;
646 unsigned int malloc_cpu_size = 0;
647 extern void* global_cpu;
648 static double last_stat_update = 0.0;
650 /* since we use wrappers for this function, the update machinery
651 * can't eliminate double invocations of this function. Check for
652 * them here, otherwise cpu_usage counters are freaking out. */
653 if (last_stat_update == current_update_time)
655 last_stat_update = current_update_time;
657 /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
658 if (!cpu_setup || !info.cpu_usage) {
663 if (!stat_template) {
665 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
669 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
670 cpu = malloc(malloc_cpu_size);
671 memset(cpu, 0, malloc_cpu_size);
675 if (!(stat_fp = open_file("/proc/stat", &rep))) {
677 if (info.cpu_usage) {
678 memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
684 while (!feof(stat_fp)) {
685 if (fgets(buf, 255, stat_fp) == NULL) {
689 if (strncmp(buf, "procs_running ", 14) == 0) {
690 sscanf(buf, "%*s %hu", &info.run_procs);
691 } else if (strncmp(buf, "cpu", 3) == 0) {
693 if (isdigit(buf[3])) {
694 idx = atoi(&buf[3]) + 1;
698 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
699 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
700 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
701 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
702 &(cpu[idx].cpu_steal));
704 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
705 cpu[idx].cpu_system + cpu[idx].cpu_idle +
706 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
707 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
709 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
710 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
712 delta = current_update_time - last_update_time;
714 if (delta <= 0.001) {
718 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
719 cpu[idx].cpu_last_active_total) /
720 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
723 #pragma omp parallel for reduction(+:curtmp) schedule(dynamic,10)
724 #endif /* HAVE_OPENMP */
725 for (i = 0; i < info.cpu_avg_samples; i++) {
726 curtmp = curtmp + cpu[idx].cpu_val[i];
728 /* TESTING -- I've removed this, because I don't think it is right.
729 * You shouldn't divide by the cpu count here ...
730 * removing for testing */
732 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
735 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
737 /* TESTING -- this line replaces the prev. "suspect" if/else */
738 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
740 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
741 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
743 #pragma omp parallel for schedule(dynamic,10)
744 #endif /* HAVE_OPENMP */
745 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
746 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
753 void update_running_processes(void)
758 void update_cpu_usage(void)
763 void update_load_average(void)
765 #ifdef HAVE_GETLOADAVG
770 info.loadavg[0] = (float) v[0];
771 info.loadavg[1] = (float) v[1];
772 info.loadavg[2] = (float) v[2];
779 if (!(fp = open_file("/proc/loadavg", &rep))) {
780 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
783 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
789 #define PROC_I8K "/proc/i8k"
790 #define I8K_DELIM " "
791 static char *i8k_procbuf = NULL;
792 void update_i8k(void)
797 i8k_procbuf = (char *) malloc(128 * sizeof(char));
799 if ((fp = fopen(PROC_I8K, "r")) == NULL) {
800 CRIT_ERR(NULL, NULL, "/proc/i8k doesn't exist! use insmod to make sure the kernel "
801 "driver is loaded...");
804 memset(&i8k_procbuf[0], 0, 128);
805 if (fread(&i8k_procbuf[0], sizeof(char), 128, fp) == 0) {
806 NORM_ERR("something wrong with /proc/i8k...");
811 i8k.version = strtok(&i8k_procbuf[0], I8K_DELIM);
812 i8k.bios = strtok(NULL, I8K_DELIM);
813 i8k.serial = strtok(NULL, I8K_DELIM);
814 i8k.cpu_temp = strtok(NULL, I8K_DELIM);
815 i8k.left_fan_status = strtok(NULL, I8K_DELIM);
816 i8k.right_fan_status = strtok(NULL, I8K_DELIM);
817 i8k.left_fan_rpm = strtok(NULL, I8K_DELIM);
818 i8k.right_fan_rpm = strtok(NULL, I8K_DELIM);
819 i8k.ac_status = strtok(NULL, I8K_DELIM);
820 i8k.buttons_status = strtok(NULL, I8K_DELIM);
823 /***********************************************************/
824 /***********************************************************/
825 /***********************************************************/
827 static int no_dots(const struct dirent *d)
829 if (d->d_name[0] == '.') {
835 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
837 struct dirent **namelist;
840 n = scandir(dir, &namelist, no_dots, alphasort);
843 NORM_ERR("scandir for %s: %s", dir, strerror(errno));
854 strncpy(s, namelist[0]->d_name, 255);
858 #pragma omp parallel for schedule(dynamic,10)
859 #endif /* HAVE_OPENMP */
860 for (i = 0; i < n; i++) {
869 static int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
870 int *divisor, char *devtype)
877 memset(buf, 0, sizeof(buf));
879 /* if device is NULL or *, get first */
880 if (dev == NULL || strcmp(dev, "*") == 0) {
883 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
889 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
891 /* buf holds result from get_first_file_in_a_directory() above,
892 * e.g. "hwmon0" -- append "/device" */
893 strcat(buf, "/device");
895 /* dev holds device number N as a string,
896 * e.g. "0", -- convert to "hwmon0/device" */
897 sprintf(buf, "hwmon%s/device", dev);
902 /* change vol to in, tempf to temp */
903 if (strcmp(type, "vol") == 0) {
905 } else if (strcmp(type, "tempf") == 0) {
910 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
912 /* first, attempt to open file in /device */
913 fd = open(path, O_RDONLY);
916 /* if it fails, strip the /device from dev and attempt again */
917 buf[strlen(buf) - 7] = 0;
918 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
919 fd = open(path, O_RDONLY);
921 CRIT_ERR(NULL, NULL, "can't open '%s': %s\nplease check your device or remove this "
922 "var from "PACKAGE_NAME, path, strerror(errno));
926 strncpy(devtype, path, 255);
928 if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
929 || strcmp(type, "tempf") == 0) {
934 /* fan does not use *_div as a read divisor */
935 if (strcmp("fan", type) == 0) {
939 /* test if *_div file exist, open it and use it as divisor */
940 if (strcmp(type, "tempf") == 0) {
941 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
943 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
946 divfd = open(path, O_RDONLY);
952 divn = read(divfd, divbuf, 63);
953 /* should read until n == 0 but I doubt that kernel will give these
954 * in multiple pieces. :) */
956 NORM_ERR("open_sysfs_sensor(): can't read from sysfs");
959 *divisor = atoi(divbuf);
967 static double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
975 lseek(*fd, 0, SEEK_SET);
981 n = read(*fd, buf, 63);
982 /* should read until n == 0 but I doubt that kernel will give these
983 * in multiple pieces. :) */
985 NORM_ERR("get_sysfs_info(): read from %s failed\n", devtype);
994 *fd = open(devtype, O_RDONLY);
996 NORM_ERR("can't open '%s': %s", devtype, strerror(errno));
999 /* My dirty hack for computing CPU value
1000 * Filedil, from forums.gentoo.org */
1001 /* if (strstr(devtype, "temp1_input") != NULL) {
1002 return -15.096 + 1.4893 * (val / 1000.0);
1005 /* divide voltage and temperature by 1000 */
1006 /* or if any other divisor is given, use that */
1007 if (strcmp(type, "tempf") == 0) {
1009 return ((val / divisor + 40) * 9.0 / 5) - 40;
1010 } else if (divisor) {
1011 return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
1013 return ((val + 40) * 9.0 / 5) - 40;
1017 return val / divisor;
1018 } else if (divisor) {
1019 return val / 1000.0;
1026 #define HWMON_RESET() {\
1031 static void parse_sysfs_sensor(struct text_object *obj, const char *arg, const char *path, const char *type)
1033 char buf1[64], buf2[64];
1034 float factor, offset;
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 obj->data.sysfs.fd = open_sysfs_sensor(path, (*buf1) ? buf1 : 0, buf2, n,
1049 &obj->data.sysfs.arg, obj->data.sysfs.devtype);
1050 strncpy(obj->data.sysfs.type, buf2, 63);
1051 obj->data.sysfs.factor = factor;
1052 obj->data.sysfs.offset = offset;
1055 #define PARSER_GENERATOR(name, path) \
1056 void parse_##name##_sensor(struct text_object *obj, const char *arg) \
1058 parse_sysfs_sensor(obj, arg, path, #name); \
1061 PARSER_GENERATOR(i2c, "/sys/bus/i2c/devices/")
1062 PARSER_GENERATOR(hwmon, "/sys/class/hwmon/")
1063 PARSER_GENERATOR(platform, "/sys/bus/platform/devices/")
1065 void print_sysfs_sensor(struct text_object *obj, char *p, int p_max_size)
1069 r = get_sysfs_info(&obj->data.sysfs.fd, obj->data.sysfs.arg,
1070 obj->data.sysfs.devtype, obj->data.sysfs.type);
1072 r = r * obj->data.sysfs.factor + obj->data.sysfs.offset;
1074 if (!strncmp(obj->data.sysfs.type, "temp", 4)) {
1075 temp_print(p, p_max_size, r, TEMP_CELSIUS);
1076 } else if (r >= 100.0 || r == 0) {
1077 snprintf(p, p_max_size, "%d", (int) r);
1079 snprintf(p, p_max_size, "%.1f", r);
1083 /* Prior to kernel version 2.6.12, the CPU fan speed was available in
1084 * ADT746X_FAN_OLD, whereas later kernel versions provide this information in
1086 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
1087 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
1089 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
1092 char adt746x_fan_state[64];
1095 if (!p_client_buffer || client_buffer_size <= 0) {
1099 if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
1100 && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL) {
1101 sprintf(adt746x_fan_state, "adt746x not found");
1103 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
1104 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
1108 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_fan_state);
1111 /* Prior to kernel version 2.6.12, the CPU temperature was found in
1112 * ADT746X_CPU_OLD, whereas later kernel versions provide this information in
1114 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
1115 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
1117 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
1120 char adt746x_cpu_state[64];
1123 if (!p_client_buffer || client_buffer_size <= 0) {
1127 if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
1128 && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL) {
1129 sprintf(adt746x_cpu_state, "adt746x not found");
1131 fscanf(fp, "%2s", adt746x_cpu_state);
1135 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state);
1138 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1139 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1141 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1142 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1143 const char *p_format, int divisor, unsigned int cpu)
1151 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1157 char current_freq_file[128];
1159 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1161 f = fopen(current_freq_file, "r");
1163 /* if there's a cpufreq /sys node, read the current frequency from
1164 * this node and divide by 1000 to get Mhz. */
1165 if (fgets(s, sizeof(s), f)) {
1166 s[strlen(s) - 1] = '\0';
1167 freq = strtod(s, NULL);
1170 snprintf(p_client_buffer, client_buffer_size, p_format,
1171 (freq / 1000) / divisor);
1176 // open the CPU information file
1177 f = open_file("/proc/cpuinfo", &rep);
1179 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1184 while (fgets(s, sizeof(s), f) != NULL) {
1186 #if defined(__i386) || defined(__x86_64)
1187 // and search for the cpu mhz
1188 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1190 #if defined(__alpha)
1191 // different on alpha
1192 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1194 // this is different on ppc for some reason
1195 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1196 #endif // defined(__alpha)
1197 #endif // defined(__i386) || defined(__x86_64)
1199 // copy just the number
1200 strcpy(frequency, strchr(s, ':') + 2);
1201 #if defined(__alpha)
1203 frequency[strlen(frequency) - 6] = '\0';
1204 // kernel reports in Hz
1205 freq = strtod(frequency, NULL) / 1000000;
1208 frequency[strlen(frequency) - 1] = '\0';
1209 freq = strtod(frequency, NULL);
1213 if (strncmp(s, "processor", 9) == 0) {
1220 snprintf(p_client_buffer, client_buffer_size, p_format,
1221 (float) freq / divisor);
1225 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1227 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1237 * Peter Tarjan (ptarjan@citromail.hu) */
1239 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1240 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1241 const char *p_format, int divisor, unsigned int cpu)
1247 char current_freq_file[128];
1250 /* build the voltage file name */
1252 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1255 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1260 /* read the current cpu frequency from the /sys node */
1261 f = fopen(current_freq_file, "r");
1263 if (fgets(s, sizeof(s), f)) {
1264 s[strlen(s) - 1] = '\0';
1265 freq = strtod(s, NULL);
1269 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1270 perror("get_voltage()");
1277 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1280 /* use the current cpu frequency to find the corresponding voltage */
1281 f = fopen(current_freq_file, "r");
1287 if (fgets(line, 255, f) == NULL) {
1290 sscanf(line, "%d %d", &freq_comp, &voltage);
1291 if (freq_comp == freq) {
1297 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1298 perror("get_voltage()");
1304 snprintf(p_client_buffer, client_buffer_size, p_format,
1305 (float) voltage / divisor);
1309 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1311 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1318 if (!p_client_buffer || client_buffer_size <= 0) {
1322 /* yeah, slow... :/ */
1323 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1324 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1328 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1330 fp = open_file(buf2, &rep);
1332 snprintf(p_client_buffer, client_buffer_size,
1333 "can't open fan's state file");
1336 memset(buf, 0, sizeof(buf));
1337 fscanf(fp, "%*s %99s", buf);
1340 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1343 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1344 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1345 /* Linux 2.6.25 onwards ac adapter info is in
1346 /sys/class/power_supply/AC/
1347 On my system I get the following.
1348 /sys/class/power_supply/AC/uevent:
1349 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1352 POWER_SUPPLY_NAME=AC
1353 POWER_SUPPLY_TYPE=Mains
1354 POWER_SUPPLY_ONLINE=1
1357 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1365 if (!p_client_buffer || client_buffer_size <= 0) {
1369 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1370 fp = open_file(buf2, &rep);
1372 /* sysfs processing */
1374 if (fgets(buf, sizeof(buf), fp) == NULL)
1377 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1379 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1380 snprintf(p_client_buffer, client_buffer_size,
1381 "%s-line", (online ? "on" : "off"));
1387 /* yeah, slow... :/ */
1388 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1389 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1393 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1395 fp = open_file(buf2, &rep);
1397 snprintf(p_client_buffer, client_buffer_size,
1398 "No ac adapter found.... where is it?");
1401 memset(buf, 0, sizeof(buf));
1402 fscanf(fp, "%*s %99s", buf);
1405 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1410 /proc/acpi/thermal_zone/THRM/cooling_mode
1411 cooling mode: active
1412 /proc/acpi/thermal_zone/THRM/polling_frequency
1414 /proc/acpi/thermal_zone/THRM/state
1416 /proc/acpi/thermal_zone/THRM/temperature
1418 /proc/acpi/thermal_zone/THRM/trip_points
1420 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1423 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1424 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1426 int open_acpi_temperature(const char *name)
1432 if (name == NULL || strcmp(name, "*") == 0) {
1435 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1441 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1443 fd = open(path, O_RDONLY);
1445 NORM_ERR("can't open '%s': %s", path, strerror(errno));
1451 static double last_acpi_temp;
1452 static double last_acpi_temp_time;
1454 double get_acpi_temperature(int fd)
1460 /* don't update acpi temperature too often */
1461 if (current_update_time - last_acpi_temp_time < 11.32) {
1462 return last_acpi_temp;
1464 last_acpi_temp_time = current_update_time;
1466 /* seek to beginning */
1467 lseek(fd, 0, SEEK_SET);
1474 n = read(fd, buf, 255);
1476 NORM_ERR("can't read fd %d: %s", fd, strerror(errno));
1479 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1483 return last_acpi_temp;
1487 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1489 design capacity: 4400 mAh
1490 last full capacity: 4064 mAh
1491 battery technology: rechargeable
1492 design voltage: 14800 mV
1493 design capacity warning: 300 mAh
1494 design capacity low: 200 mAh
1495 capacity granularity 1: 32 mAh
1496 capacity granularity 2: 32 mAh
1498 serial number: 16922
1504 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1507 charging state: unknown
1509 remaining capacity: 4064 mAh
1510 present voltage: 16608 mV
1514 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1515 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1516 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1517 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1518 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1520 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1521 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1523 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1524 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1527 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1528 Linux 2.6.24 onwards battery info is in
1529 /sys/class/power_supply/BAT0/
1530 On my system I get the following.
1531 /sys/class/power_supply/BAT0/uevent:
1532 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1534 PHYSDEVDRIVER=battery
1535 POWER_SUPPLY_NAME=BAT0
1536 POWER_SUPPLY_TYPE=Battery
1537 POWER_SUPPLY_STATUS=Discharging
1538 POWER_SUPPLY_PRESENT=1
1539 POWER_SUPPLY_TECHNOLOGY=Li-ion
1540 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1541 POWER_SUPPLY_VOLTAGE_NOW=10780000
1542 POWER_SUPPLY_CURRENT_NOW=13970000
1543 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1544 POWER_SUPPLY_ENERGY_FULL=27370000
1545 POWER_SUPPLY_ENERGY_NOW=11810000
1546 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1547 POWER_SUPPLY_MANUFACTURER=Panasonic
1548 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1551 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1552 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1553 #define APM_PATH "/proc/apm"
1554 #define MAX_BATTERY_COUNT 4
1556 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1557 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1558 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1560 static int batteries_initialized = 0;
1561 static char batteries[MAX_BATTERY_COUNT][32];
1563 static int acpi_last_full[MAX_BATTERY_COUNT];
1564 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1566 /* e.g. "charging 75%" */
1567 static char last_battery_str[MAX_BATTERY_COUNT][64];
1569 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1571 static double last_battery_time[MAX_BATTERY_COUNT];
1573 static int last_battery_perct[MAX_BATTERY_COUNT];
1574 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1576 void init_batteries(void)
1580 if (batteries_initialized) {
1584 #pragma omp parallel for schedule(dynamic,10)
1585 #endif /* HAVE_OPENMP */
1586 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1587 batteries[idx][0] = '\0';
1589 batteries_initialized = 1;
1592 int get_battery_idx(const char *bat)
1596 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1597 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1602 /* if not found, enter a new entry */
1603 if (!strlen(batteries[idx])) {
1604 snprintf(batteries[idx], 31, "%s", bat);
1610 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1612 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1614 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1615 char acpi_path[128];
1616 char sysfs_path[128];
1618 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1619 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1623 idx = get_battery_idx(bat);
1625 /* don't update battery too often */
1626 if (current_update_time - last_battery_time[idx] < 29.5) {
1627 set_return_value(buffer, n, item, idx);
1631 last_battery_time[idx] = current_update_time;
1633 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1634 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1636 /* first try SYSFS if that fails try ACPI */
1638 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1639 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1642 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1643 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1646 if (sysfs_bat_fp[idx] != NULL) {
1648 int present_rate = -1;
1649 int remaining_capacity = -1;
1650 char charging_state[64];
1653 strcpy(charging_state, "unknown");
1655 while (!feof(sysfs_bat_fp[idx])) {
1657 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1660 /* let's just hope units are ok */
1661 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1662 strcpy(present, "yes");
1663 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1664 strcpy(present, "no");
1665 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1666 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1667 /* present_rate is not the same as the
1668 current flowing now but it is the same value
1669 which was used in the past. so we continue
1671 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1672 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1673 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1674 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1675 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1676 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1677 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1678 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1679 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1680 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1683 fclose(sysfs_bat_fp[idx]);
1684 sysfs_bat_fp[idx] = NULL;
1686 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1687 if (remaining_capacity > acpi_last_full[idx])
1688 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1691 if (strcmp(present, "No") == 0) {
1692 strncpy(last_battery_str[idx], "not present", 64);
1695 else if (strcmp(charging_state, "Charging") == 0) {
1696 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1697 /* e.g. charging 75% */
1698 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1699 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1701 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1702 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1703 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1704 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1705 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1706 snprintf(last_battery_time_str[idx],
1707 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1709 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1710 snprintf(last_battery_time_str[idx],
1711 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1715 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1716 if (present_rate > 0) {
1717 /* e.g. discharging 35% */
1718 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1719 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1721 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1722 (long) (((float) remaining_capacity / present_rate) * 3600));
1723 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1724 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1725 snprintf(last_battery_time_str[idx],
1726 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1728 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1730 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1731 snprintf(last_battery_time_str[idx],
1732 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1736 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1737 else if (strncmp(charging_state, "Charged", 64) == 0 || strncmp(charging_state, "Full", 64) == 0) {
1738 /* Below happens with the second battery on my X40,
1739 * when the second one is empty and the first one
1741 if (remaining_capacity == 0)
1742 strcpy(last_battery_str[idx], "empty");
1744 strcpy(last_battery_str[idx], "charged");
1746 /* unknown, probably full / AC */
1748 if (acpi_last_full[idx] != 0
1749 && remaining_capacity != acpi_last_full[idx])
1750 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1751 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1753 strncpy(last_battery_str[idx], "AC", 64);
1755 } else if (acpi_bat_fp[idx] != NULL) {
1757 int present_rate = -1;
1758 int remaining_capacity = -1;
1759 char charging_state[64];
1762 /* read last full capacity if it's zero */
1763 if (acpi_last_full[idx] == 0) {
1764 static int rep3 = 0;
1768 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1769 fp = open_file(path, &rep3);
1774 if (fgets(b, 256, fp) == NULL) {
1777 if (sscanf(b, "last full capacity: %d",
1778 &acpi_last_full[idx]) != 0) {
1787 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1789 strcpy(charging_state, "unknown");
1791 while (!feof(acpi_bat_fp[idx])) {
1794 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1798 /* let's just hope units are ok */
1799 if (strncmp(buf, "present:", 8) == 0) {
1800 sscanf(buf, "present: %4s", present);
1801 } else if (strncmp(buf, "charging state:", 15) == 0) {
1802 sscanf(buf, "charging state: %63s", charging_state);
1803 } else if (strncmp(buf, "present rate:", 13) == 0) {
1804 sscanf(buf, "present rate: %d", &present_rate);
1805 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1806 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1809 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1810 if (remaining_capacity > acpi_last_full[idx]) {
1811 /* normalize to 100% */
1812 acpi_last_full[idx] = remaining_capacity;
1816 if (strcmp(present, "no") == 0) {
1817 strncpy(last_battery_str[idx], "not present", 64);
1819 } else if (strcmp(charging_state, "charging") == 0) {
1820 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1821 /* e.g. charging 75% */
1822 snprintf(last_battery_str[idx],
1823 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1824 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1826 format_seconds(last_battery_time_str[idx],
1827 sizeof(last_battery_time_str[idx]) - 1,
1828 (long) (((acpi_last_full[idx] - remaining_capacity) *
1829 3600) / present_rate));
1830 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1831 snprintf(last_battery_str[idx],
1832 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1833 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1834 snprintf(last_battery_time_str[idx],
1835 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1837 strncpy(last_battery_str[idx], "charging",
1838 sizeof(last_battery_str[idx]) - 1);
1839 snprintf(last_battery_time_str[idx],
1840 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1843 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1844 if (present_rate > 0) {
1845 /* e.g. discharging 35% */
1846 snprintf(last_battery_str[idx],
1847 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1848 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1850 format_seconds(last_battery_time_str[idx],
1851 sizeof(last_battery_time_str[idx]) - 1,
1852 (long) ((remaining_capacity * 3600) / present_rate));
1853 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1854 snprintf(last_battery_str[idx],
1855 sizeof(last_battery_str[idx]) - 1, "full");
1856 snprintf(last_battery_time_str[idx],
1857 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1859 snprintf(last_battery_str[idx],
1860 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1861 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1862 snprintf(last_battery_time_str[idx],
1863 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1866 } else if (strncmp(charging_state, "charged", 64) == 0) {
1867 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1868 /* Below happens with the second battery on my X40,
1869 * when the second one is empty and the first one being charged. */
1870 if (remaining_capacity == 0) {
1871 strcpy(last_battery_str[idx], "empty");
1873 strcpy(last_battery_str[idx], "charged");
1875 /* unknown, probably full / AC */
1877 if (strncmp(charging_state, "Full", 64) == 0) {
1878 strncpy(last_battery_str[idx], "full", 64);
1879 } else if (acpi_last_full[idx] != 0
1880 && remaining_capacity != acpi_last_full[idx]) {
1881 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1882 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1884 strncpy(last_battery_str[idx], "AC", 64);
1887 fclose(acpi_bat_fp[idx]);
1888 acpi_bat_fp[idx] = NULL;
1891 if (apm_bat_fp[idx] == NULL) {
1892 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1895 if (apm_bat_fp[idx] != NULL) {
1896 unsigned int ac, status, flag;
1899 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1900 &ac, &status, &flag, &life);
1903 /* could check now that there is ac */
1904 snprintf(last_battery_str[idx], 64, "AC");
1906 /* could check that status == 3 here? */
1907 } else if (ac && life != 100) {
1908 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1910 snprintf(last_battery_str[idx], 64, "%d%%", life);
1913 /* it seemed to buffer it so file must be closed (or could use
1914 * syscalls directly but I don't feel like coding it now) */
1915 fclose(apm_bat_fp[idx]);
1916 apm_bat_fp[idx] = NULL;
1919 set_return_value(buffer, n, item, idx);
1922 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1925 case BATTERY_STATUS:
1926 snprintf(buffer, n, "%s", last_battery_str[idx]);
1929 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1936 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1938 get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1939 if (0 == strncmp("charging", buffer, 8)) {
1941 memmove(buffer + 1, buffer + 8, n - 8);
1942 } else if (0 == strncmp("discharging", buffer, 11)) {
1944 memmove(buffer + 1, buffer + 11, n - 11);
1945 } else if (0 == strncmp("charged", buffer, 7)) {
1947 memmove(buffer + 1, buffer + 7, n - 7);
1948 } else if (0 == strncmp("not present", buffer, 11)) {
1950 memmove(buffer + 1, buffer + 11, n - 11);
1951 } else if (0 == strncmp("empty", buffer, 5)) {
1953 memmove(buffer + 1, buffer + 5, n - 5);
1954 } else if (0 != strncmp("AC", buffer, 2)) {
1956 memmove(buffer + 1, buffer + 11, n - 11);
1960 int get_battery_perct(const char *bat)
1964 char acpi_path[128];
1965 char sysfs_path[128];
1966 int remaining_capacity = -1;
1968 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1969 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1973 idx = get_battery_idx(bat);
1975 /* don't update battery too often */
1976 if (current_update_time - last_battery_perct_time[idx] < 30) {
1977 return last_battery_perct[idx];
1979 last_battery_perct_time[idx] = current_update_time;
1981 /* Only check for SYSFS or ACPI */
1983 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1984 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1988 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1989 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1992 if (sysfs_bat_fp[idx] != NULL) {
1994 while (!feof(sysfs_bat_fp[idx])) {
1996 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1999 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
2000 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
2001 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
2002 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
2003 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
2004 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
2005 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
2006 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
2010 fclose(sysfs_bat_fp[idx]);
2011 sysfs_bat_fp[idx] = NULL;
2013 } else if (acpi_bat_fp[idx] != NULL) {
2015 /* read last full capacity if it's zero */
2016 if (acpi_design_capacity[idx] == 0) {
2021 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
2022 fp = open_file(path, &rep2);
2027 if (fgets(b, 256, fp) == NULL) {
2030 if (sscanf(b, "last full capacity: %d",
2031 &acpi_design_capacity[idx]) != 0) {
2039 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
2041 while (!feof(acpi_bat_fp[idx])) {
2044 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
2048 if (buf[0] == 'r') {
2049 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
2053 if (remaining_capacity < 0) {
2056 /* compute the battery percentage */
2057 last_battery_perct[idx] =
2058 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
2059 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
2060 return last_battery_perct[idx];
2063 int get_battery_perct_bar(const char *bar)
2067 get_battery_perct(bar);
2068 idx = get_battery_idx(bar);
2069 return (int) (last_battery_perct[idx] * 2.56 - 1);
2072 /* On Apple powerbook and ibook:
2073 $ cat /proc/pmu/battery_0
2080 $ cat /proc/pmu/info
2081 PMU driver version : 2
2082 PMU firmware version : 0c
2087 /* defines as in <linux/pmu.h> */
2088 #define PMU_BATT_PRESENT 0x00000001
2089 #define PMU_BATT_CHARGING 0x00000002
2091 static FILE *pmu_battery_fp;
2092 static FILE *pmu_info_fp;
2093 static char pb_battery_info[3][32];
2094 static double pb_battery_info_update;
2096 #define PMU_PATH "/proc/pmu"
2097 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2100 const char *batt_path = PMU_PATH "/battery_0";
2101 const char *info_path = PMU_PATH "/info";
2103 int charge, max_charge, ac = -1;
2106 /* don't update battery too often */
2107 if (current_update_time - pb_battery_info_update < 29.5) {
2108 snprintf(buffer, n, "%s", pb_battery_info[i]);
2111 pb_battery_info_update = current_update_time;
2113 if (pmu_battery_fp == NULL) {
2114 pmu_battery_fp = open_file(batt_path, &rep);
2115 if (pmu_battery_fp == NULL) {
2120 if (pmu_battery_fp != NULL) {
2121 rewind(pmu_battery_fp);
2122 while (!feof(pmu_battery_fp)) {
2125 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2129 if (buf[0] == 'f') {
2130 sscanf(buf, "flags : %8x", &flags);
2131 } else if (buf[0] == 'c' && buf[1] == 'h') {
2132 sscanf(buf, "charge : %d", &charge);
2133 } else if (buf[0] == 'm') {
2134 sscanf(buf, "max_charge : %d", &max_charge);
2135 } else if (buf[0] == 't') {
2136 sscanf(buf, "time rem. : %ld", &timeval);
2140 if (pmu_info_fp == NULL) {
2141 pmu_info_fp = open_file(info_path, &rep);
2142 if (pmu_info_fp == NULL) {
2147 if (pmu_info_fp != NULL) {
2148 rewind(pmu_info_fp);
2149 while (!feof(pmu_info_fp)) {
2152 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2155 if (buf[0] == 'A') {
2156 sscanf(buf, "AC Power : %d", &ac);
2160 /* update status string */
2161 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2162 strncpy(pb_battery_info[PB_BATT_STATUS], "AC", sizeof(pb_battery_info[PB_BATT_STATUS]));
2163 } else if (ac && (flags & PMU_BATT_PRESENT)
2164 && !(flags & PMU_BATT_CHARGING)) {
2165 strncpy(pb_battery_info[PB_BATT_STATUS], "charged", sizeof(pb_battery_info[PB_BATT_STATUS]));
2166 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2167 strncpy(pb_battery_info[PB_BATT_STATUS], "charging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2169 strncpy(pb_battery_info[PB_BATT_STATUS], "discharging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2172 /* update percentage string */
2173 if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT)
2174 && !(flags & PMU_BATT_CHARGING)) {
2175 snprintf(pb_battery_info[PB_BATT_PERCENT],
2176 sizeof(pb_battery_info[PB_BATT_PERCENT]), "100%%");
2177 } else if (timeval == 0) {
2178 snprintf(pb_battery_info[PB_BATT_PERCENT],
2179 sizeof(pb_battery_info[PB_BATT_PERCENT]), "unknown");
2181 snprintf(pb_battery_info[PB_BATT_PERCENT],
2182 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2183 (charge * 100) / max_charge);
2186 /* update time string */
2187 if (timeval == 0) { /* fully charged or battery not present */
2188 snprintf(pb_battery_info[PB_BATT_TIME],
2189 sizeof(pb_battery_info[PB_BATT_TIME]), "unknown");
2190 } else if (timeval < 60 * 60) { /* don't show secs */
2191 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2192 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2194 format_seconds(pb_battery_info[PB_BATT_TIME],
2195 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2198 snprintf(buffer, n, "%s", pb_battery_info[i]);
2201 void update_top(void)
2203 process_find_top(info.cpu, info.memu, info.time
2208 info.first_process = get_first_process();
2211 void update_entropy(void)
2214 const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2215 const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2218 info.entropy.entropy_avail = 0;
2219 info.entropy.poolsize = 0;
2221 if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2225 if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2230 fscanf(fp1, "%u", &info.entropy.entropy_avail);
2231 fscanf(fp2, "%u", &info.entropy.poolsize);
2237 const char *get_disk_protect_queue(const char *disk)
2243 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2244 if (access(path, F_OK)) {
2245 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2247 if ((fp = fopen(path, "r")) == NULL)
2249 if (fscanf(fp, "%d\n", &state) != 1) {
2254 return (state > 0) ? "frozen" : "free ";
2257 void update_diskio(void)
2261 char buf[512], devbuf[64];
2262 unsigned int major, minor;
2264 struct diskio_stat *cur;
2265 unsigned int reads, writes;
2266 unsigned int total_reads = 0, total_writes = 0;
2269 stats.current_read = 0;
2270 stats.current_write = 0;
2272 if (!(fp = open_file("/proc/diskstats", &rep))) {
2276 /* read reads and writes from all disks (minor = 0), including cd-roms
2277 * and floppies, and sum them up */
2278 while (fgets(buf, 512, fp)) {
2279 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
2280 &minor, devbuf, &reads, &writes);
2281 /* ignore subdevices (they have only 3 matching entries in their line)
2282 * and virtual devices (LVM, network block devices, RAM disks, Loopback)
2284 * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
2285 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
2286 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
2287 total_reads += reads;
2288 total_writes += writes;
2290 col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
2291 &major, &minor, devbuf, &reads, &writes);
2292 if (col_count != 5) {
2297 while (cur && strcmp(devbuf, cur->dev))
2301 update_diskio_values(cur, reads, writes);
2303 update_diskio_values(&stats, total_reads, total_writes);