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 < 16; 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 * 16 + 1); /* Up to 17 chars per ip, max 16 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) * 16);
422 conf.ifc_len = sizeof(struct ifreq) * 16;
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)
554 struct sysinfo s_info;
557 info.procs = s_info.procs;
564 if (!(fp = open_file("/proc/loadavg", &rep))) {
568 fscanf(fp, "%*f %*f %*f %*d/%hu", &info.procs);
573 #define CPU_SAMPLE_COUNT 15
575 unsigned long long cpu_user;
576 unsigned long long cpu_system;
577 unsigned long long cpu_nice;
578 unsigned long long cpu_idle;
579 unsigned long long cpu_iowait;
580 unsigned long long cpu_irq;
581 unsigned long long cpu_softirq;
582 unsigned long long cpu_steal;
583 unsigned long long cpu_total;
584 unsigned long long cpu_active_total;
585 unsigned long long cpu_last_total;
586 unsigned long long cpu_last_active_total;
587 double cpu_val[CPU_SAMPLE_COUNT];
589 static short cpu_setup = 0;
591 /* Determine if this kernel gives us "extended" statistics information in
593 * Kernels around 2.5 and earlier only reported user, system, nice, and
594 * idle values in proc stat.
595 * Kernels around 2.6 and greater report these PLUS iowait, irq, softirq,
597 void determine_longstat(char *buf)
599 unsigned long long iowait = 0;
601 KFLAG_SETOFF(KFLAG_IS_LONGSTAT);
602 /* scanf will either return -1 or 1 because there is only 1 assignment */
603 if (sscanf(buf, "%*s %*d %*d %*d %*d %llu", &iowait) > 0) {
604 KFLAG_SETON(KFLAG_IS_LONGSTAT);
608 void get_cpu_count(void)
614 if (info.cpu_usage) {
618 if (!(stat_fp = open_file("/proc/stat", &rep))) {
624 while (!feof(stat_fp)) {
625 if (fgets(buf, 255, stat_fp) == NULL) {
629 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
630 if (info.cpu_count == 0) {
631 determine_longstat(buf);
636 info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
641 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
642 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
644 inline static void update_stat(void)
648 static struct cpu_info *cpu = NULL;
653 const char *stat_template = NULL;
654 unsigned int malloc_cpu_size = 0;
655 extern void* global_cpu;
656 static double last_stat_update = 0.0;
658 /* since we use wrappers for this function, the update machinery
659 * can't eliminate double invocations of this function. Check for
660 * them here, otherwise cpu_usage counters are freaking out. */
661 if (last_stat_update == current_update_time)
663 last_stat_update = current_update_time;
665 /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
666 if (!cpu_setup || !info.cpu_usage) {
671 if (!stat_template) {
673 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
677 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
678 cpu = malloc(malloc_cpu_size);
679 memset(cpu, 0, malloc_cpu_size);
683 if (!(stat_fp = open_file("/proc/stat", &rep))) {
685 if (info.cpu_usage) {
686 memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
692 while (!feof(stat_fp)) {
693 if (fgets(buf, 255, stat_fp) == NULL) {
697 if (strncmp(buf, "procs_running ", 14) == 0) {
698 sscanf(buf, "%*s %hu", &info.run_procs);
699 } else if (strncmp(buf, "cpu", 3) == 0) {
701 if (isdigit(buf[3])) {
702 idx = atoi(&buf[3]) + 1;
706 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
707 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
708 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
709 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
710 &(cpu[idx].cpu_steal));
712 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
713 cpu[idx].cpu_system + cpu[idx].cpu_idle +
714 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
715 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
717 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
718 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
720 delta = current_update_time - last_update_time;
722 if (delta <= 0.001) {
726 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
727 cpu[idx].cpu_last_active_total) /
728 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
731 #pragma omp parallel for reduction(+:curtmp) schedule(dynamic,10)
732 #endif /* HAVE_OPENMP */
733 for (i = 0; i < info.cpu_avg_samples; i++) {
734 curtmp = curtmp + cpu[idx].cpu_val[i];
736 /* TESTING -- I've removed this, because I don't think it is right.
737 * You shouldn't divide by the cpu count here ...
738 * removing for testing */
740 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
743 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
745 /* TESTING -- this line replaces the prev. "suspect" if/else */
746 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
748 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
749 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
751 #pragma omp parallel for schedule(dynamic,10)
752 #endif /* HAVE_OPENMP */
753 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
754 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
761 void update_running_processes(void)
766 void update_cpu_usage(void)
771 void update_load_average(void)
773 #ifdef HAVE_GETLOADAVG
778 info.loadavg[0] = (float) v[0];
779 info.loadavg[1] = (float) v[1];
780 info.loadavg[2] = (float) v[2];
787 if (!(fp = open_file("/proc/loadavg", &rep))) {
788 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
791 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
797 #define PROC_I8K "/proc/i8k"
798 #define I8K_DELIM " "
799 static char *i8k_procbuf = NULL;
800 void update_i8k(void)
805 i8k_procbuf = (char *) malloc(128 * sizeof(char));
807 if ((fp = fopen(PROC_I8K, "r")) == NULL) {
808 CRIT_ERR(NULL, NULL, "/proc/i8k doesn't exist! use insmod to make sure the kernel "
809 "driver is loaded...");
812 memset(&i8k_procbuf[0], 0, 128);
813 if (fread(&i8k_procbuf[0], sizeof(char), 128, fp) == 0) {
814 NORM_ERR("something wrong with /proc/i8k...");
819 i8k.version = strtok(&i8k_procbuf[0], I8K_DELIM);
820 i8k.bios = strtok(NULL, I8K_DELIM);
821 i8k.serial = strtok(NULL, I8K_DELIM);
822 i8k.cpu_temp = strtok(NULL, I8K_DELIM);
823 i8k.left_fan_status = strtok(NULL, I8K_DELIM);
824 i8k.right_fan_status = strtok(NULL, I8K_DELIM);
825 i8k.left_fan_rpm = strtok(NULL, I8K_DELIM);
826 i8k.right_fan_rpm = strtok(NULL, I8K_DELIM);
827 i8k.ac_status = strtok(NULL, I8K_DELIM);
828 i8k.buttons_status = strtok(NULL, I8K_DELIM);
831 /***********************************************************/
832 /***********************************************************/
833 /***********************************************************/
835 static int no_dots(const struct dirent *d)
837 if (d->d_name[0] == '.') {
843 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
845 struct dirent **namelist;
848 n = scandir(dir, &namelist, no_dots, alphasort);
851 NORM_ERR("scandir for %s: %s", dir, strerror(errno));
862 strncpy(s, namelist[0]->d_name, 255);
866 #pragma omp parallel for schedule(dynamic,10)
867 #endif /* HAVE_OPENMP */
868 for (i = 0; i < n; i++) {
877 static int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
878 int *divisor, char *devtype)
885 memset(buf, 0, sizeof(buf));
887 /* if device is NULL or *, get first */
888 if (dev == NULL || strcmp(dev, "*") == 0) {
891 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
897 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
899 /* buf holds result from get_first_file_in_a_directory() above,
900 * e.g. "hwmon0" -- append "/device" */
901 strcat(buf, "/device");
903 /* dev holds device number N as a string,
904 * e.g. "0", -- convert to "hwmon0/device" */
905 sprintf(buf, "hwmon%s/device", dev);
910 /* change vol to in, tempf to temp */
911 if (strcmp(type, "vol") == 0) {
913 } else if (strcmp(type, "tempf") == 0) {
918 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
920 /* first, attempt to open file in /device */
921 fd = open(path, O_RDONLY);
924 /* if it fails, strip the /device from dev and attempt again */
925 buf[strlen(buf) - 7] = 0;
926 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
927 fd = open(path, O_RDONLY);
929 CRIT_ERR(NULL, NULL, "can't open '%s': %s\nplease check your device or remove this "
930 "var from "PACKAGE_NAME, path, strerror(errno));
934 strncpy(devtype, path, 255);
936 if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
937 || strcmp(type, "tempf") == 0) {
942 /* fan does not use *_div as a read divisor */
943 if (strcmp("fan", type) == 0) {
947 /* test if *_div file exist, open it and use it as divisor */
948 if (strcmp(type, "tempf") == 0) {
949 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
951 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
954 divfd = open(path, O_RDONLY);
960 divn = read(divfd, divbuf, 63);
961 /* should read until n == 0 but I doubt that kernel will give these
962 * in multiple pieces. :) */
964 NORM_ERR("open_sysfs_sensor(): can't read from sysfs");
967 *divisor = atoi(divbuf);
975 static double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
983 lseek(*fd, 0, SEEK_SET);
989 n = read(*fd, buf, 63);
990 /* should read until n == 0 but I doubt that kernel will give these
991 * in multiple pieces. :) */
993 NORM_ERR("get_sysfs_info(): read from %s failed\n", devtype);
1002 *fd = open(devtype, O_RDONLY);
1004 NORM_ERR("can't open '%s': %s", devtype, strerror(errno));
1007 /* My dirty hack for computing CPU value
1008 * Filedil, from forums.gentoo.org */
1009 /* if (strstr(devtype, "temp1_input") != NULL) {
1010 return -15.096 + 1.4893 * (val / 1000.0);
1013 /* divide voltage and temperature by 1000 */
1014 /* or if any other divisor is given, use that */
1015 if (strcmp(type, "tempf") == 0) {
1017 return ((val / divisor + 40) * 9.0 / 5) - 40;
1018 } else if (divisor) {
1019 return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
1021 return ((val + 40) * 9.0 / 5) - 40;
1025 return val / divisor;
1026 } else if (divisor) {
1027 return val / 1000.0;
1034 #define HWMON_RESET() {\
1039 static void parse_sysfs_sensor(struct text_object *obj, const char *arg, const char *path, const char *type)
1041 char buf1[64], buf2[64];
1042 float factor, offset;
1046 if (sscanf(arg, "%63s %d %f %f", buf2, &n, &factor, &offset) == 4) found = 1; else HWMON_RESET();
1047 if (!found && sscanf(arg, "%63s %63s %d %f %f", buf1, buf2, &n, &factor, &offset) == 5) found = 1; else if (!found) HWMON_RESET();
1048 if (!found && sscanf(arg, "%63s %63s %d", buf1, buf2, &n) == 3) found = 1; else if (!found) HWMON_RESET();
1049 if (!found && sscanf(arg, "%63s %d", buf2, &n) == 2) found = 1; else if (!found) HWMON_RESET();
1052 NORM_ERR("i2c failed to parse arguments");
1053 obj->type = OBJ_text;
1056 DBGP("parsed %s args: '%s' '%s' %d %f %f\n", type, buf1, buf2, n, factor, offset);
1057 sf = malloc(sizeof(struct sysfs));
1058 memset(sf, 0, sizeof(struct sysfs));
1059 sf->fd = open_sysfs_sensor(path, (*buf1) ? buf1 : 0, buf2, n,
1060 &sf->arg, sf->devtype);
1061 strncpy(sf->type, buf2, 63);
1062 sf->factor = factor;
1063 sf->offset = offset;
1064 obj->data.opaque = sf;
1067 #define PARSER_GENERATOR(name, path) \
1068 void parse_##name##_sensor(struct text_object *obj, const char *arg) \
1070 parse_sysfs_sensor(obj, arg, path, #name); \
1073 PARSER_GENERATOR(i2c, "/sys/bus/i2c/devices/")
1074 PARSER_GENERATOR(hwmon, "/sys/class/hwmon/")
1075 PARSER_GENERATOR(platform, "/sys/bus/platform/devices/")
1077 void print_sysfs_sensor(struct text_object *obj, char *p, int p_max_size)
1080 struct sysfs *sf = obj->data.opaque;
1085 r = get_sysfs_info(&sf->fd, sf->arg,
1086 sf->devtype, sf->type);
1088 r = r * sf->factor + sf->offset;
1090 if (!strncmp(sf->type, "temp", 4)) {
1091 temp_print(p, p_max_size, r, TEMP_CELSIUS);
1092 } else if (r >= 100.0 || r == 0) {
1093 snprintf(p, p_max_size, "%d", (int) r);
1095 snprintf(p, p_max_size, "%.1f", r);
1099 void free_sysfs_sensor(struct text_object *obj)
1101 struct sysfs *sf = obj->data.opaque;
1107 free(obj->data.opaque);
1108 obj->data.opaque = NULL;
1111 /* Prior to kernel version 2.6.12, the CPU fan speed was available in
1112 * ADT746X_FAN_OLD, whereas later kernel versions provide this information in
1114 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
1115 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
1117 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
1120 char adt746x_fan_state[64];
1123 if (!p_client_buffer || client_buffer_size <= 0) {
1127 if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
1128 && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL) {
1129 sprintf(adt746x_fan_state, "adt746x not found");
1131 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
1132 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
1136 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_fan_state);
1139 /* Prior to kernel version 2.6.12, the CPU temperature was found in
1140 * ADT746X_CPU_OLD, whereas later kernel versions provide this information in
1142 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
1143 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
1145 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
1148 char adt746x_cpu_state[64];
1151 if (!p_client_buffer || client_buffer_size <= 0) {
1155 if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
1156 && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL) {
1157 sprintf(adt746x_cpu_state, "adt746x not found");
1159 fscanf(fp, "%2s", adt746x_cpu_state);
1163 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state);
1166 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1167 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1169 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1170 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1171 const char *p_format, int divisor, unsigned int cpu)
1179 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1185 char current_freq_file[128];
1187 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1189 f = fopen(current_freq_file, "r");
1191 /* if there's a cpufreq /sys node, read the current frequency from
1192 * this node and divide by 1000 to get Mhz. */
1193 if (fgets(s, sizeof(s), f)) {
1194 s[strlen(s) - 1] = '\0';
1195 freq = strtod(s, NULL);
1198 snprintf(p_client_buffer, client_buffer_size, p_format,
1199 (freq / 1000) / divisor);
1204 // open the CPU information file
1205 f = open_file("/proc/cpuinfo", &rep);
1207 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1212 while (fgets(s, sizeof(s), f) != NULL) {
1214 #if defined(__i386) || defined(__x86_64)
1215 // and search for the cpu mhz
1216 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1218 #if defined(__alpha)
1219 // different on alpha
1220 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1222 // this is different on ppc for some reason
1223 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1224 #endif // defined(__alpha)
1225 #endif // defined(__i386) || defined(__x86_64)
1227 // copy just the number
1228 strcpy(frequency, strchr(s, ':') + 2);
1229 #if defined(__alpha)
1231 frequency[strlen(frequency) - 6] = '\0';
1232 // kernel reports in Hz
1233 freq = strtod(frequency, NULL) / 1000000;
1236 frequency[strlen(frequency) - 1] = '\0';
1237 freq = strtod(frequency, NULL);
1241 if (strncmp(s, "processor", 9) == 0) {
1248 snprintf(p_client_buffer, client_buffer_size, p_format,
1249 (float) freq / divisor);
1253 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1255 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1265 * Peter Tarjan (ptarjan@citromail.hu) */
1267 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1268 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1269 const char *p_format, int divisor, unsigned int cpu)
1275 char current_freq_file[128];
1278 /* build the voltage file name */
1280 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1283 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1288 /* read the current cpu frequency from the /sys node */
1289 f = fopen(current_freq_file, "r");
1291 if (fgets(s, sizeof(s), f)) {
1292 s[strlen(s) - 1] = '\0';
1293 freq = strtod(s, NULL);
1297 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1298 perror("get_voltage()");
1305 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1308 /* use the current cpu frequency to find the corresponding voltage */
1309 f = fopen(current_freq_file, "r");
1315 if (fgets(line, 255, f) == NULL) {
1318 sscanf(line, "%d %d", &freq_comp, &voltage);
1319 if (freq_comp == freq) {
1325 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1326 perror("get_voltage()");
1332 snprintf(p_client_buffer, client_buffer_size, p_format,
1333 (float) voltage / divisor);
1337 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1339 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1346 if (!p_client_buffer || client_buffer_size <= 0) {
1350 /* yeah, slow... :/ */
1351 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1352 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1356 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1358 fp = open_file(buf2, &rep);
1360 snprintf(p_client_buffer, client_buffer_size,
1361 "can't open fan's state file");
1364 memset(buf, 0, sizeof(buf));
1365 fscanf(fp, "%*s %99s", buf);
1368 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1371 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1372 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1373 /* Linux 2.6.25 onwards ac adapter info is in
1374 /sys/class/power_supply/AC/
1375 On my system I get the following.
1376 /sys/class/power_supply/AC/uevent:
1377 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1380 POWER_SUPPLY_NAME=AC
1381 POWER_SUPPLY_TYPE=Mains
1382 POWER_SUPPLY_ONLINE=1
1385 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1393 if (!p_client_buffer || client_buffer_size <= 0) {
1397 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1398 fp = open_file(buf2, &rep);
1400 /* sysfs processing */
1402 if (fgets(buf, sizeof(buf), fp) == NULL)
1405 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1407 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1408 snprintf(p_client_buffer, client_buffer_size,
1409 "%s-line", (online ? "on" : "off"));
1415 /* yeah, slow... :/ */
1416 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1417 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1421 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1423 fp = open_file(buf2, &rep);
1425 snprintf(p_client_buffer, client_buffer_size,
1426 "No ac adapter found.... where is it?");
1429 memset(buf, 0, sizeof(buf));
1430 fscanf(fp, "%*s %99s", buf);
1433 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1438 /proc/acpi/thermal_zone/THRM/cooling_mode
1439 cooling mode: active
1440 /proc/acpi/thermal_zone/THRM/polling_frequency
1442 /proc/acpi/thermal_zone/THRM/state
1444 /proc/acpi/thermal_zone/THRM/temperature
1446 /proc/acpi/thermal_zone/THRM/trip_points
1448 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1451 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1452 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1454 int open_acpi_temperature(const char *name)
1460 if (name == NULL || strcmp(name, "*") == 0) {
1463 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1469 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1471 fd = open(path, O_RDONLY);
1473 NORM_ERR("can't open '%s': %s", path, strerror(errno));
1479 static double last_acpi_temp;
1480 static double last_acpi_temp_time;
1482 double get_acpi_temperature(int fd)
1488 /* don't update acpi temperature too often */
1489 if (current_update_time - last_acpi_temp_time < 11.32) {
1490 return last_acpi_temp;
1492 last_acpi_temp_time = current_update_time;
1494 /* seek to beginning */
1495 lseek(fd, 0, SEEK_SET);
1502 n = read(fd, buf, 255);
1504 NORM_ERR("can't read fd %d: %s", fd, strerror(errno));
1507 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1511 return last_acpi_temp;
1515 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1517 design capacity: 4400 mAh
1518 last full capacity: 4064 mAh
1519 battery technology: rechargeable
1520 design voltage: 14800 mV
1521 design capacity warning: 300 mAh
1522 design capacity low: 200 mAh
1523 capacity granularity 1: 32 mAh
1524 capacity granularity 2: 32 mAh
1526 serial number: 16922
1532 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1535 charging state: unknown
1537 remaining capacity: 4064 mAh
1538 present voltage: 16608 mV
1542 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1543 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1544 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1545 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1546 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1548 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1549 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1551 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1552 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1555 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1556 Linux 2.6.24 onwards battery info is in
1557 /sys/class/power_supply/BAT0/
1558 On my system I get the following.
1559 /sys/class/power_supply/BAT0/uevent:
1560 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1562 PHYSDEVDRIVER=battery
1563 POWER_SUPPLY_NAME=BAT0
1564 POWER_SUPPLY_TYPE=Battery
1565 POWER_SUPPLY_STATUS=Discharging
1566 POWER_SUPPLY_PRESENT=1
1567 POWER_SUPPLY_TECHNOLOGY=Li-ion
1568 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1569 POWER_SUPPLY_VOLTAGE_NOW=10780000
1570 POWER_SUPPLY_CURRENT_NOW=13970000
1571 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1572 POWER_SUPPLY_ENERGY_FULL=27370000
1573 POWER_SUPPLY_ENERGY_NOW=11810000
1574 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1575 POWER_SUPPLY_MANUFACTURER=Panasonic
1576 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1579 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1580 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1581 #define APM_PATH "/proc/apm"
1582 #define MAX_BATTERY_COUNT 4
1584 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1585 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1586 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1588 static int batteries_initialized = 0;
1589 static char batteries[MAX_BATTERY_COUNT][32];
1591 static int acpi_last_full[MAX_BATTERY_COUNT];
1592 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1594 /* e.g. "charging 75%" */
1595 static char last_battery_str[MAX_BATTERY_COUNT][64];
1597 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1599 static double last_battery_time[MAX_BATTERY_COUNT];
1601 static int last_battery_perct[MAX_BATTERY_COUNT];
1602 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1604 void init_batteries(void)
1608 if (batteries_initialized) {
1612 #pragma omp parallel for schedule(dynamic,10)
1613 #endif /* HAVE_OPENMP */
1614 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1615 batteries[idx][0] = '\0';
1617 batteries_initialized = 1;
1620 int get_battery_idx(const char *bat)
1624 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1625 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1630 /* if not found, enter a new entry */
1631 if (!strlen(batteries[idx])) {
1632 snprintf(batteries[idx], 31, "%s", bat);
1638 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1640 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1642 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1643 char acpi_path[128];
1644 char sysfs_path[128];
1646 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1647 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1651 idx = get_battery_idx(bat);
1653 /* don't update battery too often */
1654 if (current_update_time - last_battery_time[idx] < 29.5) {
1655 set_return_value(buffer, n, item, idx);
1659 last_battery_time[idx] = current_update_time;
1661 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1662 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1664 /* first try SYSFS if that fails try ACPI */
1666 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1667 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1670 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1671 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1674 if (sysfs_bat_fp[idx] != NULL) {
1676 int present_rate = -1;
1677 int remaining_capacity = -1;
1678 char charging_state[64];
1681 strcpy(charging_state, "unknown");
1683 while (!feof(sysfs_bat_fp[idx])) {
1685 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1688 /* let's just hope units are ok */
1689 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1690 strcpy(present, "yes");
1691 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1692 strcpy(present, "no");
1693 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1694 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1695 /* present_rate is not the same as the
1696 current flowing now but it is the same value
1697 which was used in the past. so we continue
1699 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1700 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1701 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1702 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1703 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1704 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1705 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1706 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1707 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1708 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1711 fclose(sysfs_bat_fp[idx]);
1712 sysfs_bat_fp[idx] = NULL;
1714 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1715 if (remaining_capacity > acpi_last_full[idx])
1716 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1719 if (strcmp(present, "No") == 0) {
1720 strncpy(last_battery_str[idx], "not present", 64);
1723 else if (strcmp(charging_state, "Charging") == 0) {
1724 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1725 /* e.g. charging 75% */
1726 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1727 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1729 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1730 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1731 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1732 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1733 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1734 snprintf(last_battery_time_str[idx],
1735 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1737 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1738 snprintf(last_battery_time_str[idx],
1739 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1743 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1744 if (present_rate > 0) {
1745 /* e.g. discharging 35% */
1746 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1747 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1749 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1750 (long) (((float) remaining_capacity / present_rate) * 3600));
1751 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1752 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1753 snprintf(last_battery_time_str[idx],
1754 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1756 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1758 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1759 snprintf(last_battery_time_str[idx],
1760 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1764 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1765 else if (strncmp(charging_state, "Charged", 64) == 0 || strncmp(charging_state, "Full", 64) == 0) {
1766 /* Below happens with the second battery on my X40,
1767 * when the second one is empty and the first one
1769 if (remaining_capacity == 0)
1770 strcpy(last_battery_str[idx], "empty");
1772 strcpy(last_battery_str[idx], "charged");
1774 /* unknown, probably full / AC */
1776 if (acpi_last_full[idx] != 0
1777 && remaining_capacity != acpi_last_full[idx])
1778 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1779 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1781 strncpy(last_battery_str[idx], "AC", 64);
1783 } else if (acpi_bat_fp[idx] != NULL) {
1785 int present_rate = -1;
1786 int remaining_capacity = -1;
1787 char charging_state[64];
1790 /* read last full capacity if it's zero */
1791 if (acpi_last_full[idx] == 0) {
1792 static int rep3 = 0;
1796 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1797 fp = open_file(path, &rep3);
1802 if (fgets(b, 256, fp) == NULL) {
1805 if (sscanf(b, "last full capacity: %d",
1806 &acpi_last_full[idx]) != 0) {
1815 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1817 strcpy(charging_state, "unknown");
1819 while (!feof(acpi_bat_fp[idx])) {
1822 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1826 /* let's just hope units are ok */
1827 if (strncmp(buf, "present:", 8) == 0) {
1828 sscanf(buf, "present: %4s", present);
1829 } else if (strncmp(buf, "charging state:", 15) == 0) {
1830 sscanf(buf, "charging state: %63s", charging_state);
1831 } else if (strncmp(buf, "present rate:", 13) == 0) {
1832 sscanf(buf, "present rate: %d", &present_rate);
1833 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1834 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1837 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1838 if (remaining_capacity > acpi_last_full[idx]) {
1839 /* normalize to 100% */
1840 acpi_last_full[idx] = remaining_capacity;
1844 if (strcmp(present, "no") == 0) {
1845 strncpy(last_battery_str[idx], "not present", 64);
1847 } else if (strcmp(charging_state, "charging") == 0) {
1848 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1849 /* e.g. charging 75% */
1850 snprintf(last_battery_str[idx],
1851 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1852 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1854 format_seconds(last_battery_time_str[idx],
1855 sizeof(last_battery_time_str[idx]) - 1,
1856 (long) (((acpi_last_full[idx] - remaining_capacity) *
1857 3600) / present_rate));
1858 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1859 snprintf(last_battery_str[idx],
1860 sizeof(last_battery_str[idx]) - 1, "charging %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");
1865 strncpy(last_battery_str[idx], "charging",
1866 sizeof(last_battery_str[idx]) - 1);
1867 snprintf(last_battery_time_str[idx],
1868 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1871 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1872 if (present_rate > 0) {
1873 /* e.g. discharging 35% */
1874 snprintf(last_battery_str[idx],
1875 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1876 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1878 format_seconds(last_battery_time_str[idx],
1879 sizeof(last_battery_time_str[idx]) - 1,
1880 (long) ((remaining_capacity * 3600) / present_rate));
1881 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1882 snprintf(last_battery_str[idx],
1883 sizeof(last_battery_str[idx]) - 1, "full");
1884 snprintf(last_battery_time_str[idx],
1885 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1887 snprintf(last_battery_str[idx],
1888 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1889 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1890 snprintf(last_battery_time_str[idx],
1891 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1894 } else if (strncmp(charging_state, "charged", 64) == 0) {
1895 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1896 /* Below happens with the second battery on my X40,
1897 * when the second one is empty and the first one being charged. */
1898 if (remaining_capacity == 0) {
1899 strcpy(last_battery_str[idx], "empty");
1901 strcpy(last_battery_str[idx], "charged");
1903 /* unknown, probably full / AC */
1905 if (strncmp(charging_state, "Full", 64) == 0) {
1906 strncpy(last_battery_str[idx], "full", 64);
1907 } else if (acpi_last_full[idx] != 0
1908 && remaining_capacity != acpi_last_full[idx]) {
1909 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1910 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1912 strncpy(last_battery_str[idx], "AC", 64);
1915 fclose(acpi_bat_fp[idx]);
1916 acpi_bat_fp[idx] = NULL;
1919 if (apm_bat_fp[idx] == NULL) {
1920 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1923 if (apm_bat_fp[idx] != NULL) {
1924 unsigned int ac, status, flag;
1927 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1928 &ac, &status, &flag, &life);
1931 /* could check now that there is ac */
1932 snprintf(last_battery_str[idx], 64, "AC");
1934 /* could check that status == 3 here? */
1935 } else if (ac && life != 100) {
1936 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1938 snprintf(last_battery_str[idx], 64, "%d%%", life);
1941 /* it seemed to buffer it so file must be closed (or could use
1942 * syscalls directly but I don't feel like coding it now) */
1943 fclose(apm_bat_fp[idx]);
1944 apm_bat_fp[idx] = NULL;
1947 set_return_value(buffer, n, item, idx);
1950 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1953 case BATTERY_STATUS:
1954 snprintf(buffer, n, "%s", last_battery_str[idx]);
1957 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1964 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1966 get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1967 if (0 == strncmp("charging", buffer, 8)) {
1969 memmove(buffer + 1, buffer + 8, n - 8);
1970 } else if (0 == strncmp("discharging", buffer, 11)) {
1972 memmove(buffer + 1, buffer + 11, n - 11);
1973 } else if (0 == strncmp("charged", buffer, 7)) {
1975 memmove(buffer + 1, buffer + 7, n - 7);
1976 } else if (0 == strncmp("not present", buffer, 11)) {
1978 memmove(buffer + 1, buffer + 11, n - 11);
1979 } else if (0 == strncmp("empty", buffer, 5)) {
1981 memmove(buffer + 1, buffer + 5, n - 5);
1982 } else if (0 != strncmp("AC", buffer, 2)) {
1984 memmove(buffer + 1, buffer + 11, n - 11);
1988 int get_battery_perct(const char *bat)
1992 char acpi_path[128];
1993 char sysfs_path[128];
1994 int remaining_capacity = -1;
1996 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1997 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
2001 idx = get_battery_idx(bat);
2003 /* don't update battery too often */
2004 if (current_update_time - last_battery_perct_time[idx] < 30) {
2005 return last_battery_perct[idx];
2007 last_battery_perct_time[idx] = current_update_time;
2009 /* Only check for SYSFS or ACPI */
2011 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
2012 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
2016 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
2017 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
2020 if (sysfs_bat_fp[idx] != NULL) {
2022 while (!feof(sysfs_bat_fp[idx])) {
2024 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
2027 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
2028 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
2029 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
2030 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
2031 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
2032 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
2033 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
2034 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
2038 fclose(sysfs_bat_fp[idx]);
2039 sysfs_bat_fp[idx] = NULL;
2041 } else if (acpi_bat_fp[idx] != NULL) {
2043 /* read last full capacity if it's zero */
2044 if (acpi_design_capacity[idx] == 0) {
2049 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
2050 fp = open_file(path, &rep2);
2055 if (fgets(b, 256, fp) == NULL) {
2058 if (sscanf(b, "last full capacity: %d",
2059 &acpi_design_capacity[idx]) != 0) {
2067 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
2069 while (!feof(acpi_bat_fp[idx])) {
2072 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
2076 if (buf[0] == 'r') {
2077 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
2081 if (remaining_capacity < 0) {
2084 /* compute the battery percentage */
2085 last_battery_perct[idx] =
2086 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
2087 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
2088 return last_battery_perct[idx];
2091 int get_battery_perct_bar(const char *bar)
2095 get_battery_perct(bar);
2096 idx = get_battery_idx(bar);
2097 return (int) (last_battery_perct[idx] * 2.56 - 1);
2100 /* On Apple powerbook and ibook:
2101 $ cat /proc/pmu/battery_0
2108 $ cat /proc/pmu/info
2109 PMU driver version : 2
2110 PMU firmware version : 0c
2115 /* defines as in <linux/pmu.h> */
2116 #define PMU_BATT_PRESENT 0x00000001
2117 #define PMU_BATT_CHARGING 0x00000002
2119 static FILE *pmu_battery_fp;
2120 static FILE *pmu_info_fp;
2121 static char pb_battery_info[3][32];
2122 static double pb_battery_info_update;
2124 #define PMU_PATH "/proc/pmu"
2125 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2128 const char *batt_path = PMU_PATH "/battery_0";
2129 const char *info_path = PMU_PATH "/info";
2131 int charge, max_charge, ac = -1;
2134 /* don't update battery too often */
2135 if (current_update_time - pb_battery_info_update < 29.5) {
2136 snprintf(buffer, n, "%s", pb_battery_info[i]);
2139 pb_battery_info_update = current_update_time;
2141 if (pmu_battery_fp == NULL) {
2142 pmu_battery_fp = open_file(batt_path, &rep);
2143 if (pmu_battery_fp == NULL) {
2148 if (pmu_battery_fp != NULL) {
2149 rewind(pmu_battery_fp);
2150 while (!feof(pmu_battery_fp)) {
2153 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2157 if (buf[0] == 'f') {
2158 sscanf(buf, "flags : %8x", &flags);
2159 } else if (buf[0] == 'c' && buf[1] == 'h') {
2160 sscanf(buf, "charge : %d", &charge);
2161 } else if (buf[0] == 'm') {
2162 sscanf(buf, "max_charge : %d", &max_charge);
2163 } else if (buf[0] == 't') {
2164 sscanf(buf, "time rem. : %ld", &timeval);
2168 if (pmu_info_fp == NULL) {
2169 pmu_info_fp = open_file(info_path, &rep);
2170 if (pmu_info_fp == NULL) {
2175 if (pmu_info_fp != NULL) {
2176 rewind(pmu_info_fp);
2177 while (!feof(pmu_info_fp)) {
2180 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2183 if (buf[0] == 'A') {
2184 sscanf(buf, "AC Power : %d", &ac);
2188 /* update status string */
2189 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2190 strncpy(pb_battery_info[PB_BATT_STATUS], "AC", sizeof(pb_battery_info[PB_BATT_STATUS]));
2191 } else if (ac && (flags & PMU_BATT_PRESENT)
2192 && !(flags & PMU_BATT_CHARGING)) {
2193 strncpy(pb_battery_info[PB_BATT_STATUS], "charged", sizeof(pb_battery_info[PB_BATT_STATUS]));
2194 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2195 strncpy(pb_battery_info[PB_BATT_STATUS], "charging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2197 strncpy(pb_battery_info[PB_BATT_STATUS], "discharging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2200 /* update percentage string */
2201 if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT)
2202 && !(flags & PMU_BATT_CHARGING)) {
2203 snprintf(pb_battery_info[PB_BATT_PERCENT],
2204 sizeof(pb_battery_info[PB_BATT_PERCENT]), "100%%");
2205 } else if (timeval == 0) {
2206 snprintf(pb_battery_info[PB_BATT_PERCENT],
2207 sizeof(pb_battery_info[PB_BATT_PERCENT]), "unknown");
2209 snprintf(pb_battery_info[PB_BATT_PERCENT],
2210 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2211 (charge * 100) / max_charge);
2214 /* update time string */
2215 if (timeval == 0) { /* fully charged or battery not present */
2216 snprintf(pb_battery_info[PB_BATT_TIME],
2217 sizeof(pb_battery_info[PB_BATT_TIME]), "unknown");
2218 } else if (timeval < 60 * 60) { /* don't show secs */
2219 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2220 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2222 format_seconds(pb_battery_info[PB_BATT_TIME],
2223 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2226 snprintf(buffer, n, "%s", pb_battery_info[i]);
2229 void update_top(void)
2231 process_find_top(info.cpu, info.memu, info.time
2236 info.first_process = get_first_process();
2239 void update_entropy(void)
2242 const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2243 const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2246 info.entropy.entropy_avail = 0;
2247 info.entropy.poolsize = 0;
2249 if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2253 if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2258 fscanf(fp1, "%u", &info.entropy.entropy_avail);
2259 fscanf(fp2, "%u", &info.entropy.poolsize);
2265 const char *get_disk_protect_queue(const char *disk)
2271 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2272 if (access(path, F_OK)) {
2273 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2275 if ((fp = fopen(path, "r")) == NULL)
2277 if (fscanf(fp, "%d\n", &state) != 1) {
2282 return (state > 0) ? "frozen" : "free ";
2285 void update_diskio(void)
2289 char buf[512], devbuf[64];
2290 unsigned int major, minor;
2292 struct diskio_stat *cur;
2293 unsigned int reads, writes;
2294 unsigned int total_reads = 0, total_writes = 0;
2297 stats.current_read = 0;
2298 stats.current_write = 0;
2300 if (!(fp = open_file("/proc/diskstats", &rep))) {
2304 /* read reads and writes from all disks (minor = 0), including cd-roms
2305 * and floppies, and sum them up */
2306 while (fgets(buf, 512, fp)) {
2307 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
2308 &minor, devbuf, &reads, &writes);
2309 /* ignore subdevices (they have only 3 matching entries in their line)
2310 * and virtual devices (LVM, network block devices, RAM disks, Loopback)
2312 * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
2313 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
2314 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
2315 total_reads += reads;
2316 total_writes += writes;
2318 col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
2319 &major, &minor, devbuf, &reads, &writes);
2320 if (col_count != 5) {
2325 while (cur && strcmp(devbuf, cur->dev))
2329 update_diskio_values(cur, reads, writes);
2331 update_diskio_values(&stats, total_reads, total_writes);