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/>.
37 #include "temphelper.h"
42 #include <sys/types.h>
43 #include <sys/sysinfo.h>
45 #ifndef HAVE_CLOCK_GETTIME
50 // #include <assert.h>
54 #include <sys/ioctl.h>
55 #include <sys/socket.h>
56 #include <netinet/in.h>
57 #include <linux/sockios.h>
59 #include <arpa/inet.h>
63 #include <linux/route.h>
66 /* The following ifdefs were adapted from gkrellm */
67 #include <linux/major.h>
69 #if !defined(MD_MAJOR)
73 #if !defined(LVM_BLK_MAJOR)
74 #define LVM_BLK_MAJOR 58
77 #if !defined(NBD_MAJOR)
85 #define SHORTSTAT_TEMPL "%*s %llu %llu %llu"
86 #define LONGSTAT_TEMPL "%*s %llu %llu %llu "
88 /* This flag tells the linux routines to use the /proc system where possible,
89 * even if other api's are available, e.g. sysinfo() or getloadavg().
90 * the reason for this is to allow for /proc-based distributed monitoring.
91 * using a flag in this manner creates less confusing code. */
92 static int prefer_proc = 0;
94 void prepare_update(void)
98 void update_uptime(void)
102 struct sysinfo s_info;
105 info.uptime = (double) s_info.uptime;
112 if (!(fp = open_file("/proc/uptime", &rep))) {
116 fscanf(fp, "%lf", &info.uptime);
121 int check_mount(char *s)
124 FILE *mtab = fopen("/etc/mtab", "r");
127 char buf1[256], buf2[128];
129 while (fgets(buf1, 256, mtab)) {
130 sscanf(buf1, "%*s %128s", buf2);
131 if (!strcmp(s, buf2)) {
138 NORM_ERR("Could not open mtab");
143 /* these things are also in sysinfo except Buffers:
144 * (that's why I'm reading them from proc) */
146 void update_meminfo(void)
151 /* unsigned int a; */
154 info.mem = info.memmax = info.swap = info.swapfree = info.swapmax = info.bufmem =
155 info.buffers = info.cached = info.memfree = info.memeasyfree = 0;
157 if (!(meminfo_fp = open_file("/proc/meminfo", &rep))) {
161 while (!feof(meminfo_fp)) {
162 if (fgets(buf, 255, meminfo_fp) == NULL) {
166 if (strncmp(buf, "MemTotal:", 9) == 0) {
167 sscanf(buf, "%*s %llu", &info.memmax);
168 } else if (strncmp(buf, "MemFree:", 8) == 0) {
169 sscanf(buf, "%*s %llu", &info.memfree);
170 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
171 sscanf(buf, "%*s %llu", &info.swapmax);
172 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
173 sscanf(buf, "%*s %llu", &info.swapfree);
174 } else if (strncmp(buf, "Buffers:", 8) == 0) {
175 sscanf(buf, "%*s %llu", &info.buffers);
176 } else if (strncmp(buf, "Cached:", 7) == 0) {
177 sscanf(buf, "%*s %llu", &info.cached);
181 info.mem = info.memmax - info.memfree;
182 info.memeasyfree = info.memfree;
183 info.swap = info.swapmax - info.swapfree;
185 info.bufmem = info.cached + info.buffers;
190 int get_laptop_mode(void)
195 if ((fp = fopen("/proc/sys/vm/laptop_mode", "r")) != NULL)
196 fscanf(fp, "%d\n", &val);
202 * # cat /sys/block/sda/queue/scheduler
203 * noop [anticipatory] cfq
205 char *get_ioscheduler(char *disk)
211 return strndup("n/a", text_buffer_size);
213 snprintf(buf, 127, "/sys/block/%s/queue/scheduler", disk);
214 if ((fp = fopen(buf, "r")) == NULL) {
215 return strndup("n/a", text_buffer_size);
218 fscanf(fp, "%127s", buf);
220 buf[strlen(buf) - 1] = '\0';
222 return strndup(buf + 1, text_buffer_size);
226 return strndup("n/a", text_buffer_size);
229 #define COND_FREE(x) if(x) free(x); x = 0
230 #define SAVE_SET_STRING(x, y) \
231 if (x && strcmp((char *)x, (char *)y)) { \
233 x = strndup("multiple", text_buffer_size); \
235 x = strndup(y, text_buffer_size); \
238 void update_gateway_info_failure(const char *reason)
243 //2 pointers to 1 location causes a crash when we try to free them both
244 info.gw_info.iface = strndup("failed", text_buffer_size);
245 info.gw_info.ip = strndup("failed", text_buffer_size);
249 /* Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT */
250 #define RT_ENTRY_FORMAT "%63s %lx %lx %x %*d %*d %*d %lx %*d %*d %*d\n"
252 void update_gateway_info(void)
257 unsigned long dest, gate, mask;
260 struct gateway_info *gw_info = &info.gw_info;
262 COND_FREE(gw_info->iface);
263 COND_FREE(gw_info->ip);
266 if ((fp = fopen("/proc/net/route", "r")) == NULL) {
267 update_gateway_info_failure("fopen()");
271 /* skip over the table header line, which is always present */
272 fscanf(fp, "%*[^\n]\n");
275 if(fscanf(fp, RT_ENTRY_FORMAT,
276 iface, &dest, &gate, &flags, &mask) != 5) {
277 update_gateway_info_failure("fscanf()");
280 if (!(dest || mask) && ((flags & RTF_GATEWAY) || !gate) ) {
282 SAVE_SET_STRING(gw_info->iface, iface)
284 SAVE_SET_STRING(gw_info->ip, inet_ntoa(ina))
291 void update_net_stats(void)
295 static char first = 1;
297 // FIXME: arbitrary size chosen to keep code simple.
299 unsigned int curtmp1, curtmp2;
306 // wireless info variables
307 int skfd, has_bitrate = 0;
308 struct wireless_info *winfo;
313 delta = current_update_time - last_update_time;
314 if (delta <= 0.0001) {
318 /* open file and ignore first two lines */
319 if (!(net_dev_fp = open_file("/proc/net/dev", &rep))) {
324 fgets(buf, 255, net_dev_fp); /* garbage */
325 fgets(buf, 255, net_dev_fp); /* garbage (field names) */
327 /* read each interface */
328 for (i2 = 0; i2 < 16; i2++) {
332 long long r, t, last_recv, last_trans;
334 if (fgets(buf, 255, net_dev_fp) == NULL) {
338 while (isspace((int) *p)) {
344 while (*p && *p != ':') {
353 ns = get_net_stat(s, NULL, NULL);
355 memset(&(ns->addr.sa_data), 0, 14);
357 memset(ns->addrs, 0, 17 * 16 + 1); /* Up to 17 chars per ip, max 16 interfaces. Nasty memory usage... */
359 last_recv = ns->recv;
360 last_trans = ns->trans;
362 /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
363 sscanf(p, "%lld %*d %*d %*d %*d %*d %*d %*d %lld",
366 /* if recv or trans is less than last time, an overflow happened */
367 if (r < ns->last_read_recv) {
370 ns->recv += (r - ns->last_read_recv);
372 ns->last_read_recv = r;
374 if (t < ns->last_read_trans) {
377 ns->trans += (t - ns->last_read_trans);
379 ns->last_read_trans = t;
381 /*** ip addr patch ***/
382 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
384 conf.ifc_buf = malloc(sizeof(struct ifreq) * 16);
385 conf.ifc_len = sizeof(struct ifreq) * 16;
386 memset(conf.ifc_buf, 0, conf.ifc_len);
388 ioctl((long) i, SIOCGIFCONF, &conf);
390 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
391 struct net_stat *ns2;
393 if (!(((struct ifreq *) conf.ifc_buf) + k))
397 ((struct ifreq *) conf.ifc_buf)[k].ifr_ifrn.ifrn_name, NULL, NULL);
398 ns2->addr = ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.ifru_addr;
399 sprintf(temp_addr, "%u.%u.%u.%u, ",
400 ns2->addr.sa_data[2] & 255,
401 ns2->addr.sa_data[3] & 255,
402 ns2->addr.sa_data[4] & 255,
403 ns2->addr.sa_data[5] & 255);
404 if(NULL == strstr(ns2->addrs, temp_addr))
405 strncpy(ns2->addrs + strlen(ns2->addrs), temp_addr, 17);
412 /*** end ip addr patch ***/
415 /* calculate speeds */
416 ns->net_rec[0] = (ns->recv - last_recv) / delta;
417 ns->net_trans[0] = (ns->trans - last_trans) / delta;
424 #pragma omp parallel for reduction(+:curtmp1, curtmp2) schedule(dynamic,10)
425 #endif /* HAVE_OPENMP */
426 for (i = 0; i < info.net_avg_samples; i++) {
427 curtmp1 = curtmp1 + ns->net_rec[i];
428 curtmp2 = curtmp2 + ns->net_trans[i];
436 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
437 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
438 if (info.net_avg_samples > 1) {
440 #pragma omp parallel for schedule(dynamic,10)
441 #endif /* HAVE_OPENMP */
442 for (i = info.net_avg_samples; i > 1; i--) {
443 ns->net_rec[i - 1] = ns->net_rec[i - 2];
444 ns->net_trans[i - 1] = ns->net_trans[i - 2];
449 /* update wireless info */
450 winfo = malloc(sizeof(struct wireless_info));
451 memset(winfo, 0, sizeof(struct wireless_info));
453 skfd = iw_sockets_open();
454 if (iw_get_basic_config(skfd, s, &(winfo->b)) > -1) {
456 // set present winfo variables
457 if (iw_get_stats(skfd, s, &(winfo->stats),
458 &winfo->range, winfo->has_range) >= 0) {
459 winfo->has_stats = 1;
461 if (iw_get_range_info(skfd, s, &(winfo->range)) >= 0) {
462 winfo->has_range = 1;
464 if (iw_get_ext(skfd, s, SIOCGIWAP, &wrq) >= 0) {
465 winfo->has_ap_addr = 1;
466 memcpy(&(winfo->ap_addr), &(wrq.u.ap_addr), sizeof(sockaddr));
470 if (iw_get_ext(skfd, s, SIOCGIWRATE, &wrq) >= 0) {
471 memcpy(&(winfo->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
472 iw_print_bitrate(ns->bitrate, 16, winfo->bitrate.value);
477 if (winfo->has_range && winfo->has_stats
478 && ((winfo->stats.qual.level != 0)
479 || (winfo->stats.qual.updated & IW_QUAL_DBM))) {
480 if (!(winfo->stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
481 ns->link_qual = winfo->stats.qual.qual;
482 ns->link_qual_max = winfo->range.max_qual.qual;
487 if (winfo->has_ap_addr) {
488 iw_sawap_ntop(&winfo->ap_addr, ns->ap);
492 if (winfo->b.has_essid) {
493 if (winfo->b.essid_on) {
494 snprintf(ns->essid, 32, "%s", winfo->b.essid);
496 snprintf(ns->essid, 32, "off/any");
500 snprintf(ns->mode, 16, "%s", iw_operation_mode[winfo->b.mode]);
502 iw_sockets_close(skfd);
513 void update_total_processes(void)
517 struct sysinfo s_info;
520 info.procs = s_info.procs;
527 if (!(fp = open_file("/proc/loadavg", &rep))) {
531 fscanf(fp, "%*f %*f %*f %*d/%hu", &info.procs);
536 #define CPU_SAMPLE_COUNT 15
538 unsigned long long cpu_user;
539 unsigned long long cpu_system;
540 unsigned long long cpu_nice;
541 unsigned long long cpu_idle;
542 unsigned long long cpu_iowait;
543 unsigned long long cpu_irq;
544 unsigned long long cpu_softirq;
545 unsigned long long cpu_steal;
546 unsigned long long cpu_total;
547 unsigned long long cpu_active_total;
548 unsigned long long cpu_last_total;
549 unsigned long long cpu_last_active_total;
550 double cpu_val[CPU_SAMPLE_COUNT];
552 static short cpu_setup = 0;
554 /* Determine if this kernel gives us "extended" statistics information in
556 * Kernels around 2.5 and earlier only reported user, system, nice, and
557 * idle values in proc stat.
558 * Kernels around 2.6 and greater report these PLUS iowait, irq, softirq,
560 void determine_longstat(char *buf)
562 unsigned long long iowait = 0;
564 KFLAG_SETOFF(KFLAG_IS_LONGSTAT);
565 /* scanf will either return -1 or 1 because there is only 1 assignment */
566 if (sscanf(buf, "%*s %*d %*d %*d %*d %llu", &iowait) > 0) {
567 KFLAG_SETON(KFLAG_IS_LONGSTAT);
571 void get_cpu_count(void)
577 if (info.cpu_usage) {
581 if (!(stat_fp = open_file("/proc/stat", &rep))) {
587 while (!feof(stat_fp)) {
588 if (fgets(buf, 255, stat_fp) == NULL) {
592 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
593 if (info.cpu_count == 0) {
594 determine_longstat(buf);
599 info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
604 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
605 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
607 inline static void update_stat(void)
611 static struct cpu_info *cpu = NULL;
616 const char *stat_template = NULL;
617 unsigned int malloc_cpu_size = 0;
618 extern void* global_cpu;
619 static double last_stat_update = 0.0;
621 /* since we use wrappers for this function, the update machinery
622 * can't eliminate double invocations of this function. Check for
623 * them here, otherwise cpu_usage counters are freaking out. */
624 if (last_stat_update == current_update_time)
626 last_stat_update = current_update_time;
628 /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
629 if (!cpu_setup || !info.cpu_usage) {
634 if (!stat_template) {
636 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
640 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
641 cpu = malloc(malloc_cpu_size);
642 memset(cpu, 0, malloc_cpu_size);
646 if (!(stat_fp = open_file("/proc/stat", &rep))) {
648 if (info.cpu_usage) {
649 memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
655 while (!feof(stat_fp)) {
656 if (fgets(buf, 255, stat_fp) == NULL) {
660 if (strncmp(buf, "procs_running ", 14) == 0) {
661 sscanf(buf, "%*s %hu", &info.run_procs);
662 } else if (strncmp(buf, "cpu", 3) == 0) {
664 if (isdigit(buf[3])) {
665 idx = atoi(&buf[3]) + 1;
669 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
670 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
671 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
672 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
673 &(cpu[idx].cpu_steal));
675 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
676 cpu[idx].cpu_system + cpu[idx].cpu_idle +
677 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
678 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
680 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
681 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
683 delta = current_update_time - last_update_time;
685 if (delta <= 0.001) {
689 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
690 cpu[idx].cpu_last_active_total) /
691 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
694 #pragma omp parallel for reduction(+:curtmp) schedule(dynamic,10)
695 #endif /* HAVE_OPENMP */
696 for (i = 0; i < info.cpu_avg_samples; i++) {
697 curtmp = curtmp + cpu[idx].cpu_val[i];
699 /* TESTING -- I've removed this, because I don't think it is right.
700 * You shouldn't divide by the cpu count here ...
701 * removing for testing */
703 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
706 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
708 /* TESTING -- this line replaces the prev. "suspect" if/else */
709 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
711 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
712 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
714 #pragma omp parallel for schedule(dynamic,10)
715 #endif /* HAVE_OPENMP */
716 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
717 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
724 void update_running_processes(void)
729 void update_cpu_usage(void)
734 void update_load_average(void)
736 #ifdef HAVE_GETLOADAVG
741 info.loadavg[0] = (float) v[0];
742 info.loadavg[1] = (float) v[1];
743 info.loadavg[2] = (float) v[2];
750 if (!(fp = open_file("/proc/loadavg", &rep))) {
751 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
754 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
760 #define PROC_I8K "/proc/i8k"
761 #define I8K_DELIM " "
762 static char *i8k_procbuf = NULL;
763 void update_i8k(void)
768 i8k_procbuf = (char *) malloc(128 * sizeof(char));
770 if ((fp = fopen(PROC_I8K, "r")) == NULL) {
771 CRIT_ERR(NULL, NULL, "/proc/i8k doesn't exist! use insmod to make sure the kernel "
772 "driver is loaded...");
775 memset(&i8k_procbuf[0], 0, 128);
776 if (fread(&i8k_procbuf[0], sizeof(char), 128, fp) == 0) {
777 NORM_ERR("something wrong with /proc/i8k...");
782 i8k.version = strtok(&i8k_procbuf[0], I8K_DELIM);
783 i8k.bios = strtok(NULL, I8K_DELIM);
784 i8k.serial = strtok(NULL, I8K_DELIM);
785 i8k.cpu_temp = strtok(NULL, I8K_DELIM);
786 i8k.left_fan_status = strtok(NULL, I8K_DELIM);
787 i8k.right_fan_status = strtok(NULL, I8K_DELIM);
788 i8k.left_fan_rpm = strtok(NULL, I8K_DELIM);
789 i8k.right_fan_rpm = strtok(NULL, I8K_DELIM);
790 i8k.ac_status = strtok(NULL, I8K_DELIM);
791 i8k.buttons_status = strtok(NULL, I8K_DELIM);
794 /***********************************************************/
795 /***********************************************************/
796 /***********************************************************/
798 static int no_dots(const struct dirent *d)
800 if (d->d_name[0] == '.') {
806 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
808 struct dirent **namelist;
811 n = scandir(dir, &namelist, no_dots, alphasort);
814 NORM_ERR("scandir for %s: %s", dir, strerror(errno));
825 strncpy(s, namelist[0]->d_name, 255);
829 #pragma omp parallel for schedule(dynamic,10)
830 #endif /* HAVE_OPENMP */
831 for (i = 0; i < n; i++) {
840 static int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
841 int *divisor, char *devtype)
849 memset(buf, 0, sizeof(buf));
851 /* if device is NULL or *, get first */
852 if (dev == NULL || strcmp(dev, "*") == 0) {
855 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
861 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
863 /* buf holds result from get_first_file_in_a_directory() above,
864 * e.g. "hwmon0" -- append "/device" */
865 strcat(buf, "/device");
867 /* dev holds device number N as a string,
868 * e.g. "0", -- convert to "hwmon0/device" */
869 sprintf(buf, "hwmon%s/device", dev);
874 /* At least the acpitz hwmon doesn't have a 'device' subdir,
875 * so check it's existence and strip it from buf otherwise. */
876 snprintf(path, 255, "%s%s", dir, dev);
877 if (stat(path, &st)) {
878 buf[strlen(buf) - 7] = 0;
881 /* change vol to in, tempf to temp */
882 if (strcmp(type, "vol") == 0) {
884 } else if (strcmp(type, "tempf") == 0) {
888 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
889 strncpy(devtype, path, 255);
892 fd = open(path, O_RDONLY);
894 CRIT_ERR(NULL, NULL, "can't open '%s': %s\nplease check your device or remove this "
895 "var from "PACKAGE_NAME, path, strerror(errno));
898 if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
899 || strcmp(type, "tempf") == 0) {
904 /* fan does not use *_div as a read divisor */
905 if (strcmp("fan", type) == 0) {
909 /* test if *_div file exist, open it and use it as divisor */
910 if (strcmp(type, "tempf") == 0) {
911 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
913 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
916 divfd = open(path, O_RDONLY);
922 divn = read(divfd, divbuf, 63);
923 /* should read until n == 0 but I doubt that kernel will give these
924 * in multiple pieces. :) */
926 NORM_ERR("open_sysfs_sensor(): can't read from sysfs");
929 *divisor = atoi(divbuf);
937 static double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
945 lseek(*fd, 0, SEEK_SET);
951 n = read(*fd, buf, 63);
952 /* should read until n == 0 but I doubt that kernel will give these
953 * in multiple pieces. :) */
955 NORM_ERR("get_sysfs_info(): read from %s failed\n", devtype);
964 *fd = open(devtype, O_RDONLY);
966 NORM_ERR("can't open '%s': %s", devtype, strerror(errno));
969 /* My dirty hack for computing CPU value
970 * Filedil, from forums.gentoo.org */
971 /* if (strstr(devtype, "temp1_input") != NULL) {
972 return -15.096 + 1.4893 * (val / 1000.0);
975 /* divide voltage and temperature by 1000 */
976 /* or if any other divisor is given, use that */
977 if (strcmp(type, "tempf") == 0) {
979 return ((val / divisor + 40) * 9.0 / 5) - 40;
980 } else if (divisor) {
981 return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
983 return ((val + 40) * 9.0 / 5) - 40;
987 return val / divisor;
988 } else if (divisor) {
996 #define HWMON_RESET() {\
1001 static void parse_sysfs_sensor(struct text_object *obj, const char *arg, const char *path, const char *type)
1003 char buf1[64], buf2[64];
1004 float factor, offset;
1007 if (sscanf(arg, "%63s %d %f %f", buf2, &n, &factor, &offset) == 4) found = 1; else HWMON_RESET();
1008 if (!found && sscanf(arg, "%63s %63s %d %f %f", buf1, buf2, &n, &factor, &offset) == 5) found = 1; else if (!found) HWMON_RESET();
1009 if (!found && sscanf(arg, "%63s %63s %d", buf1, buf2, &n) == 3) found = 1; else if (!found) HWMON_RESET();
1010 if (!found && sscanf(arg, "%63s %d", buf2, &n) == 2) found = 1; else if (!found) HWMON_RESET();
1013 NORM_ERR("i2c failed to parse arguments");
1014 obj->type = OBJ_text;
1017 DBGP("parsed %s args: '%s' '%s' %d %f %f\n", type, buf1, buf2, n, factor, offset);
1018 obj->data.sysfs.fd = open_sysfs_sensor(path, (*buf1) ? buf1 : 0, buf2, n,
1019 &obj->data.sysfs.arg, obj->data.sysfs.devtype);
1020 strncpy(obj->data.sysfs.type, buf2, 63);
1021 obj->data.sysfs.factor = factor;
1022 obj->data.sysfs.offset = offset;
1025 #define PARSER_GENERATOR(name, path) \
1026 void parse_##name##_sensor(struct text_object *obj, const char *arg) \
1028 parse_sysfs_sensor(obj, arg, path, #name); \
1031 PARSER_GENERATOR(i2c, "/sys/bus/i2c/devices/")
1032 PARSER_GENERATOR(hwmon, "/sys/class/hwmon/")
1033 PARSER_GENERATOR(platform, "/sys/bus/platform/devices/")
1035 void print_sysfs_sensor(struct text_object *obj, char *p, int p_max_size)
1039 r = get_sysfs_info(&obj->data.sysfs.fd, obj->data.sysfs.arg,
1040 obj->data.sysfs.devtype, obj->data.sysfs.type);
1042 r = r * obj->data.sysfs.factor + obj->data.sysfs.offset;
1044 if (!strncmp(obj->data.sysfs.type, "temp", 4)) {
1045 temp_print(p, p_max_size, r, TEMP_CELSIUS);
1046 } else if (r >= 100.0 || r == 0) {
1047 snprintf(p, p_max_size, "%d", (int) r);
1049 snprintf(p, p_max_size, "%.1f", r);
1053 /* Prior to kernel version 2.6.12, the CPU fan speed was available in
1054 * ADT746X_FAN_OLD, whereas later kernel versions provide this information in
1056 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
1057 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
1059 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
1062 char adt746x_fan_state[64];
1065 if (!p_client_buffer || client_buffer_size <= 0) {
1069 if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
1070 && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL) {
1071 sprintf(adt746x_fan_state, "adt746x not found");
1073 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
1074 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
1078 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_fan_state);
1081 /* Prior to kernel version 2.6.12, the CPU temperature was found in
1082 * ADT746X_CPU_OLD, whereas later kernel versions provide this information in
1084 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
1085 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
1087 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
1090 char adt746x_cpu_state[64];
1093 if (!p_client_buffer || client_buffer_size <= 0) {
1097 if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
1098 && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL) {
1099 sprintf(adt746x_cpu_state, "adt746x not found");
1101 fscanf(fp, "%2s", adt746x_cpu_state);
1105 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state);
1108 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1109 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1111 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1112 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1113 const char *p_format, int divisor, unsigned int cpu)
1121 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1127 char current_freq_file[128];
1129 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1131 f = fopen(current_freq_file, "r");
1133 /* if there's a cpufreq /sys node, read the current frequency from
1134 * this node and divide by 1000 to get Mhz. */
1135 if (fgets(s, sizeof(s), f)) {
1136 s[strlen(s) - 1] = '\0';
1137 freq = strtod(s, NULL);
1140 snprintf(p_client_buffer, client_buffer_size, p_format,
1141 (freq / 1000) / divisor);
1146 // open the CPU information file
1147 f = open_file("/proc/cpuinfo", &rep);
1149 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1154 while (fgets(s, sizeof(s), f) != NULL) {
1156 #if defined(__i386) || defined(__x86_64)
1157 // and search for the cpu mhz
1158 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1160 #if defined(__alpha)
1161 // different on alpha
1162 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1164 // this is different on ppc for some reason
1165 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1166 #endif // defined(__alpha)
1167 #endif // defined(__i386) || defined(__x86_64)
1169 // copy just the number
1170 strcpy(frequency, strchr(s, ':') + 2);
1171 #if defined(__alpha)
1173 frequency[strlen(frequency) - 6] = '\0';
1174 // kernel reports in Hz
1175 freq = strtod(frequency, NULL) / 1000000;
1178 frequency[strlen(frequency) - 1] = '\0';
1179 freq = strtod(frequency, NULL);
1183 if (strncmp(s, "processor", 9) == 0) {
1190 snprintf(p_client_buffer, client_buffer_size, p_format,
1191 (float) freq / divisor);
1195 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1197 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1207 * Peter Tarjan (ptarjan@citromail.hu) */
1209 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1210 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1211 const char *p_format, int divisor, unsigned int cpu)
1217 char current_freq_file[128];
1220 /* build the voltage file name */
1222 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1225 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1230 /* read the current cpu frequency from the /sys node */
1231 f = fopen(current_freq_file, "r");
1233 if (fgets(s, sizeof(s), f)) {
1234 s[strlen(s) - 1] = '\0';
1235 freq = strtod(s, NULL);
1239 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1240 perror("get_voltage()");
1247 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1250 /* use the current cpu frequency to find the corresponding voltage */
1251 f = fopen(current_freq_file, "r");
1257 if (fgets(line, 255, f) == NULL) {
1260 sscanf(line, "%d %d", &freq_comp, &voltage);
1261 if (freq_comp == freq) {
1267 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1268 perror("get_voltage()");
1274 snprintf(p_client_buffer, client_buffer_size, p_format,
1275 (float) voltage / divisor);
1279 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1281 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1288 if (!p_client_buffer || client_buffer_size <= 0) {
1292 /* yeah, slow... :/ */
1293 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1294 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1298 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1300 fp = open_file(buf2, &rep);
1302 snprintf(p_client_buffer, client_buffer_size,
1303 "can't open fan's state file");
1306 memset(buf, 0, sizeof(buf));
1307 fscanf(fp, "%*s %99s", buf);
1310 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1313 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1314 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1315 /* Linux 2.6.25 onwards ac adapter info is in
1316 /sys/class/power_supply/AC/
1317 On my system I get the following.
1318 /sys/class/power_supply/AC/uevent:
1319 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1322 POWER_SUPPLY_NAME=AC
1323 POWER_SUPPLY_TYPE=Mains
1324 POWER_SUPPLY_ONLINE=1
1327 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1335 if (!p_client_buffer || client_buffer_size <= 0) {
1339 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1340 fp = open_file(buf2, &rep);
1342 /* sysfs processing */
1344 if (fgets(buf, sizeof(buf), fp) == NULL)
1347 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1349 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1350 snprintf(p_client_buffer, client_buffer_size,
1351 "%s-line", (online ? "on" : "off"));
1357 /* yeah, slow... :/ */
1358 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1359 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1363 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1365 fp = open_file(buf2, &rep);
1367 snprintf(p_client_buffer, client_buffer_size,
1368 "No ac adapter found.... where is it?");
1371 memset(buf, 0, sizeof(buf));
1372 fscanf(fp, "%*s %99s", buf);
1375 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1380 /proc/acpi/thermal_zone/THRM/cooling_mode
1381 cooling mode: active
1382 /proc/acpi/thermal_zone/THRM/polling_frequency
1384 /proc/acpi/thermal_zone/THRM/state
1386 /proc/acpi/thermal_zone/THRM/temperature
1388 /proc/acpi/thermal_zone/THRM/trip_points
1390 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1393 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1394 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1396 int open_acpi_temperature(const char *name)
1402 if (name == NULL || strcmp(name, "*") == 0) {
1405 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1411 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1413 fd = open(path, O_RDONLY);
1415 NORM_ERR("can't open '%s': %s", path, strerror(errno));
1421 static double last_acpi_temp;
1422 static double last_acpi_temp_time;
1424 double get_acpi_temperature(int fd)
1430 /* don't update acpi temperature too often */
1431 if (current_update_time - last_acpi_temp_time < 11.32) {
1432 return last_acpi_temp;
1434 last_acpi_temp_time = current_update_time;
1436 /* seek to beginning */
1437 lseek(fd, 0, SEEK_SET);
1444 n = read(fd, buf, 255);
1446 NORM_ERR("can't read fd %d: %s", fd, strerror(errno));
1449 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1453 return last_acpi_temp;
1457 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1459 design capacity: 4400 mAh
1460 last full capacity: 4064 mAh
1461 battery technology: rechargeable
1462 design voltage: 14800 mV
1463 design capacity warning: 300 mAh
1464 design capacity low: 200 mAh
1465 capacity granularity 1: 32 mAh
1466 capacity granularity 2: 32 mAh
1468 serial number: 16922
1474 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1477 charging state: unknown
1479 remaining capacity: 4064 mAh
1480 present voltage: 16608 mV
1484 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1485 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1486 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1487 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1488 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1490 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1491 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1493 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1494 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1497 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1498 Linux 2.6.24 onwards battery info is in
1499 /sys/class/power_supply/BAT0/
1500 On my system I get the following.
1501 /sys/class/power_supply/BAT0/uevent:
1502 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1504 PHYSDEVDRIVER=battery
1505 POWER_SUPPLY_NAME=BAT0
1506 POWER_SUPPLY_TYPE=Battery
1507 POWER_SUPPLY_STATUS=Discharging
1508 POWER_SUPPLY_PRESENT=1
1509 POWER_SUPPLY_TECHNOLOGY=Li-ion
1510 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1511 POWER_SUPPLY_VOLTAGE_NOW=10780000
1512 POWER_SUPPLY_CURRENT_NOW=13970000
1513 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1514 POWER_SUPPLY_ENERGY_FULL=27370000
1515 POWER_SUPPLY_ENERGY_NOW=11810000
1516 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1517 POWER_SUPPLY_MANUFACTURER=Panasonic
1518 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1521 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1522 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1523 #define APM_PATH "/proc/apm"
1524 #define MAX_BATTERY_COUNT 4
1526 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1527 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1528 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1530 static int batteries_initialized = 0;
1531 static char batteries[MAX_BATTERY_COUNT][32];
1533 static int acpi_last_full[MAX_BATTERY_COUNT];
1534 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1536 /* e.g. "charging 75%" */
1537 static char last_battery_str[MAX_BATTERY_COUNT][64];
1539 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1541 static double last_battery_time[MAX_BATTERY_COUNT];
1543 static int last_battery_perct[MAX_BATTERY_COUNT];
1544 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1546 void init_batteries(void)
1550 if (batteries_initialized) {
1554 #pragma omp parallel for schedule(dynamic,10)
1555 #endif /* HAVE_OPENMP */
1556 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1557 batteries[idx][0] = '\0';
1559 batteries_initialized = 1;
1562 int get_battery_idx(const char *bat)
1566 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1567 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1572 /* if not found, enter a new entry */
1573 if (!strlen(batteries[idx])) {
1574 snprintf(batteries[idx], 31, "%s", bat);
1580 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1582 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1584 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1585 char acpi_path[128];
1586 char sysfs_path[128];
1588 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1589 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1593 idx = get_battery_idx(bat);
1595 /* don't update battery too often */
1596 if (current_update_time - last_battery_time[idx] < 29.5) {
1597 set_return_value(buffer, n, item, idx);
1601 last_battery_time[idx] = current_update_time;
1603 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1604 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1606 /* first try SYSFS if that fails try ACPI */
1608 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1609 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1612 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1613 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1616 if (sysfs_bat_fp[idx] != NULL) {
1618 int present_rate = -1;
1619 int remaining_capacity = -1;
1620 char charging_state[64];
1623 strcpy(charging_state, "unknown");
1625 while (!feof(sysfs_bat_fp[idx])) {
1627 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1630 /* let's just hope units are ok */
1631 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1632 strcpy(present, "yes");
1633 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1634 strcpy(present, "no");
1635 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1636 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1637 /* present_rate is not the same as the
1638 current flowing now but it is the same value
1639 which was used in the past. so we continue
1641 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1642 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1643 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1644 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1645 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1646 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1647 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1648 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1649 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1650 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1653 fclose(sysfs_bat_fp[idx]);
1654 sysfs_bat_fp[idx] = NULL;
1656 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1657 if (remaining_capacity > acpi_last_full[idx])
1658 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1661 if (strcmp(present, "No") == 0) {
1662 strncpy(last_battery_str[idx], "not present", 64);
1665 else if (strcmp(charging_state, "Charging") == 0) {
1666 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1667 /* e.g. charging 75% */
1668 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1669 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1671 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1672 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1673 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1674 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1675 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1676 snprintf(last_battery_time_str[idx],
1677 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1679 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1680 snprintf(last_battery_time_str[idx],
1681 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1685 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1686 if (present_rate > 0) {
1687 /* e.g. discharging 35% */
1688 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1689 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1691 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1692 (long) (((float) remaining_capacity / present_rate) * 3600));
1693 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1694 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1695 snprintf(last_battery_time_str[idx],
1696 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1698 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1700 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1701 snprintf(last_battery_time_str[idx],
1702 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1706 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1707 else if (strncmp(charging_state, "Charged", 64) == 0 || strncmp(charging_state, "Full", 64) == 0) {
1708 /* Below happens with the second battery on my X40,
1709 * when the second one is empty and the first one
1711 if (remaining_capacity == 0)
1712 strcpy(last_battery_str[idx], "empty");
1714 strcpy(last_battery_str[idx], "charged");
1716 /* unknown, probably full / AC */
1718 if (acpi_last_full[idx] != 0
1719 && remaining_capacity != acpi_last_full[idx])
1720 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1721 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1723 strncpy(last_battery_str[idx], "AC", 64);
1725 } else if (acpi_bat_fp[idx] != NULL) {
1727 int present_rate = -1;
1728 int remaining_capacity = -1;
1729 char charging_state[64];
1732 /* read last full capacity if it's zero */
1733 if (acpi_last_full[idx] == 0) {
1734 static int rep3 = 0;
1738 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1739 fp = open_file(path, &rep3);
1744 if (fgets(b, 256, fp) == NULL) {
1747 if (sscanf(b, "last full capacity: %d",
1748 &acpi_last_full[idx]) != 0) {
1757 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1759 strcpy(charging_state, "unknown");
1761 while (!feof(acpi_bat_fp[idx])) {
1764 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1768 /* let's just hope units are ok */
1769 if (strncmp(buf, "present:", 8) == 0) {
1770 sscanf(buf, "present: %4s", present);
1771 } else if (strncmp(buf, "charging state:", 15) == 0) {
1772 sscanf(buf, "charging state: %63s", charging_state);
1773 } else if (strncmp(buf, "present rate:", 13) == 0) {
1774 sscanf(buf, "present rate: %d", &present_rate);
1775 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1776 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1779 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1780 if (remaining_capacity > acpi_last_full[idx]) {
1781 /* normalize to 100% */
1782 acpi_last_full[idx] = remaining_capacity;
1786 if (strcmp(present, "no") == 0) {
1787 strncpy(last_battery_str[idx], "not present", 64);
1789 } else if (strcmp(charging_state, "charging") == 0) {
1790 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1791 /* e.g. charging 75% */
1792 snprintf(last_battery_str[idx],
1793 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1794 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1796 format_seconds(last_battery_time_str[idx],
1797 sizeof(last_battery_time_str[idx]) - 1,
1798 (long) (((acpi_last_full[idx] - remaining_capacity) *
1799 3600) / present_rate));
1800 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1801 snprintf(last_battery_str[idx],
1802 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1803 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1804 snprintf(last_battery_time_str[idx],
1805 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1807 strncpy(last_battery_str[idx], "charging",
1808 sizeof(last_battery_str[idx]) - 1);
1809 snprintf(last_battery_time_str[idx],
1810 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1813 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1814 if (present_rate > 0) {
1815 /* e.g. discharging 35% */
1816 snprintf(last_battery_str[idx],
1817 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1818 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1820 format_seconds(last_battery_time_str[idx],
1821 sizeof(last_battery_time_str[idx]) - 1,
1822 (long) ((remaining_capacity * 3600) / present_rate));
1823 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1824 snprintf(last_battery_str[idx],
1825 sizeof(last_battery_str[idx]) - 1, "full");
1826 snprintf(last_battery_time_str[idx],
1827 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1829 snprintf(last_battery_str[idx],
1830 sizeof(last_battery_str[idx]) - 1, "discharging %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");
1836 } else if (strncmp(charging_state, "charged", 64) == 0) {
1837 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1838 /* Below happens with the second battery on my X40,
1839 * when the second one is empty and the first one being charged. */
1840 if (remaining_capacity == 0) {
1841 strcpy(last_battery_str[idx], "empty");
1843 strcpy(last_battery_str[idx], "charged");
1845 /* unknown, probably full / AC */
1847 if (strncmp(charging_state, "Full", 64) == 0) {
1848 strncpy(last_battery_str[idx], "full", 64);
1849 } else if (acpi_last_full[idx] != 0
1850 && remaining_capacity != acpi_last_full[idx]) {
1851 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1852 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1854 strncpy(last_battery_str[idx], "AC", 64);
1857 fclose(acpi_bat_fp[idx]);
1858 acpi_bat_fp[idx] = NULL;
1861 if (apm_bat_fp[idx] == NULL) {
1862 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1865 if (apm_bat_fp[idx] != NULL) {
1866 unsigned int ac, status, flag;
1869 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1870 &ac, &status, &flag, &life);
1873 /* could check now that there is ac */
1874 snprintf(last_battery_str[idx], 64, "AC");
1876 /* could check that status == 3 here? */
1877 } else if (ac && life != 100) {
1878 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1880 snprintf(last_battery_str[idx], 64, "%d%%", life);
1883 /* it seemed to buffer it so file must be closed (or could use
1884 * syscalls directly but I don't feel like coding it now) */
1885 fclose(apm_bat_fp[idx]);
1886 apm_bat_fp[idx] = NULL;
1889 set_return_value(buffer, n, item, idx);
1892 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1895 case BATTERY_STATUS:
1896 snprintf(buffer, n, "%s", last_battery_str[idx]);
1899 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1906 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1908 get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1909 if (0 == strncmp("charging", buffer, 8)) {
1911 memmove(buffer + 1, buffer + 8, n - 8);
1912 } else if (0 == strncmp("discharging", buffer, 11)) {
1914 memmove(buffer + 1, buffer + 11, n - 11);
1915 } else if (0 == strncmp("charged", buffer, 7)) {
1917 memmove(buffer + 1, buffer + 7, n - 7);
1918 } else if (0 == strncmp("not present", buffer, 11)) {
1920 memmove(buffer + 1, buffer + 11, n - 11);
1921 } else if (0 == strncmp("empty", buffer, 5)) {
1923 memmove(buffer + 1, buffer + 5, n - 5);
1924 } else if (0 != strncmp("AC", buffer, 2)) {
1926 memmove(buffer + 1, buffer + 11, n - 11);
1930 int get_battery_perct(const char *bat)
1934 char acpi_path[128];
1935 char sysfs_path[128];
1936 int remaining_capacity = -1;
1938 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1939 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1943 idx = get_battery_idx(bat);
1945 /* don't update battery too often */
1946 if (current_update_time - last_battery_perct_time[idx] < 30) {
1947 return last_battery_perct[idx];
1949 last_battery_perct_time[idx] = current_update_time;
1951 /* Only check for SYSFS or ACPI */
1953 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1954 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1958 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1959 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1962 if (sysfs_bat_fp[idx] != NULL) {
1964 while (!feof(sysfs_bat_fp[idx])) {
1966 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1969 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1970 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1971 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1972 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1973 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1974 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1975 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1976 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1980 fclose(sysfs_bat_fp[idx]);
1981 sysfs_bat_fp[idx] = NULL;
1983 } else if (acpi_bat_fp[idx] != NULL) {
1985 /* read last full capacity if it's zero */
1986 if (acpi_design_capacity[idx] == 0) {
1991 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1992 fp = open_file(path, &rep2);
1997 if (fgets(b, 256, fp) == NULL) {
2000 if (sscanf(b, "last full capacity: %d",
2001 &acpi_design_capacity[idx]) != 0) {
2009 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
2011 while (!feof(acpi_bat_fp[idx])) {
2014 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
2018 if (buf[0] == 'r') {
2019 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
2023 if (remaining_capacity < 0) {
2026 /* compute the battery percentage */
2027 last_battery_perct[idx] =
2028 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
2029 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
2030 return last_battery_perct[idx];
2033 int get_battery_perct_bar(const char *bar)
2037 get_battery_perct(bar);
2038 idx = get_battery_idx(bar);
2039 return (int) (last_battery_perct[idx] * 2.56 - 1);
2042 /* On Apple powerbook and ibook:
2043 $ cat /proc/pmu/battery_0
2050 $ cat /proc/pmu/info
2051 PMU driver version : 2
2052 PMU firmware version : 0c
2057 /* defines as in <linux/pmu.h> */
2058 #define PMU_BATT_PRESENT 0x00000001
2059 #define PMU_BATT_CHARGING 0x00000002
2061 static FILE *pmu_battery_fp;
2062 static FILE *pmu_info_fp;
2063 static char pb_battery_info[3][32];
2064 static double pb_battery_info_update;
2066 #define PMU_PATH "/proc/pmu"
2067 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2070 const char *batt_path = PMU_PATH "/battery_0";
2071 const char *info_path = PMU_PATH "/info";
2073 int charge, max_charge, ac = -1;
2076 /* don't update battery too often */
2077 if (current_update_time - pb_battery_info_update < 29.5) {
2078 snprintf(buffer, n, "%s", pb_battery_info[i]);
2081 pb_battery_info_update = current_update_time;
2083 if (pmu_battery_fp == NULL) {
2084 pmu_battery_fp = open_file(batt_path, &rep);
2085 if (pmu_battery_fp == NULL) {
2090 if (pmu_battery_fp != NULL) {
2091 rewind(pmu_battery_fp);
2092 while (!feof(pmu_battery_fp)) {
2095 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2099 if (buf[0] == 'f') {
2100 sscanf(buf, "flags : %8x", &flags);
2101 } else if (buf[0] == 'c' && buf[1] == 'h') {
2102 sscanf(buf, "charge : %d", &charge);
2103 } else if (buf[0] == 'm') {
2104 sscanf(buf, "max_charge : %d", &max_charge);
2105 } else if (buf[0] == 't') {
2106 sscanf(buf, "time rem. : %ld", &timeval);
2110 if (pmu_info_fp == NULL) {
2111 pmu_info_fp = open_file(info_path, &rep);
2112 if (pmu_info_fp == NULL) {
2117 if (pmu_info_fp != NULL) {
2118 rewind(pmu_info_fp);
2119 while (!feof(pmu_info_fp)) {
2122 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2125 if (buf[0] == 'A') {
2126 sscanf(buf, "AC Power : %d", &ac);
2130 /* update status string */
2131 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2132 strncpy(pb_battery_info[PB_BATT_STATUS], "AC", sizeof(pb_battery_info[PB_BATT_STATUS]));
2133 } else if (ac && (flags & PMU_BATT_PRESENT)
2134 && !(flags & PMU_BATT_CHARGING)) {
2135 strncpy(pb_battery_info[PB_BATT_STATUS], "charged", sizeof(pb_battery_info[PB_BATT_STATUS]));
2136 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2137 strncpy(pb_battery_info[PB_BATT_STATUS], "charging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2139 strncpy(pb_battery_info[PB_BATT_STATUS], "discharging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2142 /* update percentage string */
2143 if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT)
2144 && !(flags & PMU_BATT_CHARGING)) {
2145 snprintf(pb_battery_info[PB_BATT_PERCENT],
2146 sizeof(pb_battery_info[PB_BATT_PERCENT]), "100%%");
2147 } else if (timeval == 0) {
2148 snprintf(pb_battery_info[PB_BATT_PERCENT],
2149 sizeof(pb_battery_info[PB_BATT_PERCENT]), "unknown");
2151 snprintf(pb_battery_info[PB_BATT_PERCENT],
2152 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2153 (charge * 100) / max_charge);
2156 /* update time string */
2157 if (timeval == 0) { /* fully charged or battery not present */
2158 snprintf(pb_battery_info[PB_BATT_TIME],
2159 sizeof(pb_battery_info[PB_BATT_TIME]), "unknown");
2160 } else if (timeval < 60 * 60) { /* don't show secs */
2161 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2162 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2164 format_seconds(pb_battery_info[PB_BATT_TIME],
2165 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2168 snprintf(buffer, n, "%s", pb_battery_info[i]);
2171 void update_top(void)
2173 process_find_top(info.cpu, info.memu, info.time
2178 info.first_process = get_first_process();
2181 void update_entropy(void)
2184 const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2185 const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2188 info.entropy.entropy_avail = 0;
2189 info.entropy.poolsize = 0;
2191 if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2195 if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2200 fscanf(fp1, "%u", &info.entropy.entropy_avail);
2201 fscanf(fp2, "%u", &info.entropy.poolsize);
2207 const char *get_disk_protect_queue(const char *disk)
2213 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2214 if (access(path, F_OK)) {
2215 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2217 if ((fp = fopen(path, "r")) == NULL)
2219 if (fscanf(fp, "%d\n", &state) != 1) {
2224 return (state > 0) ? "frozen" : "free ";
2227 void update_diskio(void)
2231 char buf[512], devbuf[64];
2232 unsigned int major, minor;
2234 struct diskio_stat *cur;
2235 unsigned int reads, writes;
2236 unsigned int total_reads = 0, total_writes = 0;
2239 stats.current_read = 0;
2240 stats.current_write = 0;
2242 if (!(fp = open_file("/proc/diskstats", &rep))) {
2246 /* read reads and writes from all disks (minor = 0), including cd-roms
2247 * and floppies, and sum them up */
2248 while (fgets(buf, 512, fp)) {
2249 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
2250 &minor, devbuf, &reads, &writes);
2251 /* ignore subdevices (they have only 3 matching entries in their line)
2252 * and virtual devices (LVM, network block devices, RAM disks, Loopback)
2254 * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
2255 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
2256 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
2257 total_reads += reads;
2258 total_writes += writes;
2260 col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
2261 &major, &minor, devbuf, &reads, &writes);
2262 if (col_count != 5) {
2267 while (cur && strcmp(devbuf, cur->dev))
2271 update_diskio_values(cur, reads, writes);
2273 update_diskio_values(&stats, total_reads, total_writes);