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 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1137 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1139 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1140 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1141 const char *p_format, int divisor, unsigned int cpu)
1149 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1155 char current_freq_file[128];
1157 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1159 f = fopen(current_freq_file, "r");
1161 /* if there's a cpufreq /sys node, read the current frequency from
1162 * this node and divide by 1000 to get Mhz. */
1163 if (fgets(s, sizeof(s), f)) {
1164 s[strlen(s) - 1] = '\0';
1165 freq = strtod(s, NULL);
1168 snprintf(p_client_buffer, client_buffer_size, p_format,
1169 (freq / 1000) / divisor);
1174 // open the CPU information file
1175 f = open_file("/proc/cpuinfo", &rep);
1177 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1182 while (fgets(s, sizeof(s), f) != NULL) {
1184 #if defined(__i386) || defined(__x86_64)
1185 // and search for the cpu mhz
1186 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1188 #if defined(__alpha)
1189 // different on alpha
1190 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1192 // this is different on ppc for some reason
1193 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1194 #endif // defined(__alpha)
1195 #endif // defined(__i386) || defined(__x86_64)
1197 // copy just the number
1198 strcpy(frequency, strchr(s, ':') + 2);
1199 #if defined(__alpha)
1201 frequency[strlen(frequency) - 6] = '\0';
1202 // kernel reports in Hz
1203 freq = strtod(frequency, NULL) / 1000000;
1206 frequency[strlen(frequency) - 1] = '\0';
1207 freq = strtod(frequency, NULL);
1211 if (strncmp(s, "processor", 9) == 0) {
1218 snprintf(p_client_buffer, client_buffer_size, p_format,
1219 (float) freq / divisor);
1223 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1225 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1235 * Peter Tarjan (ptarjan@citromail.hu) */
1237 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1238 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1239 const char *p_format, int divisor, unsigned int cpu)
1245 char current_freq_file[128];
1248 /* build the voltage file name */
1250 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1253 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1258 /* read the current cpu frequency from the /sys node */
1259 f = fopen(current_freq_file, "r");
1261 if (fgets(s, sizeof(s), f)) {
1262 s[strlen(s) - 1] = '\0';
1263 freq = strtod(s, NULL);
1267 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1268 perror("get_voltage()");
1275 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1278 /* use the current cpu frequency to find the corresponding voltage */
1279 f = fopen(current_freq_file, "r");
1285 if (fgets(line, 255, f) == NULL) {
1288 sscanf(line, "%d %d", &freq_comp, &voltage);
1289 if (freq_comp == freq) {
1295 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1296 perror("get_voltage()");
1302 snprintf(p_client_buffer, client_buffer_size, p_format,
1303 (float) voltage / divisor);
1307 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1309 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1316 if (!p_client_buffer || client_buffer_size <= 0) {
1320 /* yeah, slow... :/ */
1321 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1322 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1326 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1328 fp = open_file(buf2, &rep);
1330 snprintf(p_client_buffer, client_buffer_size,
1331 "can't open fan's state file");
1334 memset(buf, 0, sizeof(buf));
1335 fscanf(fp, "%*s %99s", buf);
1338 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1341 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1342 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1343 /* Linux 2.6.25 onwards ac adapter info is in
1344 /sys/class/power_supply/AC/
1345 On my system I get the following.
1346 /sys/class/power_supply/AC/uevent:
1347 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1350 POWER_SUPPLY_NAME=AC
1351 POWER_SUPPLY_TYPE=Mains
1352 POWER_SUPPLY_ONLINE=1
1355 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1363 if (!p_client_buffer || client_buffer_size <= 0) {
1367 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1368 fp = open_file(buf2, &rep);
1370 /* sysfs processing */
1372 if (fgets(buf, sizeof(buf), fp) == NULL)
1375 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1377 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1378 snprintf(p_client_buffer, client_buffer_size,
1379 "%s-line", (online ? "on" : "off"));
1385 /* yeah, slow... :/ */
1386 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1387 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1391 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1393 fp = open_file(buf2, &rep);
1395 snprintf(p_client_buffer, client_buffer_size,
1396 "No ac adapter found.... where is it?");
1399 memset(buf, 0, sizeof(buf));
1400 fscanf(fp, "%*s %99s", buf);
1403 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1408 /proc/acpi/thermal_zone/THRM/cooling_mode
1409 cooling mode: active
1410 /proc/acpi/thermal_zone/THRM/polling_frequency
1412 /proc/acpi/thermal_zone/THRM/state
1414 /proc/acpi/thermal_zone/THRM/temperature
1416 /proc/acpi/thermal_zone/THRM/trip_points
1418 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1421 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1422 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1424 int open_acpi_temperature(const char *name)
1430 if (name == NULL || strcmp(name, "*") == 0) {
1433 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1439 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1441 fd = open(path, O_RDONLY);
1443 NORM_ERR("can't open '%s': %s", path, strerror(errno));
1449 static double last_acpi_temp;
1450 static double last_acpi_temp_time;
1452 double get_acpi_temperature(int fd)
1458 /* don't update acpi temperature too often */
1459 if (current_update_time - last_acpi_temp_time < 11.32) {
1460 return last_acpi_temp;
1462 last_acpi_temp_time = current_update_time;
1464 /* seek to beginning */
1465 lseek(fd, 0, SEEK_SET);
1472 n = read(fd, buf, 255);
1474 NORM_ERR("can't read fd %d: %s", fd, strerror(errno));
1477 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1481 return last_acpi_temp;
1485 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1487 design capacity: 4400 mAh
1488 last full capacity: 4064 mAh
1489 battery technology: rechargeable
1490 design voltage: 14800 mV
1491 design capacity warning: 300 mAh
1492 design capacity low: 200 mAh
1493 capacity granularity 1: 32 mAh
1494 capacity granularity 2: 32 mAh
1496 serial number: 16922
1502 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1505 charging state: unknown
1507 remaining capacity: 4064 mAh
1508 present voltage: 16608 mV
1512 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1513 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1514 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1515 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1516 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1518 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1519 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1521 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1522 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1525 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1526 Linux 2.6.24 onwards battery info is in
1527 /sys/class/power_supply/BAT0/
1528 On my system I get the following.
1529 /sys/class/power_supply/BAT0/uevent:
1530 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1532 PHYSDEVDRIVER=battery
1533 POWER_SUPPLY_NAME=BAT0
1534 POWER_SUPPLY_TYPE=Battery
1535 POWER_SUPPLY_STATUS=Discharging
1536 POWER_SUPPLY_PRESENT=1
1537 POWER_SUPPLY_TECHNOLOGY=Li-ion
1538 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1539 POWER_SUPPLY_VOLTAGE_NOW=10780000
1540 POWER_SUPPLY_CURRENT_NOW=13970000
1541 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1542 POWER_SUPPLY_ENERGY_FULL=27370000
1543 POWER_SUPPLY_ENERGY_NOW=11810000
1544 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1545 POWER_SUPPLY_MANUFACTURER=Panasonic
1546 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1549 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1550 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1551 #define APM_PATH "/proc/apm"
1552 #define MAX_BATTERY_COUNT 4
1554 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1555 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1556 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1558 static int batteries_initialized = 0;
1559 static char batteries[MAX_BATTERY_COUNT][32];
1561 static int acpi_last_full[MAX_BATTERY_COUNT];
1562 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1564 /* e.g. "charging 75%" */
1565 static char last_battery_str[MAX_BATTERY_COUNT][64];
1567 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1569 static double last_battery_time[MAX_BATTERY_COUNT];
1571 static int last_battery_perct[MAX_BATTERY_COUNT];
1572 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1574 void init_batteries(void)
1578 if (batteries_initialized) {
1582 #pragma omp parallel for schedule(dynamic,10)
1583 #endif /* HAVE_OPENMP */
1584 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1585 batteries[idx][0] = '\0';
1587 batteries_initialized = 1;
1590 int get_battery_idx(const char *bat)
1594 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1595 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1600 /* if not found, enter a new entry */
1601 if (!strlen(batteries[idx])) {
1602 snprintf(batteries[idx], 31, "%s", bat);
1608 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1610 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1612 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1613 char acpi_path[128];
1614 char sysfs_path[128];
1616 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1617 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1621 idx = get_battery_idx(bat);
1623 /* don't update battery too often */
1624 if (current_update_time - last_battery_time[idx] < 29.5) {
1625 set_return_value(buffer, n, item, idx);
1629 last_battery_time[idx] = current_update_time;
1631 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1632 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1634 /* first try SYSFS if that fails try ACPI */
1636 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1637 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1640 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1641 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1644 if (sysfs_bat_fp[idx] != NULL) {
1646 int present_rate = -1;
1647 int remaining_capacity = -1;
1648 char charging_state[64];
1651 strcpy(charging_state, "unknown");
1653 while (!feof(sysfs_bat_fp[idx])) {
1655 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1658 /* let's just hope units are ok */
1659 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1660 strcpy(present, "yes");
1661 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1662 strcpy(present, "no");
1663 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1664 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1665 /* present_rate is not the same as the
1666 current flowing now but it is the same value
1667 which was used in the past. so we continue
1669 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1670 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1671 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1672 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1673 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1674 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1675 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1676 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1677 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1678 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1681 fclose(sysfs_bat_fp[idx]);
1682 sysfs_bat_fp[idx] = NULL;
1684 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1685 if (remaining_capacity > acpi_last_full[idx])
1686 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1689 if (strcmp(present, "No") == 0) {
1690 strncpy(last_battery_str[idx], "not present", 64);
1693 else if (strcmp(charging_state, "Charging") == 0) {
1694 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1695 /* e.g. charging 75% */
1696 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1697 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1699 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1700 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1701 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1702 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1703 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1704 snprintf(last_battery_time_str[idx],
1705 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1707 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1708 snprintf(last_battery_time_str[idx],
1709 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1713 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1714 if (present_rate > 0) {
1715 /* e.g. discharging 35% */
1716 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1717 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1719 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1720 (long) (((float) remaining_capacity / present_rate) * 3600));
1721 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1722 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1723 snprintf(last_battery_time_str[idx],
1724 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1726 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1728 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1729 snprintf(last_battery_time_str[idx],
1730 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1734 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1735 else if (strncmp(charging_state, "Charged", 64) == 0 || strncmp(charging_state, "Full", 64) == 0) {
1736 /* Below happens with the second battery on my X40,
1737 * when the second one is empty and the first one
1739 if (remaining_capacity == 0)
1740 strcpy(last_battery_str[idx], "empty");
1742 strcpy(last_battery_str[idx], "charged");
1744 /* unknown, probably full / AC */
1746 if (acpi_last_full[idx] != 0
1747 && remaining_capacity != acpi_last_full[idx])
1748 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1749 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1751 strncpy(last_battery_str[idx], "AC", 64);
1753 } else if (acpi_bat_fp[idx] != NULL) {
1755 int present_rate = -1;
1756 int remaining_capacity = -1;
1757 char charging_state[64];
1760 /* read last full capacity if it's zero */
1761 if (acpi_last_full[idx] == 0) {
1762 static int rep3 = 0;
1766 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1767 fp = open_file(path, &rep3);
1772 if (fgets(b, 256, fp) == NULL) {
1775 if (sscanf(b, "last full capacity: %d",
1776 &acpi_last_full[idx]) != 0) {
1785 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1787 strcpy(charging_state, "unknown");
1789 while (!feof(acpi_bat_fp[idx])) {
1792 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1796 /* let's just hope units are ok */
1797 if (strncmp(buf, "present:", 8) == 0) {
1798 sscanf(buf, "present: %4s", present);
1799 } else if (strncmp(buf, "charging state:", 15) == 0) {
1800 sscanf(buf, "charging state: %63s", charging_state);
1801 } else if (strncmp(buf, "present rate:", 13) == 0) {
1802 sscanf(buf, "present rate: %d", &present_rate);
1803 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1804 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1807 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1808 if (remaining_capacity > acpi_last_full[idx]) {
1809 /* normalize to 100% */
1810 acpi_last_full[idx] = remaining_capacity;
1814 if (strcmp(present, "no") == 0) {
1815 strncpy(last_battery_str[idx], "not present", 64);
1817 } else if (strcmp(charging_state, "charging") == 0) {
1818 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1819 /* e.g. charging 75% */
1820 snprintf(last_battery_str[idx],
1821 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1822 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1824 format_seconds(last_battery_time_str[idx],
1825 sizeof(last_battery_time_str[idx]) - 1,
1826 (long) (((acpi_last_full[idx] - remaining_capacity) *
1827 3600) / present_rate));
1828 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1829 snprintf(last_battery_str[idx],
1830 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1831 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1832 snprintf(last_battery_time_str[idx],
1833 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1835 strncpy(last_battery_str[idx], "charging",
1836 sizeof(last_battery_str[idx]) - 1);
1837 snprintf(last_battery_time_str[idx],
1838 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1841 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1842 if (present_rate > 0) {
1843 /* e.g. discharging 35% */
1844 snprintf(last_battery_str[idx],
1845 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1846 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1848 format_seconds(last_battery_time_str[idx],
1849 sizeof(last_battery_time_str[idx]) - 1,
1850 (long) ((remaining_capacity * 3600) / present_rate));
1851 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1852 snprintf(last_battery_str[idx],
1853 sizeof(last_battery_str[idx]) - 1, "full");
1854 snprintf(last_battery_time_str[idx],
1855 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1857 snprintf(last_battery_str[idx],
1858 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1859 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1860 snprintf(last_battery_time_str[idx],
1861 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1864 } else if (strncmp(charging_state, "charged", 64) == 0) {
1865 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1866 /* Below happens with the second battery on my X40,
1867 * when the second one is empty and the first one being charged. */
1868 if (remaining_capacity == 0) {
1869 strcpy(last_battery_str[idx], "empty");
1871 strcpy(last_battery_str[idx], "charged");
1873 /* unknown, probably full / AC */
1875 if (strncmp(charging_state, "Full", 64) == 0) {
1876 strncpy(last_battery_str[idx], "full", 64);
1877 } else if (acpi_last_full[idx] != 0
1878 && remaining_capacity != acpi_last_full[idx]) {
1879 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1880 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1882 strncpy(last_battery_str[idx], "AC", 64);
1885 fclose(acpi_bat_fp[idx]);
1886 acpi_bat_fp[idx] = NULL;
1889 if (apm_bat_fp[idx] == NULL) {
1890 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1893 if (apm_bat_fp[idx] != NULL) {
1894 unsigned int ac, status, flag;
1897 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1898 &ac, &status, &flag, &life);
1901 /* could check now that there is ac */
1902 snprintf(last_battery_str[idx], 64, "AC");
1904 /* could check that status == 3 here? */
1905 } else if (ac && life != 100) {
1906 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1908 snprintf(last_battery_str[idx], 64, "%d%%", life);
1911 /* it seemed to buffer it so file must be closed (or could use
1912 * syscalls directly but I don't feel like coding it now) */
1913 fclose(apm_bat_fp[idx]);
1914 apm_bat_fp[idx] = NULL;
1917 set_return_value(buffer, n, item, idx);
1920 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1923 case BATTERY_STATUS:
1924 snprintf(buffer, n, "%s", last_battery_str[idx]);
1927 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1934 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1936 get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1937 if (0 == strncmp("charging", buffer, 8)) {
1939 memmove(buffer + 1, buffer + 8, n - 8);
1940 } else if (0 == strncmp("discharging", buffer, 11)) {
1942 memmove(buffer + 1, buffer + 11, n - 11);
1943 } else if (0 == strncmp("charged", buffer, 7)) {
1945 memmove(buffer + 1, buffer + 7, n - 7);
1946 } else if (0 == strncmp("not present", buffer, 11)) {
1948 memmove(buffer + 1, buffer + 11, n - 11);
1949 } else if (0 == strncmp("empty", buffer, 5)) {
1951 memmove(buffer + 1, buffer + 5, n - 5);
1952 } else if (0 != strncmp("AC", buffer, 2)) {
1954 memmove(buffer + 1, buffer + 11, n - 11);
1958 int get_battery_perct(const char *bat)
1962 char acpi_path[128];
1963 char sysfs_path[128];
1964 int remaining_capacity = -1;
1966 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1967 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1971 idx = get_battery_idx(bat);
1973 /* don't update battery too often */
1974 if (current_update_time - last_battery_perct_time[idx] < 30) {
1975 return last_battery_perct[idx];
1977 last_battery_perct_time[idx] = current_update_time;
1979 /* Only check for SYSFS or ACPI */
1981 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1982 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1986 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1987 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1990 if (sysfs_bat_fp[idx] != NULL) {
1992 while (!feof(sysfs_bat_fp[idx])) {
1994 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1997 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1998 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1999 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
2000 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
2001 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
2002 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
2003 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
2004 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
2008 fclose(sysfs_bat_fp[idx]);
2009 sysfs_bat_fp[idx] = NULL;
2011 } else if (acpi_bat_fp[idx] != NULL) {
2013 /* read last full capacity if it's zero */
2014 if (acpi_design_capacity[idx] == 0) {
2019 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
2020 fp = open_file(path, &rep2);
2025 if (fgets(b, 256, fp) == NULL) {
2028 if (sscanf(b, "last full capacity: %d",
2029 &acpi_design_capacity[idx]) != 0) {
2037 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
2039 while (!feof(acpi_bat_fp[idx])) {
2042 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
2046 if (buf[0] == 'r') {
2047 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
2051 if (remaining_capacity < 0) {
2054 /* compute the battery percentage */
2055 last_battery_perct[idx] =
2056 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
2057 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
2058 return last_battery_perct[idx];
2061 int get_battery_perct_bar(const char *bar)
2065 get_battery_perct(bar);
2066 idx = get_battery_idx(bar);
2067 return (int) (last_battery_perct[idx] * 2.56 - 1);
2070 /* On Apple powerbook and ibook:
2071 $ cat /proc/pmu/battery_0
2078 $ cat /proc/pmu/info
2079 PMU driver version : 2
2080 PMU firmware version : 0c
2085 /* defines as in <linux/pmu.h> */
2086 #define PMU_BATT_PRESENT 0x00000001
2087 #define PMU_BATT_CHARGING 0x00000002
2089 static FILE *pmu_battery_fp;
2090 static FILE *pmu_info_fp;
2091 static char pb_battery_info[3][32];
2092 static double pb_battery_info_update;
2094 #define PMU_PATH "/proc/pmu"
2095 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2098 const char *batt_path = PMU_PATH "/battery_0";
2099 const char *info_path = PMU_PATH "/info";
2101 int charge, max_charge, ac = -1;
2104 /* don't update battery too often */
2105 if (current_update_time - pb_battery_info_update < 29.5) {
2106 snprintf(buffer, n, "%s", pb_battery_info[i]);
2109 pb_battery_info_update = current_update_time;
2111 if (pmu_battery_fp == NULL) {
2112 pmu_battery_fp = open_file(batt_path, &rep);
2113 if (pmu_battery_fp == NULL) {
2118 if (pmu_battery_fp != NULL) {
2119 rewind(pmu_battery_fp);
2120 while (!feof(pmu_battery_fp)) {
2123 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2127 if (buf[0] == 'f') {
2128 sscanf(buf, "flags : %8x", &flags);
2129 } else if (buf[0] == 'c' && buf[1] == 'h') {
2130 sscanf(buf, "charge : %d", &charge);
2131 } else if (buf[0] == 'm') {
2132 sscanf(buf, "max_charge : %d", &max_charge);
2133 } else if (buf[0] == 't') {
2134 sscanf(buf, "time rem. : %ld", &timeval);
2138 if (pmu_info_fp == NULL) {
2139 pmu_info_fp = open_file(info_path, &rep);
2140 if (pmu_info_fp == NULL) {
2145 if (pmu_info_fp != NULL) {
2146 rewind(pmu_info_fp);
2147 while (!feof(pmu_info_fp)) {
2150 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2153 if (buf[0] == 'A') {
2154 sscanf(buf, "AC Power : %d", &ac);
2158 /* update status string */
2159 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2160 strncpy(pb_battery_info[PB_BATT_STATUS], "AC", sizeof(pb_battery_info[PB_BATT_STATUS]));
2161 } else if (ac && (flags & PMU_BATT_PRESENT)
2162 && !(flags & PMU_BATT_CHARGING)) {
2163 strncpy(pb_battery_info[PB_BATT_STATUS], "charged", sizeof(pb_battery_info[PB_BATT_STATUS]));
2164 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2165 strncpy(pb_battery_info[PB_BATT_STATUS], "charging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2167 strncpy(pb_battery_info[PB_BATT_STATUS], "discharging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2170 /* update percentage string */
2171 if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT)
2172 && !(flags & PMU_BATT_CHARGING)) {
2173 snprintf(pb_battery_info[PB_BATT_PERCENT],
2174 sizeof(pb_battery_info[PB_BATT_PERCENT]), "100%%");
2175 } else if (timeval == 0) {
2176 snprintf(pb_battery_info[PB_BATT_PERCENT],
2177 sizeof(pb_battery_info[PB_BATT_PERCENT]), "unknown");
2179 snprintf(pb_battery_info[PB_BATT_PERCENT],
2180 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2181 (charge * 100) / max_charge);
2184 /* update time string */
2185 if (timeval == 0) { /* fully charged or battery not present */
2186 snprintf(pb_battery_info[PB_BATT_TIME],
2187 sizeof(pb_battery_info[PB_BATT_TIME]), "unknown");
2188 } else if (timeval < 60 * 60) { /* don't show secs */
2189 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2190 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2192 format_seconds(pb_battery_info[PB_BATT_TIME],
2193 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2196 snprintf(buffer, n, "%s", pb_battery_info[i]);
2199 void update_top(void)
2201 process_find_top(info.cpu, info.memu, info.time
2206 info.first_process = get_first_process();
2209 #define ENTROPY_AVAIL_PATH "/proc/sys/kernel/random/entropy_avail"
2211 int get_entropy_avail(unsigned int *val)
2216 if (!(fp = open_file(ENTROPY_AVAIL_PATH, &rep)))
2219 if (fscanf(fp, "%u", val) != 1)
2226 #define ENTROPY_POOLSIZE_PATH "/proc/sys/kernel/random/poolsize"
2228 int get_entropy_poolsize(unsigned int *val)
2233 if (!(fp = open_file(ENTROPY_POOLSIZE_PATH, &rep)))
2236 if (fscanf(fp, "%u", val) != 1)
2243 const char *get_disk_protect_queue(const char *disk)
2249 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2250 if (access(path, F_OK)) {
2251 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2253 if ((fp = fopen(path, "r")) == NULL)
2255 if (fscanf(fp, "%d\n", &state) != 1) {
2260 return (state > 0) ? "frozen" : "free ";
2263 void update_diskio(void)
2267 char buf[512], devbuf[64];
2268 unsigned int major, minor;
2270 struct diskio_stat *cur;
2271 unsigned int reads, writes;
2272 unsigned int total_reads = 0, total_writes = 0;
2275 stats.current_read = 0;
2276 stats.current_write = 0;
2278 if (!(fp = open_file("/proc/diskstats", &rep))) {
2282 /* read reads and writes from all disks (minor = 0), including cd-roms
2283 * and floppies, and sum them up */
2284 while (fgets(buf, 512, fp)) {
2285 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
2286 &minor, devbuf, &reads, &writes);
2287 /* ignore subdevices (they have only 3 matching entries in their line)
2288 * and virtual devices (LVM, network block devices, RAM disks, Loopback)
2290 * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
2291 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
2292 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
2293 total_reads += reads;
2294 total_writes += writes;
2296 col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
2297 &major, &minor, devbuf, &reads, &writes);
2298 if (col_count != 5) {
2303 while (cur && strcmp(devbuf, cur->dev))
2307 update_diskio_values(cur, reads, writes);
2309 update_diskio_values(&stats, total_reads, total_writes);