1 /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
2 * vim: ts=4 sw=4 noet ai cindent syntax=c
4 * Conky, a system monitor, based on torsmo
6 * Any original torsmo code is licensed under the BSD license
8 * All code written since the fork of torsmo is licensed under the GPL
10 * Please see COPYING for details
12 * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
13 * Copyright (c) 2007 Toni Spets
14 * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
16 * All rights reserved.
18 * This program is free software: you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation, either version 3 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
38 #include "temphelper.h"
43 #include <sys/types.h>
44 #include <sys/sysinfo.h>
46 #ifndef HAVE_CLOCK_GETTIME
51 // #include <assert.h>
55 #include <sys/ioctl.h>
56 #include <sys/socket.h>
57 #include <netinet/in.h>
58 #include <linux/sockios.h>
60 #include <arpa/inet.h>
64 #include <linux/route.h>
67 /* The following ifdefs were adapted from gkrellm */
68 #include <linux/major.h>
70 #if !defined(MD_MAJOR)
74 #if !defined(LVM_BLK_MAJOR)
75 #define LVM_BLK_MAJOR 58
78 #if !defined(NBD_MAJOR)
94 #define SHORTSTAT_TEMPL "%*s %llu %llu %llu"
95 #define LONGSTAT_TEMPL "%*s %llu %llu %llu "
97 /* This flag tells the linux routines to use the /proc system where possible,
98 * even if other api's are available, e.g. sysinfo() or getloadavg().
99 * the reason for this is to allow for /proc-based distributed monitoring.
100 * using a flag in this manner creates less confusing code. */
101 static int prefer_proc = 0;
103 void prepare_update(void)
107 void update_uptime(void)
111 struct sysinfo s_info;
114 info.uptime = (double) s_info.uptime;
121 if (!(fp = open_file("/proc/uptime", &rep))) {
125 fscanf(fp, "%lf", &info.uptime);
130 int check_mount(char *s)
133 FILE *mtab = fopen("/etc/mtab", "r");
136 char buf1[256], buf2[128];
138 while (fgets(buf1, 256, mtab)) {
139 sscanf(buf1, "%*s %128s", buf2);
140 if (!strcmp(s, buf2)) {
147 NORM_ERR("Could not open mtab");
152 /* these things are also in sysinfo except Buffers:
153 * (that's why I'm reading them from proc) */
155 void update_meminfo(void)
160 /* unsigned int a; */
163 info.mem = info.memmax = info.swap = info.swapfree = info.swapmax = info.bufmem =
164 info.buffers = info.cached = info.memfree = info.memeasyfree = 0;
166 if (!(meminfo_fp = open_file("/proc/meminfo", &rep))) {
170 while (!feof(meminfo_fp)) {
171 if (fgets(buf, 255, meminfo_fp) == NULL) {
175 if (strncmp(buf, "MemTotal:", 9) == 0) {
176 sscanf(buf, "%*s %llu", &info.memmax);
177 } else if (strncmp(buf, "MemFree:", 8) == 0) {
178 sscanf(buf, "%*s %llu", &info.memfree);
179 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
180 sscanf(buf, "%*s %llu", &info.swapmax);
181 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
182 sscanf(buf, "%*s %llu", &info.swapfree);
183 } else if (strncmp(buf, "Buffers:", 8) == 0) {
184 sscanf(buf, "%*s %llu", &info.buffers);
185 } else if (strncmp(buf, "Cached:", 7) == 0) {
186 sscanf(buf, "%*s %llu", &info.cached);
190 info.mem = info.memmax - info.memfree;
191 info.memeasyfree = info.memfree;
192 info.swap = info.swapmax - info.swapfree;
194 info.bufmem = info.cached + info.buffers;
199 int get_laptop_mode(void)
204 if ((fp = fopen("/proc/sys/vm/laptop_mode", "r")) != NULL)
205 fscanf(fp, "%d\n", &val);
211 * # cat /sys/block/sda/queue/scheduler
212 * noop [anticipatory] cfq
214 char *get_ioscheduler(char *disk)
220 return strndup("n/a", text_buffer_size);
222 snprintf(buf, 127, "/sys/block/%s/queue/scheduler", disk);
223 if ((fp = fopen(buf, "r")) == NULL) {
224 return strndup("n/a", text_buffer_size);
227 fscanf(fp, "%127s", buf);
229 buf[strlen(buf) - 1] = '\0';
231 return strndup(buf + 1, text_buffer_size);
235 return strndup("n/a", text_buffer_size);
244 #define COND_FREE(x) if(x) free(x); x = 0
245 #define SAVE_SET_STRING(x, y) \
246 if (x && strcmp((char *)x, (char *)y)) { \
248 x = strndup("multiple", text_buffer_size); \
250 x = strndup(y, text_buffer_size); \
253 void update_gateway_info_failure(const char *reason)
258 //2 pointers to 1 location causes a crash when we try to free them both
259 gw_info.iface = strndup("failed", text_buffer_size);
260 gw_info.ip = strndup("failed", text_buffer_size);
264 /* Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT */
265 #define RT_ENTRY_FORMAT "%63s %lx %lx %x %*d %*d %*d %lx %*d %*d %*d\n"
267 void update_gateway_info(void)
272 unsigned long dest, gate, mask;
275 COND_FREE(gw_info.iface);
276 COND_FREE(gw_info.ip);
279 if ((fp = fopen("/proc/net/route", "r")) == NULL) {
280 update_gateway_info_failure("fopen()");
284 /* skip over the table header line, which is always present */
285 fscanf(fp, "%*[^\n]\n");
288 if(fscanf(fp, RT_ENTRY_FORMAT,
289 iface, &dest, &gate, &flags, &mask) != 5) {
290 update_gateway_info_failure("fscanf()");
293 if (!(dest || mask) && ((flags & RTF_GATEWAY) || !gate) ) {
295 SAVE_SET_STRING(gw_info.iface, iface)
297 SAVE_SET_STRING(gw_info.ip, inet_ntoa(ina))
304 void free_gateway_info(void)
310 memset(&gw_info, 0, sizeof(gw_info));
313 int gateway_exists(void)
315 return !!gw_info.count;
318 void print_gateway_iface(char *p, int p_max_size)
320 snprintf(p, p_max_size, "%s", gw_info.iface);
323 void print_gateway_ip(char *p, int p_max_size)
325 snprintf(p, p_max_size, "%s", gw_info.ip);
328 void update_net_stats(void)
332 static char first = 1;
334 // FIXME: arbitrary size chosen to keep code simple.
336 unsigned int curtmp1, curtmp2;
343 // wireless info variables
344 int skfd, has_bitrate = 0;
345 struct wireless_info *winfo;
350 delta = current_update_time - last_update_time;
351 if (delta <= 0.0001) {
355 /* open file and ignore first two lines */
356 if (!(net_dev_fp = open_file("/proc/net/dev", &rep))) {
361 fgets(buf, 255, net_dev_fp); /* garbage */
362 fgets(buf, 255, net_dev_fp); /* garbage (field names) */
364 /* read each interface */
365 for (i2 = 0; i2 < MAX_NET_INTERFACES; i2++) {
369 long long r, t, last_recv, last_trans;
371 if (fgets(buf, 255, net_dev_fp) == NULL) {
375 while (isspace((int) *p)) {
381 while (*p && *p != ':') {
390 ns = get_net_stat(s, NULL, NULL);
392 memset(&(ns->addr.sa_data), 0, 14);
394 memset(ns->addrs, 0, 17 * MAX_NET_INTERFACES + 1); /* Up to 17 chars per ip, max MAX_NET_INTERFACES interfaces. Nasty memory usage... */
396 last_recv = ns->recv;
397 last_trans = ns->trans;
399 /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
400 sscanf(p, "%lld %*d %*d %*d %*d %*d %*d %*d %lld",
403 /* if recv or trans is less than last time, an overflow happened */
404 if (r < ns->last_read_recv) {
407 ns->recv += (r - ns->last_read_recv);
409 ns->last_read_recv = r;
411 if (t < ns->last_read_trans) {
414 ns->trans += (t - ns->last_read_trans);
416 ns->last_read_trans = t;
418 /*** ip addr patch ***/
419 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
421 conf.ifc_buf = malloc(sizeof(struct ifreq) * MAX_NET_INTERFACES);
422 conf.ifc_len = sizeof(struct ifreq) * MAX_NET_INTERFACES;
423 memset(conf.ifc_buf, 0, conf.ifc_len);
425 ioctl((long) i, SIOCGIFCONF, &conf);
427 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
428 struct net_stat *ns2;
430 if (!(((struct ifreq *) conf.ifc_buf) + k))
434 ((struct ifreq *) conf.ifc_buf)[k].ifr_ifrn.ifrn_name, NULL, NULL);
435 ns2->addr = ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.ifru_addr;
436 sprintf(temp_addr, "%u.%u.%u.%u, ",
437 ns2->addr.sa_data[2] & 255,
438 ns2->addr.sa_data[3] & 255,
439 ns2->addr.sa_data[4] & 255,
440 ns2->addr.sa_data[5] & 255);
441 if(NULL == strstr(ns2->addrs, temp_addr))
442 strncpy(ns2->addrs + strlen(ns2->addrs), temp_addr, 17);
449 /*** end ip addr patch ***/
452 /* calculate speeds */
453 ns->net_rec[0] = (ns->recv - last_recv) / delta;
454 ns->net_trans[0] = (ns->trans - last_trans) / delta;
461 #pragma omp parallel for reduction(+:curtmp1, curtmp2) schedule(dynamic,10)
462 #endif /* HAVE_OPENMP */
463 for (i = 0; i < info.net_avg_samples; i++) {
464 curtmp1 = curtmp1 + ns->net_rec[i];
465 curtmp2 = curtmp2 + ns->net_trans[i];
473 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
474 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
475 if (info.net_avg_samples > 1) {
477 #pragma omp parallel for schedule(dynamic,10)
478 #endif /* HAVE_OPENMP */
479 for (i = info.net_avg_samples; i > 1; i--) {
480 ns->net_rec[i - 1] = ns->net_rec[i - 2];
481 ns->net_trans[i - 1] = ns->net_trans[i - 2];
486 /* update wireless info */
487 winfo = malloc(sizeof(struct wireless_info));
488 memset(winfo, 0, sizeof(struct wireless_info));
490 skfd = iw_sockets_open();
491 if (iw_get_basic_config(skfd, s, &(winfo->b)) > -1) {
493 // set present winfo variables
494 if (iw_get_stats(skfd, s, &(winfo->stats),
495 &winfo->range, winfo->has_range) >= 0) {
496 winfo->has_stats = 1;
498 if (iw_get_range_info(skfd, s, &(winfo->range)) >= 0) {
499 winfo->has_range = 1;
501 if (iw_get_ext(skfd, s, SIOCGIWAP, &wrq) >= 0) {
502 winfo->has_ap_addr = 1;
503 memcpy(&(winfo->ap_addr), &(wrq.u.ap_addr), sizeof(sockaddr));
507 if (iw_get_ext(skfd, s, SIOCGIWRATE, &wrq) >= 0) {
508 memcpy(&(winfo->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
509 iw_print_bitrate(ns->bitrate, 16, winfo->bitrate.value);
514 if (winfo->has_range && winfo->has_stats
515 && ((winfo->stats.qual.level != 0)
516 || (winfo->stats.qual.updated & IW_QUAL_DBM))) {
517 if (!(winfo->stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
518 ns->link_qual = winfo->stats.qual.qual;
519 ns->link_qual_max = winfo->range.max_qual.qual;
524 if (winfo->has_ap_addr) {
525 iw_sawap_ntop(&winfo->ap_addr, ns->ap);
529 if (winfo->b.has_essid) {
530 if (winfo->b.essid_on) {
531 snprintf(ns->essid, 32, "%s", winfo->b.essid);
533 snprintf(ns->essid, 32, "off/any");
537 snprintf(ns->mode, 16, "%s", iw_operation_mode[winfo->b.mode]);
539 iw_sockets_close(skfd);
550 void update_total_processes(void)
553 struct dirent *entry;
558 if (!(dir = opendir("/proc"))) {
561 while ((entry = readdir(dir))) {
563 /* Problem reading list of processes */
568 if (sscanf(entry->d_name, "%d%c", &ignore1, &ignore2) == 1) {
575 void update_threads(void)
579 struct sysinfo s_info;
582 info.threads = s_info.procs;
589 if (!(fp = open_file("/proc/loadavg", &rep))) {
593 fscanf(fp, "%*f %*f %*f %*d/%hu", &info.threads);
598 #define CPU_SAMPLE_COUNT 15
600 unsigned long long cpu_user;
601 unsigned long long cpu_system;
602 unsigned long long cpu_nice;
603 unsigned long long cpu_idle;
604 unsigned long long cpu_iowait;
605 unsigned long long cpu_irq;
606 unsigned long long cpu_softirq;
607 unsigned long long cpu_steal;
608 unsigned long long cpu_total;
609 unsigned long long cpu_active_total;
610 unsigned long long cpu_last_total;
611 unsigned long long cpu_last_active_total;
612 double cpu_val[CPU_SAMPLE_COUNT];
614 static short cpu_setup = 0;
616 /* Determine if this kernel gives us "extended" statistics information in
618 * Kernels around 2.5 and earlier only reported user, system, nice, and
619 * idle values in proc stat.
620 * Kernels around 2.6 and greater report these PLUS iowait, irq, softirq,
622 void determine_longstat(char *buf)
624 unsigned long long iowait = 0;
626 KFLAG_SETOFF(KFLAG_IS_LONGSTAT);
627 /* scanf will either return -1 or 1 because there is only 1 assignment */
628 if (sscanf(buf, "%*s %*d %*d %*d %*d %llu", &iowait) > 0) {
629 KFLAG_SETON(KFLAG_IS_LONGSTAT);
633 void get_cpu_count(void)
639 if (info.cpu_usage) {
643 if (!(stat_fp = open_file("/proc/stat", &rep))) {
649 while (!feof(stat_fp)) {
650 if (fgets(buf, 255, stat_fp) == NULL) {
654 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
655 if (info.cpu_count == 0) {
656 determine_longstat(buf);
661 info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
666 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
667 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
669 inline static void update_stat(void)
673 static struct cpu_info *cpu = NULL;
678 const char *stat_template = NULL;
679 unsigned int malloc_cpu_size = 0;
680 extern void* global_cpu;
681 static double last_stat_update = 0.0;
683 /* since we use wrappers for this function, the update machinery
684 * can't eliminate double invocations of this function. Check for
685 * them here, otherwise cpu_usage counters are freaking out. */
686 if (last_stat_update == current_update_time)
688 last_stat_update = current_update_time;
690 /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
691 if (!cpu_setup || !info.cpu_usage) {
696 if (!stat_template) {
698 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
702 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
703 cpu = malloc(malloc_cpu_size);
704 memset(cpu, 0, malloc_cpu_size);
708 if (!(stat_fp = open_file("/proc/stat", &rep))) {
710 if (info.cpu_usage) {
711 memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
717 while (!feof(stat_fp)) {
718 if (fgets(buf, 255, stat_fp) == NULL) {
722 if (strncmp(buf, "procs_running ", 14) == 0) {
723 sscanf(buf, "%*s %hu", &info.run_procs);
724 } else if (strncmp(buf, "cpu", 3) == 0) {
726 if (isdigit(buf[3])) {
727 idx = atoi(&buf[3]) + 1;
731 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
732 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
733 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
734 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
735 &(cpu[idx].cpu_steal));
737 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
738 cpu[idx].cpu_system + cpu[idx].cpu_idle +
739 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
740 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
742 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
743 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
745 delta = current_update_time - last_update_time;
747 if (delta <= 0.001) {
751 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
752 cpu[idx].cpu_last_active_total) /
753 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
756 #pragma omp parallel for reduction(+:curtmp) schedule(dynamic,10)
757 #endif /* HAVE_OPENMP */
758 for (i = 0; i < info.cpu_avg_samples; i++) {
759 curtmp = curtmp + cpu[idx].cpu_val[i];
761 /* TESTING -- I've removed this, because I don't think it is right.
762 * You shouldn't divide by the cpu count here ...
763 * removing for testing */
765 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
768 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
770 /* TESTING -- this line replaces the prev. "suspect" if/else */
771 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
773 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
774 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
776 #pragma omp parallel for schedule(dynamic,10)
777 #endif /* HAVE_OPENMP */
778 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
779 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
786 void update_running_processes(void)
791 void update_cpu_usage(void)
796 void update_load_average(void)
798 #ifdef HAVE_GETLOADAVG
803 info.loadavg[0] = (float) v[0];
804 info.loadavg[1] = (float) v[1];
805 info.loadavg[2] = (float) v[2];
812 if (!(fp = open_file("/proc/loadavg", &rep))) {
813 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
816 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
822 #define PROC_I8K "/proc/i8k"
823 #define I8K_DELIM " "
824 static char *i8k_procbuf = NULL;
825 void update_i8k(void)
830 i8k_procbuf = (char *) malloc(128 * sizeof(char));
832 if ((fp = fopen(PROC_I8K, "r")) == NULL) {
833 CRIT_ERR(NULL, NULL, "/proc/i8k doesn't exist! use insmod to make sure the kernel "
834 "driver is loaded...");
837 memset(&i8k_procbuf[0], 0, 128);
838 if (fread(&i8k_procbuf[0], sizeof(char), 128, fp) == 0) {
839 NORM_ERR("something wrong with /proc/i8k...");
844 i8k.version = strtok(&i8k_procbuf[0], I8K_DELIM);
845 i8k.bios = strtok(NULL, I8K_DELIM);
846 i8k.serial = strtok(NULL, I8K_DELIM);
847 i8k.cpu_temp = strtok(NULL, I8K_DELIM);
848 i8k.left_fan_status = strtok(NULL, I8K_DELIM);
849 i8k.right_fan_status = strtok(NULL, I8K_DELIM);
850 i8k.left_fan_rpm = strtok(NULL, I8K_DELIM);
851 i8k.right_fan_rpm = strtok(NULL, I8K_DELIM);
852 i8k.ac_status = strtok(NULL, I8K_DELIM);
853 i8k.buttons_status = strtok(NULL, I8K_DELIM);
856 /***********************************************************/
857 /***********************************************************/
858 /***********************************************************/
860 static int no_dots(const struct dirent *d)
862 if (d->d_name[0] == '.') {
868 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
870 struct dirent **namelist;
873 n = scandir(dir, &namelist, no_dots, alphasort);
876 NORM_ERR("scandir for %s: %s", dir, strerror(errno));
887 strncpy(s, namelist[0]->d_name, 255);
891 #pragma omp parallel for schedule(dynamic,10)
892 #endif /* HAVE_OPENMP */
893 for (i = 0; i < n; i++) {
902 static int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
903 int *divisor, char *devtype)
910 memset(buf, 0, sizeof(buf));
912 /* if device is NULL or *, get first */
913 if (dev == NULL || strcmp(dev, "*") == 0) {
916 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
922 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
924 /* buf holds result from get_first_file_in_a_directory() above,
925 * e.g. "hwmon0" -- append "/device" */
926 strcat(buf, "/device");
928 /* dev holds device number N as a string,
929 * e.g. "0", -- convert to "hwmon0/device" */
930 sprintf(buf, "hwmon%s/device", dev);
935 /* change vol to in, tempf to temp */
936 if (strcmp(type, "vol") == 0) {
938 } else if (strcmp(type, "tempf") == 0) {
943 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
945 /* first, attempt to open file in /device */
946 fd = open(path, O_RDONLY);
949 /* if it fails, strip the /device from dev and attempt again */
950 buf[strlen(buf) - 7] = 0;
951 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
952 fd = open(path, O_RDONLY);
954 CRIT_ERR(NULL, NULL, "can't open '%s': %s\nplease check your device or remove this "
955 "var from "PACKAGE_NAME, path, strerror(errno));
959 strncpy(devtype, path, 255);
961 if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
962 || strcmp(type, "tempf") == 0) {
967 /* fan does not use *_div as a read divisor */
968 if (strcmp("fan", type) == 0) {
972 /* test if *_div file exist, open it and use it as divisor */
973 if (strcmp(type, "tempf") == 0) {
974 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
976 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
979 divfd = open(path, O_RDONLY);
985 divn = read(divfd, divbuf, 63);
986 /* should read until n == 0 but I doubt that kernel will give these
987 * in multiple pieces. :) */
989 NORM_ERR("open_sysfs_sensor(): can't read from sysfs");
992 *divisor = atoi(divbuf);
1000 static double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
1008 lseek(*fd, 0, SEEK_SET);
1014 n = read(*fd, buf, 63);
1015 /* should read until n == 0 but I doubt that kernel will give these
1016 * in multiple pieces. :) */
1018 NORM_ERR("get_sysfs_info(): read from %s failed\n", devtype);
1027 *fd = open(devtype, O_RDONLY);
1029 NORM_ERR("can't open '%s': %s", devtype, strerror(errno));
1032 /* My dirty hack for computing CPU value
1033 * Filedil, from forums.gentoo.org */
1034 /* if (strstr(devtype, "temp1_input") != NULL) {
1035 return -15.096 + 1.4893 * (val / 1000.0);
1038 /* divide voltage and temperature by 1000 */
1039 /* or if any other divisor is given, use that */
1040 if (strcmp(type, "tempf") == 0) {
1042 return ((val / divisor + 40) * 9.0 / 5) - 40;
1043 } else if (divisor) {
1044 return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
1046 return ((val + 40) * 9.0 / 5) - 40;
1050 return val / divisor;
1051 } else if (divisor) {
1052 return val / 1000.0;
1059 #define HWMON_RESET() {\
1064 static void parse_sysfs_sensor(struct text_object *obj, const char *arg, const char *path, const char *type)
1066 char buf1[64], buf2[64];
1067 float factor, offset;
1071 if (sscanf(arg, "%63s %d %f %f", buf2, &n, &factor, &offset) == 4) found = 1; else HWMON_RESET();
1072 if (!found && sscanf(arg, "%63s %63s %d %f %f", buf1, buf2, &n, &factor, &offset) == 5) found = 1; else if (!found) HWMON_RESET();
1073 if (!found && sscanf(arg, "%63s %63s %d", buf1, buf2, &n) == 3) found = 1; else if (!found) HWMON_RESET();
1074 if (!found && sscanf(arg, "%63s %d", buf2, &n) == 2) found = 1; else if (!found) HWMON_RESET();
1077 NORM_ERR("i2c failed to parse arguments");
1078 obj->type = OBJ_text;
1081 DBGP("parsed %s args: '%s' '%s' %d %f %f\n", type, buf1, buf2, n, factor, offset);
1082 sf = malloc(sizeof(struct sysfs));
1083 memset(sf, 0, sizeof(struct sysfs));
1084 sf->fd = open_sysfs_sensor(path, (*buf1) ? buf1 : 0, buf2, n,
1085 &sf->arg, sf->devtype);
1086 strncpy(sf->type, buf2, 63);
1087 sf->factor = factor;
1088 sf->offset = offset;
1089 obj->data.opaque = sf;
1092 #define PARSER_GENERATOR(name, path) \
1093 void parse_##name##_sensor(struct text_object *obj, const char *arg) \
1095 parse_sysfs_sensor(obj, arg, path, #name); \
1098 PARSER_GENERATOR(i2c, "/sys/bus/i2c/devices/")
1099 PARSER_GENERATOR(hwmon, "/sys/class/hwmon/")
1100 PARSER_GENERATOR(platform, "/sys/bus/platform/devices/")
1102 void print_sysfs_sensor(struct text_object *obj, char *p, int p_max_size)
1105 struct sysfs *sf = obj->data.opaque;
1110 r = get_sysfs_info(&sf->fd, sf->arg,
1111 sf->devtype, sf->type);
1113 r = r * sf->factor + sf->offset;
1115 if (!strncmp(sf->type, "temp", 4)) {
1116 temp_print(p, p_max_size, r, TEMP_CELSIUS);
1117 } else if (r >= 100.0 || r == 0) {
1118 snprintf(p, p_max_size, "%d", (int) r);
1120 snprintf(p, p_max_size, "%.1f", r);
1124 void free_sysfs_sensor(struct text_object *obj)
1126 struct sysfs *sf = obj->data.opaque;
1132 free(obj->data.opaque);
1133 obj->data.opaque = NULL;
1136 /* Prior to kernel version 2.6.12, the CPU fan speed was available in
1137 * ADT746X_FAN_OLD, whereas later kernel versions provide this information in
1139 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
1140 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
1142 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
1145 char adt746x_fan_state[64];
1148 if (!p_client_buffer || client_buffer_size <= 0) {
1152 if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
1153 && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL) {
1154 sprintf(adt746x_fan_state, "adt746x not found");
1156 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
1157 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
1161 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_fan_state);
1164 /* Prior to kernel version 2.6.12, the CPU temperature was found in
1165 * ADT746X_CPU_OLD, whereas later kernel versions provide this information in
1167 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
1168 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
1170 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
1173 char adt746x_cpu_state[64];
1176 if (!p_client_buffer || client_buffer_size <= 0) {
1180 if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
1181 && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL) {
1182 sprintf(adt746x_cpu_state, "adt746x not found");
1184 fscanf(fp, "%2s", adt746x_cpu_state);
1188 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state);
1191 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1192 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1194 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1195 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1196 const char *p_format, int divisor, unsigned int cpu)
1204 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1210 char current_freq_file[128];
1212 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1214 f = fopen(current_freq_file, "r");
1216 /* if there's a cpufreq /sys node, read the current frequency from
1217 * this node and divide by 1000 to get Mhz. */
1218 if (fgets(s, sizeof(s), f)) {
1219 s[strlen(s) - 1] = '\0';
1220 freq = strtod(s, NULL);
1223 snprintf(p_client_buffer, client_buffer_size, p_format,
1224 (freq / 1000) / divisor);
1229 // open the CPU information file
1230 f = open_file("/proc/cpuinfo", &rep);
1232 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1237 while (fgets(s, sizeof(s), f) != NULL) {
1239 #if defined(__i386) || defined(__x86_64)
1240 // and search for the cpu mhz
1241 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1243 #if defined(__alpha)
1244 // different on alpha
1245 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1247 // this is different on ppc for some reason
1248 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1249 #endif // defined(__alpha)
1250 #endif // defined(__i386) || defined(__x86_64)
1252 // copy just the number
1253 strcpy(frequency, strchr(s, ':') + 2);
1254 #if defined(__alpha)
1256 frequency[strlen(frequency) - 6] = '\0';
1257 // kernel reports in Hz
1258 freq = strtod(frequency, NULL) / 1000000;
1261 frequency[strlen(frequency) - 1] = '\0';
1262 freq = strtod(frequency, NULL);
1266 if (strncmp(s, "processor", 9) == 0) {
1273 snprintf(p_client_buffer, client_buffer_size, p_format,
1274 (float) freq / divisor);
1278 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1280 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1290 * Peter Tarjan (ptarjan@citromail.hu) */
1292 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1293 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1294 const char *p_format, int divisor, unsigned int cpu)
1300 char current_freq_file[128];
1303 /* build the voltage file name */
1305 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1308 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1313 /* read the current cpu frequency from the /sys node */
1314 f = fopen(current_freq_file, "r");
1316 if (fgets(s, sizeof(s), f)) {
1317 s[strlen(s) - 1] = '\0';
1318 freq = strtod(s, NULL);
1322 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1323 perror("get_voltage()");
1330 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1333 /* use the current cpu frequency to find the corresponding voltage */
1334 f = fopen(current_freq_file, "r");
1340 if (fgets(line, 255, f) == NULL) {
1343 sscanf(line, "%d %d", &freq_comp, &voltage);
1344 if (freq_comp == freq) {
1350 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1351 perror("get_voltage()");
1357 snprintf(p_client_buffer, client_buffer_size, p_format,
1358 (float) voltage / divisor);
1362 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1364 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1371 if (!p_client_buffer || client_buffer_size <= 0) {
1375 /* yeah, slow... :/ */
1376 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1377 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1381 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1383 fp = open_file(buf2, &rep);
1385 snprintf(p_client_buffer, client_buffer_size,
1386 "can't open fan's state file");
1389 memset(buf, 0, sizeof(buf));
1390 fscanf(fp, "%*s %99s", buf);
1393 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1396 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1397 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1398 /* Linux 2.6.25 onwards ac adapter info is in
1399 /sys/class/power_supply/AC/
1400 On my system I get the following.
1401 /sys/class/power_supply/AC/uevent:
1402 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1405 POWER_SUPPLY_NAME=AC
1406 POWER_SUPPLY_TYPE=Mains
1407 POWER_SUPPLY_ONLINE=1
1410 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1418 if (!p_client_buffer || client_buffer_size <= 0) {
1422 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1423 fp = open_file(buf2, &rep);
1425 /* sysfs processing */
1427 if (fgets(buf, sizeof(buf), fp) == NULL)
1430 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1432 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1433 snprintf(p_client_buffer, client_buffer_size,
1434 "%s-line", (online ? "on" : "off"));
1440 /* yeah, slow... :/ */
1441 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1442 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1446 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1448 fp = open_file(buf2, &rep);
1450 snprintf(p_client_buffer, client_buffer_size,
1451 "No ac adapter found.... where is it?");
1454 memset(buf, 0, sizeof(buf));
1455 fscanf(fp, "%*s %99s", buf);
1458 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1463 /proc/acpi/thermal_zone/THRM/cooling_mode
1464 cooling mode: active
1465 /proc/acpi/thermal_zone/THRM/polling_frequency
1467 /proc/acpi/thermal_zone/THRM/state
1469 /proc/acpi/thermal_zone/THRM/temperature
1471 /proc/acpi/thermal_zone/THRM/trip_points
1473 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1476 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1477 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1479 int open_acpi_temperature(const char *name)
1485 if (name == NULL || strcmp(name, "*") == 0) {
1488 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1494 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1496 fd = open(path, O_RDONLY);
1498 NORM_ERR("can't open '%s': %s", path, strerror(errno));
1504 static double last_acpi_temp;
1505 static double last_acpi_temp_time;
1507 double get_acpi_temperature(int fd)
1513 /* don't update acpi temperature too often */
1514 if (current_update_time - last_acpi_temp_time < 11.32) {
1515 return last_acpi_temp;
1517 last_acpi_temp_time = current_update_time;
1519 /* seek to beginning */
1520 lseek(fd, 0, SEEK_SET);
1527 n = read(fd, buf, 255);
1529 NORM_ERR("can't read fd %d: %s", fd, strerror(errno));
1532 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1536 return last_acpi_temp;
1540 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1542 design capacity: 4400 mAh
1543 last full capacity: 4064 mAh
1544 battery technology: rechargeable
1545 design voltage: 14800 mV
1546 design capacity warning: 300 mAh
1547 design capacity low: 200 mAh
1548 capacity granularity 1: 32 mAh
1549 capacity granularity 2: 32 mAh
1551 serial number: 16922
1557 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1560 charging state: unknown
1562 remaining capacity: 4064 mAh
1563 present voltage: 16608 mV
1567 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1568 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1569 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1570 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1571 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1573 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1574 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1576 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1577 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1580 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1581 Linux 2.6.24 onwards battery info is in
1582 /sys/class/power_supply/BAT0/
1583 On my system I get the following.
1584 /sys/class/power_supply/BAT0/uevent:
1585 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1587 PHYSDEVDRIVER=battery
1588 POWER_SUPPLY_NAME=BAT0
1589 POWER_SUPPLY_TYPE=Battery
1590 POWER_SUPPLY_STATUS=Discharging
1591 POWER_SUPPLY_PRESENT=1
1592 POWER_SUPPLY_TECHNOLOGY=Li-ion
1593 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1594 POWER_SUPPLY_VOLTAGE_NOW=10780000
1595 POWER_SUPPLY_CURRENT_NOW=13970000
1596 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1597 POWER_SUPPLY_ENERGY_FULL=27370000
1598 POWER_SUPPLY_ENERGY_NOW=11810000
1599 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1600 POWER_SUPPLY_MANUFACTURER=Panasonic
1601 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1604 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1605 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1606 #define APM_PATH "/proc/apm"
1607 #define MAX_BATTERY_COUNT 4
1609 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1610 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1611 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1613 static int batteries_initialized = 0;
1614 static char batteries[MAX_BATTERY_COUNT][32];
1616 static int acpi_last_full[MAX_BATTERY_COUNT];
1617 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1619 /* e.g. "charging 75%" */
1620 static char last_battery_str[MAX_BATTERY_COUNT][64];
1622 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1624 static double last_battery_time[MAX_BATTERY_COUNT];
1626 static int last_battery_perct[MAX_BATTERY_COUNT];
1627 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1629 void init_batteries(void)
1633 if (batteries_initialized) {
1637 #pragma omp parallel for schedule(dynamic,10)
1638 #endif /* HAVE_OPENMP */
1639 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1640 batteries[idx][0] = '\0';
1642 batteries_initialized = 1;
1645 int get_battery_idx(const char *bat)
1649 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1650 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1655 /* if not found, enter a new entry */
1656 if (!strlen(batteries[idx])) {
1657 snprintf(batteries[idx], 31, "%s", bat);
1663 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1665 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1667 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1668 char acpi_path[128];
1669 char sysfs_path[128];
1671 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1672 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1676 idx = get_battery_idx(bat);
1678 /* don't update battery too often */
1679 if (current_update_time - last_battery_time[idx] < 29.5) {
1680 set_return_value(buffer, n, item, idx);
1684 last_battery_time[idx] = current_update_time;
1686 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1687 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1689 /* first try SYSFS if that fails try ACPI */
1691 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1692 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1695 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1696 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1699 if (sysfs_bat_fp[idx] != NULL) {
1701 int present_rate = -1;
1702 int remaining_capacity = -1;
1703 char charging_state[64];
1706 strcpy(charging_state, "unknown");
1708 while (!feof(sysfs_bat_fp[idx])) {
1710 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1713 /* let's just hope units are ok */
1714 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1715 strcpy(present, "yes");
1716 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1717 strcpy(present, "no");
1718 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1719 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1720 /* present_rate is not the same as the
1721 current flowing now but it is the same value
1722 which was used in the past. so we continue
1724 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1725 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1726 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1727 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1728 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1729 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1730 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1731 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1732 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1733 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1736 fclose(sysfs_bat_fp[idx]);
1737 sysfs_bat_fp[idx] = NULL;
1739 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1740 if (remaining_capacity > acpi_last_full[idx])
1741 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1744 if (strcmp(present, "No") == 0) {
1745 strncpy(last_battery_str[idx], "not present", 64);
1748 else if (strcmp(charging_state, "Charging") == 0) {
1749 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1750 /* e.g. charging 75% */
1751 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1752 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1754 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1755 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1756 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1757 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
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");
1762 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1763 snprintf(last_battery_time_str[idx],
1764 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1768 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1769 if (present_rate > 0) {
1770 /* e.g. discharging 35% */
1771 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1772 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1774 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1775 (long) (((float) remaining_capacity / present_rate) * 3600));
1776 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1777 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1778 snprintf(last_battery_time_str[idx],
1779 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1781 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1783 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1784 snprintf(last_battery_time_str[idx],
1785 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1789 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1790 else if (strncmp(charging_state, "Charged", 64) == 0 || strncmp(charging_state, "Full", 64) == 0) {
1791 /* Below happens with the second battery on my X40,
1792 * when the second one is empty and the first one
1794 if (remaining_capacity == 0)
1795 strcpy(last_battery_str[idx], "empty");
1797 strcpy(last_battery_str[idx], "charged");
1799 /* unknown, probably full / AC */
1801 if (acpi_last_full[idx] != 0
1802 && remaining_capacity != acpi_last_full[idx])
1803 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1804 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1806 strncpy(last_battery_str[idx], "AC", 64);
1808 } else if (acpi_bat_fp[idx] != NULL) {
1810 int present_rate = -1;
1811 int remaining_capacity = -1;
1812 char charging_state[64];
1815 /* read last full capacity if it's zero */
1816 if (acpi_last_full[idx] == 0) {
1817 static int rep3 = 0;
1821 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1822 fp = open_file(path, &rep3);
1827 if (fgets(b, 256, fp) == NULL) {
1830 if (sscanf(b, "last full capacity: %d",
1831 &acpi_last_full[idx]) != 0) {
1840 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1842 strcpy(charging_state, "unknown");
1844 while (!feof(acpi_bat_fp[idx])) {
1847 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1851 /* let's just hope units are ok */
1852 if (strncmp(buf, "present:", 8) == 0) {
1853 sscanf(buf, "present: %4s", present);
1854 } else if (strncmp(buf, "charging state:", 15) == 0) {
1855 sscanf(buf, "charging state: %63s", charging_state);
1856 } else if (strncmp(buf, "present rate:", 13) == 0) {
1857 sscanf(buf, "present rate: %d", &present_rate);
1858 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1859 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1862 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1863 if (remaining_capacity > acpi_last_full[idx]) {
1864 /* normalize to 100% */
1865 acpi_last_full[idx] = remaining_capacity;
1869 if (strcmp(present, "no") == 0) {
1870 strncpy(last_battery_str[idx], "not present", 64);
1872 } else if (strcmp(charging_state, "charging") == 0) {
1873 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1874 /* e.g. charging 75% */
1875 snprintf(last_battery_str[idx],
1876 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1877 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1879 format_seconds(last_battery_time_str[idx],
1880 sizeof(last_battery_time_str[idx]) - 1,
1881 (long) (((acpi_last_full[idx] - remaining_capacity) *
1882 3600) / present_rate));
1883 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1884 snprintf(last_battery_str[idx],
1885 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1886 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1887 snprintf(last_battery_time_str[idx],
1888 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1890 strncpy(last_battery_str[idx], "charging",
1891 sizeof(last_battery_str[idx]) - 1);
1892 snprintf(last_battery_time_str[idx],
1893 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1896 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1897 if (present_rate > 0) {
1898 /* e.g. discharging 35% */
1899 snprintf(last_battery_str[idx],
1900 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1901 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1903 format_seconds(last_battery_time_str[idx],
1904 sizeof(last_battery_time_str[idx]) - 1,
1905 (long) ((remaining_capacity * 3600) / present_rate));
1906 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1907 snprintf(last_battery_str[idx],
1908 sizeof(last_battery_str[idx]) - 1, "full");
1909 snprintf(last_battery_time_str[idx],
1910 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1912 snprintf(last_battery_str[idx],
1913 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1914 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1915 snprintf(last_battery_time_str[idx],
1916 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1919 } else if (strncmp(charging_state, "charged", 64) == 0) {
1920 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1921 /* Below happens with the second battery on my X40,
1922 * when the second one is empty and the first one being charged. */
1923 if (remaining_capacity == 0) {
1924 strcpy(last_battery_str[idx], "empty");
1926 strcpy(last_battery_str[idx], "charged");
1928 /* unknown, probably full / AC */
1930 if (strncmp(charging_state, "Full", 64) == 0) {
1931 strncpy(last_battery_str[idx], "full", 64);
1932 } else if (acpi_last_full[idx] != 0
1933 && remaining_capacity != acpi_last_full[idx]) {
1934 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1935 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1937 strncpy(last_battery_str[idx], "AC", 64);
1940 fclose(acpi_bat_fp[idx]);
1941 acpi_bat_fp[idx] = NULL;
1944 if (apm_bat_fp[idx] == NULL) {
1945 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1948 if (apm_bat_fp[idx] != NULL) {
1949 unsigned int ac, status, flag;
1952 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1953 &ac, &status, &flag, &life);
1956 /* could check now that there is ac */
1957 snprintf(last_battery_str[idx], 64, "AC");
1959 /* could check that status == 3 here? */
1960 } else if (ac && life != 100) {
1961 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1963 snprintf(last_battery_str[idx], 64, "%d%%", life);
1966 /* it seemed to buffer it so file must be closed (or could use
1967 * syscalls directly but I don't feel like coding it now) */
1968 fclose(apm_bat_fp[idx]);
1969 apm_bat_fp[idx] = NULL;
1972 set_return_value(buffer, n, item, idx);
1975 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1978 case BATTERY_STATUS:
1979 snprintf(buffer, n, "%s", last_battery_str[idx]);
1982 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1989 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1991 get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1992 if (0 == strncmp("charging", buffer, 8)) {
1994 memmove(buffer + 1, buffer + 8, n - 8);
1995 } else if (0 == strncmp("discharging", buffer, 11)) {
1997 memmove(buffer + 1, buffer + 11, n - 11);
1998 } else if (0 == strncmp("charged", buffer, 7)) {
2000 memmove(buffer + 1, buffer + 7, n - 7);
2001 } else if (0 == strncmp("not present", buffer, 11)) {
2003 memmove(buffer + 1, buffer + 11, n - 11);
2004 } else if (0 == strncmp("empty", buffer, 5)) {
2006 memmove(buffer + 1, buffer + 5, n - 5);
2007 } else if (0 != strncmp("AC", buffer, 2)) {
2009 memmove(buffer + 1, buffer + 11, n - 11);
2013 int get_battery_perct(const char *bat)
2017 char acpi_path[128];
2018 char sysfs_path[128];
2019 int remaining_capacity = -1;
2021 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
2022 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
2026 idx = get_battery_idx(bat);
2028 /* don't update battery too often */
2029 if (current_update_time - last_battery_perct_time[idx] < 30) {
2030 return last_battery_perct[idx];
2032 last_battery_perct_time[idx] = current_update_time;
2034 /* Only check for SYSFS or ACPI */
2036 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
2037 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
2041 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
2042 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
2045 if (sysfs_bat_fp[idx] != NULL) {
2047 while (!feof(sysfs_bat_fp[idx])) {
2049 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
2052 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
2053 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
2054 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
2055 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
2056 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
2057 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
2058 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
2059 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
2063 fclose(sysfs_bat_fp[idx]);
2064 sysfs_bat_fp[idx] = NULL;
2066 } else if (acpi_bat_fp[idx] != NULL) {
2068 /* read last full capacity if it's zero */
2069 if (acpi_design_capacity[idx] == 0) {
2074 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
2075 fp = open_file(path, &rep2);
2080 if (fgets(b, 256, fp) == NULL) {
2083 if (sscanf(b, "last full capacity: %d",
2084 &acpi_design_capacity[idx]) != 0) {
2092 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
2094 while (!feof(acpi_bat_fp[idx])) {
2097 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
2101 if (buf[0] == 'r') {
2102 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
2106 if (remaining_capacity < 0) {
2109 /* compute the battery percentage */
2110 last_battery_perct[idx] =
2111 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
2112 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
2113 return last_battery_perct[idx];
2116 int get_battery_perct_bar(const char *bar)
2120 get_battery_perct(bar);
2121 idx = get_battery_idx(bar);
2122 return (int) (last_battery_perct[idx] * 2.56 - 1);
2125 /* On Apple powerbook and ibook:
2126 $ cat /proc/pmu/battery_0
2133 $ cat /proc/pmu/info
2134 PMU driver version : 2
2135 PMU firmware version : 0c
2140 /* defines as in <linux/pmu.h> */
2141 #define PMU_BATT_PRESENT 0x00000001
2142 #define PMU_BATT_CHARGING 0x00000002
2144 static FILE *pmu_battery_fp;
2145 static FILE *pmu_info_fp;
2146 static char pb_battery_info[3][32];
2147 static double pb_battery_info_update;
2149 #define PMU_PATH "/proc/pmu"
2150 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2153 const char *batt_path = PMU_PATH "/battery_0";
2154 const char *info_path = PMU_PATH "/info";
2156 int charge, max_charge, ac = -1;
2159 /* don't update battery too often */
2160 if (current_update_time - pb_battery_info_update < 29.5) {
2161 snprintf(buffer, n, "%s", pb_battery_info[i]);
2164 pb_battery_info_update = current_update_time;
2166 if (pmu_battery_fp == NULL) {
2167 pmu_battery_fp = open_file(batt_path, &rep);
2168 if (pmu_battery_fp == NULL) {
2173 if (pmu_battery_fp != NULL) {
2174 rewind(pmu_battery_fp);
2175 while (!feof(pmu_battery_fp)) {
2178 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2182 if (buf[0] == 'f') {
2183 sscanf(buf, "flags : %8x", &flags);
2184 } else if (buf[0] == 'c' && buf[1] == 'h') {
2185 sscanf(buf, "charge : %d", &charge);
2186 } else if (buf[0] == 'm') {
2187 sscanf(buf, "max_charge : %d", &max_charge);
2188 } else if (buf[0] == 't') {
2189 sscanf(buf, "time rem. : %ld", &timeval);
2193 if (pmu_info_fp == NULL) {
2194 pmu_info_fp = open_file(info_path, &rep);
2195 if (pmu_info_fp == NULL) {
2200 if (pmu_info_fp != NULL) {
2201 rewind(pmu_info_fp);
2202 while (!feof(pmu_info_fp)) {
2205 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2208 if (buf[0] == 'A') {
2209 sscanf(buf, "AC Power : %d", &ac);
2213 /* update status string */
2214 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2215 strncpy(pb_battery_info[PB_BATT_STATUS], "AC", sizeof(pb_battery_info[PB_BATT_STATUS]));
2216 } else if (ac && (flags & PMU_BATT_PRESENT)
2217 && !(flags & PMU_BATT_CHARGING)) {
2218 strncpy(pb_battery_info[PB_BATT_STATUS], "charged", sizeof(pb_battery_info[PB_BATT_STATUS]));
2219 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2220 strncpy(pb_battery_info[PB_BATT_STATUS], "charging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2222 strncpy(pb_battery_info[PB_BATT_STATUS], "discharging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2225 /* update percentage string */
2226 if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT)
2227 && !(flags & PMU_BATT_CHARGING)) {
2228 snprintf(pb_battery_info[PB_BATT_PERCENT],
2229 sizeof(pb_battery_info[PB_BATT_PERCENT]), "100%%");
2230 } else if (timeval == 0) {
2231 snprintf(pb_battery_info[PB_BATT_PERCENT],
2232 sizeof(pb_battery_info[PB_BATT_PERCENT]), "unknown");
2234 snprintf(pb_battery_info[PB_BATT_PERCENT],
2235 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2236 (charge * 100) / max_charge);
2239 /* update time string */
2240 if (timeval == 0) { /* fully charged or battery not present */
2241 snprintf(pb_battery_info[PB_BATT_TIME],
2242 sizeof(pb_battery_info[PB_BATT_TIME]), "unknown");
2243 } else if (timeval < 60 * 60) { /* don't show secs */
2244 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2245 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2247 format_seconds(pb_battery_info[PB_BATT_TIME],
2248 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2251 snprintf(buffer, n, "%s", pb_battery_info[i]);
2254 void update_top(void)
2256 process_find_top(info.cpu, info.memu, info.time
2261 info.first_process = get_first_process();
2264 #define ENTROPY_AVAIL_PATH "/proc/sys/kernel/random/entropy_avail"
2266 int get_entropy_avail(unsigned int *val)
2271 if (!(fp = open_file(ENTROPY_AVAIL_PATH, &rep)))
2274 if (fscanf(fp, "%u", val) != 1)
2281 #define ENTROPY_POOLSIZE_PATH "/proc/sys/kernel/random/poolsize"
2283 int get_entropy_poolsize(unsigned int *val)
2288 if (!(fp = open_file(ENTROPY_POOLSIZE_PATH, &rep)))
2291 if (fscanf(fp, "%u", val) != 1)
2298 const char *get_disk_protect_queue(const char *disk)
2304 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2305 if (access(path, F_OK)) {
2306 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2308 if ((fp = fopen(path, "r")) == NULL)
2310 if (fscanf(fp, "%d\n", &state) != 1) {
2315 return (state > 0) ? "frozen" : "free ";
2318 void update_diskio(void)
2322 char buf[512], devbuf[64];
2323 unsigned int major, minor;
2325 struct diskio_stat *cur;
2326 unsigned int reads, writes;
2327 unsigned int total_reads = 0, total_writes = 0;
2330 stats.current_read = 0;
2331 stats.current_write = 0;
2333 if (!(fp = open_file("/proc/diskstats", &rep))) {
2337 /* read reads and writes from all disks (minor = 0), including cd-roms
2338 * and floppies, and sum them up */
2339 while (fgets(buf, 512, fp)) {
2340 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
2341 &minor, devbuf, &reads, &writes);
2342 /* ignore subdevices (they have only 3 matching entries in their line)
2343 * and virtual devices (LVM, network block devices, RAM disks, Loopback)
2345 * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
2346 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
2347 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
2348 total_reads += reads;
2349 total_writes += writes;
2351 col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
2352 &major, &minor, devbuf, &reads, &writes);
2353 if (col_count != 5) {
2358 while (cur && strcmp(devbuf, cur->dev))
2362 update_diskio_values(cur, reads, writes);
2364 update_diskio_values(&stats, total_reads, total_writes);