1 /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
3 * Conky, a system monitor, based on torsmo
5 * Any original torsmo code is licensed under the BSD license
7 * All code written since the fork of torsmo is licensed under the GPL
9 * Please see COPYING for details
11 * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
12 * Copyright (c) 2007 Toni Spets
13 * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
15 * All rights reserved.
17 * This program is free software: you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation, either version 3 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29 * vim: ts=4 sw=4 noet ai cindent syntax=c
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);
119 info.mask |= (1 << INFO_UPTIME);
122 int check_mount(char *s)
125 FILE *mtab = fopen("/etc/mtab", "r");
128 char buf1[256], buf2[128];
130 while (fgets(buf1, 256, mtab)) {
131 sscanf(buf1, "%*s %128s", buf2);
132 if (!strcmp(s, buf2)) {
139 NORM_ERR("Could not open mtab");
144 /* these things are also in sysinfo except Buffers:
145 * (that's why I'm reading them from proc) */
147 void update_meminfo(void)
152 /* unsigned int a; */
155 info.mem = info.memmax = info.swap = info.swapfree = info.swapmax = info.bufmem =
156 info.buffers = info.cached = info.memfree = info.memeasyfree = 0;
158 if (!(meminfo_fp = open_file("/proc/meminfo", &rep))) {
162 while (!feof(meminfo_fp)) {
163 if (fgets(buf, 255, meminfo_fp) == NULL) {
167 if (strncmp(buf, "MemTotal:", 9) == 0) {
168 sscanf(buf, "%*s %llu", &info.memmax);
169 } else if (strncmp(buf, "MemFree:", 8) == 0) {
170 sscanf(buf, "%*s %llu", &info.memfree);
171 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
172 sscanf(buf, "%*s %llu", &info.swapmax);
173 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
174 sscanf(buf, "%*s %llu", &info.swapfree);
175 } else if (strncmp(buf, "Buffers:", 8) == 0) {
176 sscanf(buf, "%*s %llu", &info.buffers);
177 } else if (strncmp(buf, "Cached:", 7) == 0) {
178 sscanf(buf, "%*s %llu", &info.cached);
182 info.mem = info.memmax - info.memfree;
183 info.memeasyfree = info.memfree;
184 info.swap = info.swapmax - info.swapfree;
186 info.bufmem = info.cached + info.buffers;
188 info.mask |= (1 << INFO_MEM) | (1 << INFO_BUFFERS);
193 int get_laptop_mode(void)
198 if ((fp = fopen("/proc/sys/vm/laptop_mode", "r")) != NULL)
199 fscanf(fp, "%d\n", &val);
205 * # cat /sys/block/sda/queue/scheduler
206 * noop [anticipatory] cfq
208 char *get_ioscheduler(char *disk)
214 return strndup("n/a", text_buffer_size);
216 snprintf(buf, 127, "/sys/block/%s/queue/scheduler", disk);
217 if ((fp = fopen(buf, "r")) == NULL) {
218 return strndup("n/a", text_buffer_size);
221 fscanf(fp, "%127s", buf);
223 buf[strlen(buf) - 1] = '\0';
225 return strndup(buf + 1, text_buffer_size);
229 return strndup("n/a", text_buffer_size);
232 #define COND_FREE(x) if(x) free(x); x = 0
233 #define SAVE_SET_STRING(x, y) \
234 if (x && strcmp((char *)x, (char *)y)) { \
236 x = strndup("multiple", text_buffer_size); \
238 x = strndup(y, text_buffer_size); \
241 void update_gateway_info_failure(const char *reason)
246 //2 pointers to 1 location causes a crash when we try to free them both
247 info.gw_info.iface = strndup("failed", text_buffer_size);
248 info.gw_info.ip = strndup("failed", text_buffer_size);
252 /* Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT */
253 #define RT_ENTRY_FORMAT "%63s %lx %lx %x %*d %*d %*d %lx %*d %*d %*d\n"
255 void update_gateway_info(void)
260 unsigned long dest, gate, mask;
263 struct gateway_info *gw_info = &info.gw_info;
265 COND_FREE(gw_info->iface);
266 COND_FREE(gw_info->ip);
269 if ((fp = fopen("/proc/net/route", "r")) == NULL) {
270 update_gateway_info_failure("fopen()");
274 /* skip over the table header line, which is always present */
275 fscanf(fp, "%*[^\n]\n");
278 if(fscanf(fp, RT_ENTRY_FORMAT,
279 iface, &dest, &gate, &flags, &mask) != 5) {
280 update_gateway_info_failure("fscanf()");
283 if (!(dest || mask) && ((flags & RTF_GATEWAY) || !gate) ) {
285 SAVE_SET_STRING(gw_info->iface, iface)
287 SAVE_SET_STRING(gw_info->ip, inet_ntoa(ina))
294 void update_net_stats(void)
298 static char first = 1;
300 // FIXME: arbitrary size chosen to keep code simple.
302 unsigned int curtmp1, curtmp2;
309 // wireless info variables
310 int skfd, has_bitrate = 0;
311 struct wireless_info *winfo;
316 delta = current_update_time - last_update_time;
317 if (delta <= 0.0001) {
321 /* open file and ignore first two lines */
322 if (!(net_dev_fp = open_file("/proc/net/dev", &rep))) {
327 fgets(buf, 255, net_dev_fp); /* garbage */
328 fgets(buf, 255, net_dev_fp); /* garbage (field names) */
330 /* read each interface */
331 for (i2 = 0; i2 < 16; i2++) {
335 long long r, t, last_recv, last_trans;
337 if (fgets(buf, 255, net_dev_fp) == NULL) {
341 while (isspace((int) *p)) {
347 while (*p && *p != ':') {
356 ns = get_net_stat(s, NULL, NULL);
358 memset(&(ns->addr.sa_data), 0, 14);
360 memset(ns->addrs, 0, 17 * 16 + 1); /* Up to 17 chars per ip, max 16 interfaces. Nasty memory usage... */
362 last_recv = ns->recv;
363 last_trans = ns->trans;
365 /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
366 sscanf(p, "%lld %*d %*d %*d %*d %*d %*d %*d %lld",
369 /* if recv or trans is less than last time, an overflow happened */
370 if (r < ns->last_read_recv) {
373 ns->recv += (r - ns->last_read_recv);
375 ns->last_read_recv = r;
377 if (t < ns->last_read_trans) {
380 ns->trans += (t - ns->last_read_trans);
382 ns->last_read_trans = t;
384 /*** ip addr patch ***/
385 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
387 conf.ifc_buf = malloc(sizeof(struct ifreq) * 16);
388 conf.ifc_len = sizeof(struct ifreq) * 16;
389 memset(conf.ifc_buf, 0, conf.ifc_len);
391 ioctl((long) i, SIOCGIFCONF, &conf);
393 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
394 struct net_stat *ns2;
396 if (!(((struct ifreq *) conf.ifc_buf) + k))
400 ((struct ifreq *) conf.ifc_buf)[k].ifr_ifrn.ifrn_name, NULL, NULL);
401 ns2->addr = ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.ifru_addr;
402 sprintf(temp_addr, "%u.%u.%u.%u, ",
403 ns2->addr.sa_data[2] & 255,
404 ns2->addr.sa_data[3] & 255,
405 ns2->addr.sa_data[4] & 255,
406 ns2->addr.sa_data[5] & 255);
407 if(NULL == strstr(ns2->addrs, temp_addr))
408 strncpy(ns2->addrs + strlen(ns2->addrs), temp_addr, 17);
415 /*** end ip addr patch ***/
418 /* calculate speeds */
419 ns->net_rec[0] = (ns->recv - last_recv) / delta;
420 ns->net_trans[0] = (ns->trans - last_trans) / delta;
427 #pragma omp parallel for reduction(+:curtmp1, curtmp2) schedule(dynamic,10)
428 #endif /* HAVE_OPENMP */
429 for (i = 0; i < info.net_avg_samples; i++) {
430 curtmp1 = curtmp1 + ns->net_rec[i];
431 curtmp2 = curtmp2 + ns->net_trans[i];
439 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
440 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
441 if (info.net_avg_samples > 1) {
443 #pragma omp parallel for schedule(dynamic,10)
444 #endif /* HAVE_OPENMP */
445 for (i = info.net_avg_samples; i > 1; i--) {
446 ns->net_rec[i - 1] = ns->net_rec[i - 2];
447 ns->net_trans[i - 1] = ns->net_trans[i - 2];
452 /* update wireless info */
453 winfo = malloc(sizeof(struct wireless_info));
454 memset(winfo, 0, sizeof(struct wireless_info));
456 skfd = iw_sockets_open();
457 if (iw_get_basic_config(skfd, s, &(winfo->b)) > -1) {
459 // set present winfo variables
460 if (iw_get_stats(skfd, s, &(winfo->stats),
461 &winfo->range, winfo->has_range) >= 0) {
462 winfo->has_stats = 1;
464 if (iw_get_range_info(skfd, s, &(winfo->range)) >= 0) {
465 winfo->has_range = 1;
467 if (iw_get_ext(skfd, s, SIOCGIWAP, &wrq) >= 0) {
468 winfo->has_ap_addr = 1;
469 memcpy(&(winfo->ap_addr), &(wrq.u.ap_addr), sizeof(sockaddr));
473 if (iw_get_ext(skfd, s, SIOCGIWRATE, &wrq) >= 0) {
474 memcpy(&(winfo->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
475 iw_print_bitrate(ns->bitrate, 16, winfo->bitrate.value);
480 if (winfo->has_range && winfo->has_stats
481 && ((winfo->stats.qual.level != 0)
482 || (winfo->stats.qual.updated & IW_QUAL_DBM))) {
483 if (!(winfo->stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
484 ns->link_qual = winfo->stats.qual.qual;
485 ns->link_qual_max = winfo->range.max_qual.qual;
490 if (winfo->has_ap_addr) {
491 iw_sawap_ntop(&winfo->ap_addr, ns->ap);
495 if (winfo->b.has_essid) {
496 if (winfo->b.essid_on) {
497 snprintf(ns->essid, 32, "%s", winfo->b.essid);
499 snprintf(ns->essid, 32, "off/any");
503 snprintf(ns->mode, 16, "%s", iw_operation_mode[winfo->b.mode]);
505 iw_sockets_close(skfd);
513 info.mask |= (1 << INFO_NET);
518 void update_total_processes(void)
522 struct sysinfo s_info;
525 info.procs = s_info.procs;
532 if (!(fp = open_file("/proc/loadavg", &rep))) {
536 fscanf(fp, "%*f %*f %*f %*d/%hu", &info.procs);
539 info.mask |= (1 << INFO_PROCS);
542 #define CPU_SAMPLE_COUNT 15
544 unsigned long long cpu_user;
545 unsigned long long cpu_system;
546 unsigned long long cpu_nice;
547 unsigned long long cpu_idle;
548 unsigned long long cpu_iowait;
549 unsigned long long cpu_irq;
550 unsigned long long cpu_softirq;
551 unsigned long long cpu_steal;
552 unsigned long long cpu_total;
553 unsigned long long cpu_active_total;
554 unsigned long long cpu_last_total;
555 unsigned long long cpu_last_active_total;
556 double cpu_val[CPU_SAMPLE_COUNT];
558 static short cpu_setup = 0;
560 /* Determine if this kernel gives us "extended" statistics information in
562 * Kernels around 2.5 and earlier only reported user, system, nice, and
563 * idle values in proc stat.
564 * Kernels around 2.6 and greater report these PLUS iowait, irq, softirq,
566 void determine_longstat(char *buf)
568 unsigned long long iowait = 0;
570 KFLAG_SETOFF(KFLAG_IS_LONGSTAT);
571 /* scanf will either return -1 or 1 because there is only 1 assignment */
572 if (sscanf(buf, "%*s %*d %*d %*d %*d %llu", &iowait) > 0) {
573 KFLAG_SETON(KFLAG_IS_LONGSTAT);
577 void get_cpu_count(void)
583 if (info.cpu_usage) {
587 if (!(stat_fp = open_file("/proc/stat", &rep))) {
593 while (!feof(stat_fp)) {
594 if (fgets(buf, 255, stat_fp) == NULL) {
598 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
599 if (info.cpu_count == 0) {
600 determine_longstat(buf);
605 info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
610 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
611 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
613 static void *global_cpu = 0;
615 void clear_cpu_stats(void)
623 inline static void update_stat(void)
627 static struct cpu_info *cpu = NULL;
632 const char *stat_template = NULL;
633 unsigned int malloc_cpu_size = 0;
635 /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
636 if (!cpu_setup || !info.cpu_usage) {
641 if (!stat_template) {
643 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
647 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
648 cpu = malloc(malloc_cpu_size);
649 memset(cpu, 0, malloc_cpu_size);
653 if (!(stat_fp = open_file("/proc/stat", &rep))) {
655 if (info.cpu_usage) {
656 memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
662 while (!feof(stat_fp)) {
663 if (fgets(buf, 255, stat_fp) == NULL) {
667 if (strncmp(buf, "procs_running ", 14) == 0) {
668 sscanf(buf, "%*s %hu", &info.run_procs);
669 info.mask |= (1 << INFO_RUN_PROCS);
670 } else if (strncmp(buf, "cpu", 3) == 0) {
672 if (isdigit(buf[3])) {
673 idx = atoi(&buf[3]) + 1;
677 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
678 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
679 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
680 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
681 &(cpu[idx].cpu_steal));
683 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
684 cpu[idx].cpu_system + cpu[idx].cpu_idle +
685 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
686 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
688 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
689 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
690 info.mask |= (1 << INFO_CPU);
692 delta = current_update_time - last_update_time;
694 if (delta <= 0.001) {
698 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
699 cpu[idx].cpu_last_active_total) /
700 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
703 #pragma omp parallel for reduction(+:curtmp) schedule(dynamic,10)
704 #endif /* HAVE_OPENMP */
705 for (i = 0; i < info.cpu_avg_samples; i++) {
706 curtmp = curtmp + cpu[idx].cpu_val[i];
708 /* TESTING -- I've removed this, because I don't think it is right.
709 * You shouldn't divide by the cpu count here ...
710 * removing for testing */
712 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
715 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
717 /* TESTING -- this line replaces the prev. "suspect" if/else */
718 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
720 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
721 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
723 #pragma omp parallel for schedule(dynamic,10)
724 #endif /* HAVE_OPENMP */
725 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
726 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
733 void update_running_processes(void)
738 void update_cpu_usage(void)
743 void update_load_average(void)
745 #ifdef HAVE_GETLOADAVG
750 info.loadavg[0] = (float) v[0];
751 info.loadavg[1] = (float) v[1];
752 info.loadavg[2] = (float) v[2];
759 if (!(fp = open_file("/proc/loadavg", &rep))) {
760 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
763 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
767 info.mask |= (1 << INFO_LOADAVG);
770 #define PROC_I8K "/proc/i8k"
771 #define I8K_DELIM " "
772 static char *i8k_procbuf = NULL;
773 void update_i8k(void)
778 i8k_procbuf = (char *) malloc(128 * sizeof(char));
780 if ((fp = fopen(PROC_I8K, "r")) == NULL) {
781 CRIT_ERR(NULL, NULL, "/proc/i8k doesn't exist! use insmod to make sure the kernel "
782 "driver is loaded...");
785 memset(&i8k_procbuf[0], 0, 128);
786 if (fread(&i8k_procbuf[0], sizeof(char), 128, fp) == 0) {
787 NORM_ERR("something wrong with /proc/i8k...");
792 i8k.version = strtok(&i8k_procbuf[0], I8K_DELIM);
793 i8k.bios = strtok(NULL, I8K_DELIM);
794 i8k.serial = strtok(NULL, I8K_DELIM);
795 i8k.cpu_temp = strtok(NULL, I8K_DELIM);
796 i8k.left_fan_status = strtok(NULL, I8K_DELIM);
797 i8k.right_fan_status = strtok(NULL, I8K_DELIM);
798 i8k.left_fan_rpm = strtok(NULL, I8K_DELIM);
799 i8k.right_fan_rpm = strtok(NULL, I8K_DELIM);
800 i8k.ac_status = strtok(NULL, I8K_DELIM);
801 i8k.buttons_status = strtok(NULL, I8K_DELIM);
804 /***********************************************************/
805 /***********************************************************/
806 /***********************************************************/
808 static int no_dots(const struct dirent *d)
810 if (d->d_name[0] == '.') {
816 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
818 struct dirent **namelist;
821 n = scandir(dir, &namelist, no_dots, alphasort);
824 NORM_ERR("scandir for %s: %s", dir, strerror(errno));
835 strncpy(s, namelist[0]->d_name, 255);
839 #pragma omp parallel for schedule(dynamic,10)
840 #endif /* HAVE_OPENMP */
841 for (i = 0; i < n; i++) {
850 int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
851 int *divisor, char *devtype)
859 memset(buf, 0, sizeof(buf));
861 /* if device is NULL or *, get first */
862 if (dev == NULL || strcmp(dev, "*") == 0) {
865 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
871 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
873 /* buf holds result from get_first_file_in_a_directory() above,
874 * e.g. "hwmon0" -- append "/device" */
875 strcat(buf, "/device");
877 /* dev holds device number N as a string,
878 * e.g. "0", -- convert to "hwmon0/device" */
879 sprintf(buf, "hwmon%s/device", dev);
884 /* At least the acpitz hwmon doesn't have a 'device' subdir,
885 * so check it's existence and strip it from buf otherwise. */
886 snprintf(path, 255, "%s%s", dir, dev);
887 if (stat(path, &st)) {
888 buf[strlen(buf) - 7] = 0;
891 /* change vol to in, tempf to temp */
892 if (strcmp(type, "vol") == 0) {
894 } else if (strcmp(type, "tempf") == 0) {
898 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
899 strncpy(devtype, path, 255);
902 fd = open(path, O_RDONLY);
904 CRIT_ERR(NULL, NULL, "can't open '%s': %s\nplease check your device or remove this "
905 "var from "PACKAGE_NAME, path, strerror(errno));
908 if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
909 || strcmp(type, "tempf") == 0) {
914 /* fan does not use *_div as a read divisor */
915 if (strcmp("fan", type) == 0) {
919 /* test if *_div file exist, open it and use it as divisor */
920 if (strcmp(type, "tempf") == 0) {
921 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
923 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
926 divfd = open(path, O_RDONLY);
932 divn = read(divfd, divbuf, 63);
933 /* should read until n == 0 but I doubt that kernel will give these
934 * in multiple pieces. :) */
936 NORM_ERR("open_sysfs_sensor(): can't read from sysfs");
939 *divisor = atoi(divbuf);
947 double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
955 lseek(*fd, 0, SEEK_SET);
961 n = read(*fd, buf, 63);
962 /* should read until n == 0 but I doubt that kernel will give these
963 * in multiple pieces. :) */
965 NORM_ERR("get_sysfs_info(): read from %s failed\n", devtype);
974 *fd = open(devtype, O_RDONLY);
976 NORM_ERR("can't open '%s': %s", devtype, strerror(errno));
979 /* My dirty hack for computing CPU value
980 * Filedil, from forums.gentoo.org */
981 /* if (strstr(devtype, "temp1_input") != NULL) {
982 return -15.096 + 1.4893 * (val / 1000.0);
985 /* divide voltage and temperature by 1000 */
986 /* or if any other divisor is given, use that */
987 if (strcmp(type, "tempf") == 0) {
989 return ((val / divisor + 40) * 9.0 / 5) - 40;
990 } else if (divisor) {
991 return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
993 return ((val + 40) * 9.0 / 5) - 40;
997 return val / divisor;
998 } else if (divisor) {
1006 /* Prior to kernel version 2.6.12, the CPU fan speed was available in
1007 * ADT746X_FAN_OLD, whereas later kernel versions provide this information in
1009 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
1010 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
1012 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
1015 char adt746x_fan_state[64];
1018 if (!p_client_buffer || client_buffer_size <= 0) {
1022 if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
1023 && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL) {
1024 sprintf(adt746x_fan_state, "adt746x not found");
1026 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
1027 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
1031 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_fan_state);
1034 /* Prior to kernel version 2.6.12, the CPU temperature was found in
1035 * ADT746X_CPU_OLD, whereas later kernel versions provide this information in
1037 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
1038 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
1040 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
1043 char adt746x_cpu_state[64];
1046 if (!p_client_buffer || client_buffer_size <= 0) {
1050 if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
1051 && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL) {
1052 sprintf(adt746x_cpu_state, "adt746x not found");
1054 fscanf(fp, "%2s", adt746x_cpu_state);
1058 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state);
1061 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1062 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1064 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1065 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1066 const char *p_format, int divisor, unsigned int cpu)
1074 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1080 char current_freq_file[128];
1082 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1084 f = fopen(current_freq_file, "r");
1086 /* if there's a cpufreq /sys node, read the current frequency from
1087 * this node and divide by 1000 to get Mhz. */
1088 if (fgets(s, sizeof(s), f)) {
1089 s[strlen(s) - 1] = '\0';
1090 freq = strtod(s, NULL);
1093 snprintf(p_client_buffer, client_buffer_size, p_format,
1094 (freq / 1000) / divisor);
1099 // open the CPU information file
1100 f = open_file("/proc/cpuinfo", &rep);
1102 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1107 while (fgets(s, sizeof(s), f) != NULL) {
1109 #if defined(__i386) || defined(__x86_64)
1110 // and search for the cpu mhz
1111 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1113 #if defined(__alpha)
1114 // different on alpha
1115 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1117 // this is different on ppc for some reason
1118 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1119 #endif // defined(__alpha)
1120 #endif // defined(__i386) || defined(__x86_64)
1122 // copy just the number
1123 strcpy(frequency, strchr(s, ':') + 2);
1124 #if defined(__alpha)
1126 frequency[strlen(frequency) - 6] = '\0';
1127 // kernel reports in Hz
1128 freq = strtod(frequency, NULL) / 1000000;
1131 frequency[strlen(frequency) - 1] = '\0';
1132 freq = strtod(frequency, NULL);
1136 if (strncmp(s, "processor", 9) == 0) {
1143 snprintf(p_client_buffer, client_buffer_size, p_format,
1144 (float) freq / divisor);
1148 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1150 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1160 * Peter Tarjan (ptarjan@citromail.hu) */
1162 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1163 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1164 const char *p_format, int divisor, unsigned int cpu)
1170 char current_freq_file[128];
1173 /* build the voltage file name */
1175 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1178 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1183 /* read the current cpu frequency from the /sys node */
1184 f = fopen(current_freq_file, "r");
1186 if (fgets(s, sizeof(s), f)) {
1187 s[strlen(s) - 1] = '\0';
1188 freq = strtod(s, NULL);
1192 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1193 perror("get_voltage()");
1200 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1203 /* use the current cpu frequency to find the corresponding voltage */
1204 f = fopen(current_freq_file, "r");
1210 if (fgets(line, 255, f) == NULL) {
1213 sscanf(line, "%d %d", &freq_comp, &voltage);
1214 if (freq_comp == freq) {
1220 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1221 perror("get_voltage()");
1227 snprintf(p_client_buffer, client_buffer_size, p_format,
1228 (float) voltage / divisor);
1232 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1234 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1241 if (!p_client_buffer || client_buffer_size <= 0) {
1245 /* yeah, slow... :/ */
1246 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1247 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1251 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1253 fp = open_file(buf2, &rep);
1255 snprintf(p_client_buffer, client_buffer_size,
1256 "can't open fan's state file");
1259 memset(buf, 0, sizeof(buf));
1260 fscanf(fp, "%*s %99s", buf);
1263 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1266 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1267 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1268 /* Linux 2.6.25 onwards ac adapter info is in
1269 /sys/class/power_supply/AC/
1270 On my system I get the following.
1271 /sys/class/power_supply/AC/uevent:
1272 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1275 POWER_SUPPLY_NAME=AC
1276 POWER_SUPPLY_TYPE=Mains
1277 POWER_SUPPLY_ONLINE=1
1280 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1288 if (!p_client_buffer || client_buffer_size <= 0) {
1292 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1293 fp = open_file(buf2, &rep);
1295 /* sysfs processing */
1297 if (fgets(buf, sizeof(buf), fp) == NULL)
1300 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1302 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1303 snprintf(p_client_buffer, client_buffer_size,
1304 "%s-line", (online ? "on" : "off"));
1310 /* yeah, slow... :/ */
1311 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1312 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1316 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1318 fp = open_file(buf2, &rep);
1320 snprintf(p_client_buffer, client_buffer_size,
1321 "No ac adapter found.... where is it?");
1324 memset(buf, 0, sizeof(buf));
1325 fscanf(fp, "%*s %99s", buf);
1328 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1333 /proc/acpi/thermal_zone/THRM/cooling_mode
1334 cooling mode: active
1335 /proc/acpi/thermal_zone/THRM/polling_frequency
1337 /proc/acpi/thermal_zone/THRM/state
1339 /proc/acpi/thermal_zone/THRM/temperature
1341 /proc/acpi/thermal_zone/THRM/trip_points
1343 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1346 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1347 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1349 int open_acpi_temperature(const char *name)
1355 if (name == NULL || strcmp(name, "*") == 0) {
1358 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1364 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1366 fd = open(path, O_RDONLY);
1368 NORM_ERR("can't open '%s': %s", path, strerror(errno));
1374 static double last_acpi_temp;
1375 static double last_acpi_temp_time;
1377 double get_acpi_temperature(int fd)
1383 /* don't update acpi temperature too often */
1384 if (current_update_time - last_acpi_temp_time < 11.32) {
1385 return last_acpi_temp;
1387 last_acpi_temp_time = current_update_time;
1389 /* seek to beginning */
1390 lseek(fd, 0, SEEK_SET);
1397 n = read(fd, buf, 255);
1399 NORM_ERR("can't read fd %d: %s", fd, strerror(errno));
1402 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1406 return last_acpi_temp;
1410 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1412 design capacity: 4400 mAh
1413 last full capacity: 4064 mAh
1414 battery technology: rechargeable
1415 design voltage: 14800 mV
1416 design capacity warning: 300 mAh
1417 design capacity low: 200 mAh
1418 capacity granularity 1: 32 mAh
1419 capacity granularity 2: 32 mAh
1421 serial number: 16922
1427 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1430 charging state: unknown
1432 remaining capacity: 4064 mAh
1433 present voltage: 16608 mV
1437 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1438 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1439 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1440 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1441 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1443 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1444 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1446 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1447 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1450 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1451 Linux 2.6.24 onwards battery info is in
1452 /sys/class/power_supply/BAT0/
1453 On my system I get the following.
1454 /sys/class/power_supply/BAT0/uevent:
1455 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1457 PHYSDEVDRIVER=battery
1458 POWER_SUPPLY_NAME=BAT0
1459 POWER_SUPPLY_TYPE=Battery
1460 POWER_SUPPLY_STATUS=Discharging
1461 POWER_SUPPLY_PRESENT=1
1462 POWER_SUPPLY_TECHNOLOGY=Li-ion
1463 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1464 POWER_SUPPLY_VOLTAGE_NOW=10780000
1465 POWER_SUPPLY_CURRENT_NOW=13970000
1466 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1467 POWER_SUPPLY_ENERGY_FULL=27370000
1468 POWER_SUPPLY_ENERGY_NOW=11810000
1469 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1470 POWER_SUPPLY_MANUFACTURER=Panasonic
1471 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1474 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1475 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1476 #define APM_PATH "/proc/apm"
1477 #define MAX_BATTERY_COUNT 4
1479 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1480 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1481 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1483 static int batteries_initialized = 0;
1484 static char batteries[MAX_BATTERY_COUNT][32];
1486 static int acpi_last_full[MAX_BATTERY_COUNT];
1487 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1489 /* e.g. "charging 75%" */
1490 static char last_battery_str[MAX_BATTERY_COUNT][64];
1492 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1494 static double last_battery_time[MAX_BATTERY_COUNT];
1496 static int last_battery_perct[MAX_BATTERY_COUNT];
1497 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1499 void init_batteries(void)
1503 if (batteries_initialized) {
1507 #pragma omp parallel for schedule(dynamic,10)
1508 #endif /* HAVE_OPENMP */
1509 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1510 batteries[idx][0] = '\0';
1512 batteries_initialized = 1;
1515 int get_battery_idx(const char *bat)
1519 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1520 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1525 /* if not found, enter a new entry */
1526 if (!strlen(batteries[idx])) {
1527 snprintf(batteries[idx], 31, "%s", bat);
1533 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1535 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1537 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1538 char acpi_path[128];
1539 char sysfs_path[128];
1541 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1542 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1546 idx = get_battery_idx(bat);
1548 /* don't update battery too often */
1549 if (current_update_time - last_battery_time[idx] < 29.5) {
1550 set_return_value(buffer, n, item, idx);
1554 last_battery_time[idx] = current_update_time;
1556 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1557 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1559 /* first try SYSFS if that fails try ACPI */
1561 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1562 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1565 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1566 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1569 if (sysfs_bat_fp[idx] != NULL) {
1571 int present_rate = -1;
1572 int remaining_capacity = -1;
1573 char charging_state[64];
1576 strcpy(charging_state, "unknown");
1578 while (!feof(sysfs_bat_fp[idx])) {
1580 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1583 /* let's just hope units are ok */
1584 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1585 strcpy(present, "yes");
1586 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1587 strcpy(present, "no");
1588 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1589 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1590 /* present_rate is not the same as the
1591 current flowing now but it is the same value
1592 which was used in the past. so we continue
1594 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1595 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1596 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1597 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1598 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1599 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1600 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1601 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1602 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1603 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1606 fclose(sysfs_bat_fp[idx]);
1607 sysfs_bat_fp[idx] = NULL;
1609 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1610 if (remaining_capacity > acpi_last_full[idx])
1611 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1614 if (strcmp(present, "No") == 0) {
1615 strncpy(last_battery_str[idx], "not present", 64);
1618 else if (strcmp(charging_state, "Charging") == 0) {
1619 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1620 /* e.g. charging 75% */
1621 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1622 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1624 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1625 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1626 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1627 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1628 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1629 snprintf(last_battery_time_str[idx],
1630 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1632 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1633 snprintf(last_battery_time_str[idx],
1634 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1638 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1639 if (present_rate > 0) {
1640 /* e.g. discharging 35% */
1641 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1642 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1644 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1645 (long) (((float) remaining_capacity / present_rate) * 3600));
1646 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1647 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1648 snprintf(last_battery_time_str[idx],
1649 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1651 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1653 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1654 snprintf(last_battery_time_str[idx],
1655 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1659 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1660 else if (strncmp(charging_state, "Charged", 64) == 0 || strncmp(charging_state, "Full", 64) == 0) {
1661 /* Below happens with the second battery on my X40,
1662 * when the second one is empty and the first one
1664 if (remaining_capacity == 0)
1665 strcpy(last_battery_str[idx], "empty");
1667 strcpy(last_battery_str[idx], "charged");
1669 /* unknown, probably full / AC */
1671 if (acpi_last_full[idx] != 0
1672 && remaining_capacity != acpi_last_full[idx])
1673 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1674 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1676 strncpy(last_battery_str[idx], "AC", 64);
1678 } else if (acpi_bat_fp[idx] != NULL) {
1680 int present_rate = -1;
1681 int remaining_capacity = -1;
1682 char charging_state[64];
1685 /* read last full capacity if it's zero */
1686 if (acpi_last_full[idx] == 0) {
1687 static int rep3 = 0;
1691 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1692 fp = open_file(path, &rep3);
1697 if (fgets(b, 256, fp) == NULL) {
1700 if (sscanf(b, "last full capacity: %d",
1701 &acpi_last_full[idx]) != 0) {
1710 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1712 strcpy(charging_state, "unknown");
1714 while (!feof(acpi_bat_fp[idx])) {
1717 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1721 /* let's just hope units are ok */
1722 if (strncmp(buf, "present:", 8) == 0) {
1723 sscanf(buf, "present: %4s", present);
1724 } else if (strncmp(buf, "charging state:", 15) == 0) {
1725 sscanf(buf, "charging state: %63s", charging_state);
1726 } else if (strncmp(buf, "present rate:", 13) == 0) {
1727 sscanf(buf, "present rate: %d", &present_rate);
1728 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1729 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1732 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1733 if (remaining_capacity > acpi_last_full[idx]) {
1734 /* normalize to 100% */
1735 acpi_last_full[idx] = remaining_capacity;
1739 if (strcmp(present, "no") == 0) {
1740 strncpy(last_battery_str[idx], "not present", 64);
1742 } else if (strcmp(charging_state, "charging") == 0) {
1743 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1744 /* e.g. charging 75% */
1745 snprintf(last_battery_str[idx],
1746 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1747 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1749 format_seconds(last_battery_time_str[idx],
1750 sizeof(last_battery_time_str[idx]) - 1,
1751 (long) (((acpi_last_full[idx] - remaining_capacity) *
1752 3600) / present_rate));
1753 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1754 snprintf(last_battery_str[idx],
1755 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1756 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1757 snprintf(last_battery_time_str[idx],
1758 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1760 strncpy(last_battery_str[idx], "charging",
1761 sizeof(last_battery_str[idx]) - 1);
1762 snprintf(last_battery_time_str[idx],
1763 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1766 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1767 if (present_rate > 0) {
1768 /* e.g. discharging 35% */
1769 snprintf(last_battery_str[idx],
1770 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1771 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1773 format_seconds(last_battery_time_str[idx],
1774 sizeof(last_battery_time_str[idx]) - 1,
1775 (long) ((remaining_capacity * 3600) / present_rate));
1776 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1777 snprintf(last_battery_str[idx],
1778 sizeof(last_battery_str[idx]) - 1, "full");
1779 snprintf(last_battery_time_str[idx],
1780 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1782 snprintf(last_battery_str[idx],
1783 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1784 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1785 snprintf(last_battery_time_str[idx],
1786 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1789 } else if (strncmp(charging_state, "charged", 64) == 0) {
1790 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1791 /* Below happens with the second battery on my X40,
1792 * when the second one is empty and the first one being charged. */
1793 if (remaining_capacity == 0) {
1794 strcpy(last_battery_str[idx], "empty");
1796 strcpy(last_battery_str[idx], "charged");
1798 /* unknown, probably full / AC */
1800 if (strncmp(charging_state, "Full", 64) == 0) {
1801 strncpy(last_battery_str[idx], "full", 64);
1802 } else if (acpi_last_full[idx] != 0
1803 && remaining_capacity != acpi_last_full[idx]) {
1804 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1805 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1807 strncpy(last_battery_str[idx], "AC", 64);
1810 fclose(acpi_bat_fp[idx]);
1811 acpi_bat_fp[idx] = NULL;
1814 if (apm_bat_fp[idx] == NULL) {
1815 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1818 if (apm_bat_fp[idx] != NULL) {
1819 unsigned int ac, status, flag;
1822 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1823 &ac, &status, &flag, &life);
1826 /* could check now that there is ac */
1827 snprintf(last_battery_str[idx], 64, "AC");
1829 /* could check that status == 3 here? */
1830 } else if (ac && life != 100) {
1831 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1833 snprintf(last_battery_str[idx], 64, "%d%%", life);
1836 /* it seemed to buffer it so file must be closed (or could use
1837 * syscalls directly but I don't feel like coding it now) */
1838 fclose(apm_bat_fp[idx]);
1839 apm_bat_fp[idx] = NULL;
1842 set_return_value(buffer, n, item, idx);
1845 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1848 case BATTERY_STATUS:
1849 snprintf(buffer, n, "%s", last_battery_str[idx]);
1852 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1859 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1861 get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1862 if (0 == strncmp("charging", buffer, 8)) {
1864 memmove(buffer + 1, buffer + 8, n - 8);
1865 } else if (0 == strncmp("discharging", buffer, 11)) {
1867 memmove(buffer + 1, buffer + 11, n - 11);
1868 } else if (0 == strncmp("charged", buffer, 7)) {
1870 memmove(buffer + 1, buffer + 7, n - 7);
1871 } else if (0 == strncmp("not present", buffer, 11)) {
1873 memmove(buffer + 1, buffer + 11, n - 11);
1874 } else if (0 == strncmp("empty", buffer, 5)) {
1876 memmove(buffer + 1, buffer + 5, n - 5);
1877 } else if (0 != strncmp("AC", buffer, 2)) {
1879 memmove(buffer + 1, buffer + 11, n - 11);
1883 int get_battery_perct(const char *bat)
1887 char acpi_path[128];
1888 char sysfs_path[128];
1889 int remaining_capacity = -1;
1891 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1892 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1896 idx = get_battery_idx(bat);
1898 /* don't update battery too often */
1899 if (current_update_time - last_battery_perct_time[idx] < 30) {
1900 return last_battery_perct[idx];
1902 last_battery_perct_time[idx] = current_update_time;
1904 /* Only check for SYSFS or ACPI */
1906 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1907 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1911 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1912 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1915 if (sysfs_bat_fp[idx] != NULL) {
1917 while (!feof(sysfs_bat_fp[idx])) {
1919 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1922 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1923 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1924 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1925 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1926 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1927 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1928 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1929 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1933 fclose(sysfs_bat_fp[idx]);
1934 sysfs_bat_fp[idx] = NULL;
1936 } else if (acpi_bat_fp[idx] != NULL) {
1938 /* read last full capacity if it's zero */
1939 if (acpi_design_capacity[idx] == 0) {
1944 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1945 fp = open_file(path, &rep2);
1950 if (fgets(b, 256, fp) == NULL) {
1953 if (sscanf(b, "last full capacity: %d",
1954 &acpi_design_capacity[idx]) != 0) {
1962 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1964 while (!feof(acpi_bat_fp[idx])) {
1967 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1971 if (buf[0] == 'r') {
1972 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1976 if (remaining_capacity < 0) {
1979 /* compute the battery percentage */
1980 last_battery_perct[idx] =
1981 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
1982 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
1983 return last_battery_perct[idx];
1986 int get_battery_perct_bar(const char *bar)
1990 get_battery_perct(bar);
1991 idx = get_battery_idx(bar);
1992 return (int) (last_battery_perct[idx] * 2.56 - 1);
1995 /* On Apple powerbook and ibook:
1996 $ cat /proc/pmu/battery_0
2003 $ cat /proc/pmu/info
2004 PMU driver version : 2
2005 PMU firmware version : 0c
2010 /* defines as in <linux/pmu.h> */
2011 #define PMU_BATT_PRESENT 0x00000001
2012 #define PMU_BATT_CHARGING 0x00000002
2014 static FILE *pmu_battery_fp;
2015 static FILE *pmu_info_fp;
2016 static char pb_battery_info[3][32];
2017 static double pb_battery_info_update;
2019 #define PMU_PATH "/proc/pmu"
2020 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2023 const char *batt_path = PMU_PATH "/battery_0";
2024 const char *info_path = PMU_PATH "/info";
2026 int charge, max_charge, ac = -1;
2029 /* don't update battery too often */
2030 if (current_update_time - pb_battery_info_update < 29.5) {
2031 snprintf(buffer, n, "%s", pb_battery_info[i]);
2034 pb_battery_info_update = current_update_time;
2036 if (pmu_battery_fp == NULL) {
2037 pmu_battery_fp = open_file(batt_path, &rep);
2038 if (pmu_battery_fp == NULL) {
2043 if (pmu_battery_fp != NULL) {
2044 rewind(pmu_battery_fp);
2045 while (!feof(pmu_battery_fp)) {
2048 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2052 if (buf[0] == 'f') {
2053 sscanf(buf, "flags : %8x", &flags);
2054 } else if (buf[0] == 'c' && buf[1] == 'h') {
2055 sscanf(buf, "charge : %d", &charge);
2056 } else if (buf[0] == 'm') {
2057 sscanf(buf, "max_charge : %d", &max_charge);
2058 } else if (buf[0] == 't') {
2059 sscanf(buf, "time rem. : %ld", &timeval);
2063 if (pmu_info_fp == NULL) {
2064 pmu_info_fp = open_file(info_path, &rep);
2065 if (pmu_info_fp == NULL) {
2070 if (pmu_info_fp != NULL) {
2071 rewind(pmu_info_fp);
2072 while (!feof(pmu_info_fp)) {
2075 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2078 if (buf[0] == 'A') {
2079 sscanf(buf, "AC Power : %d", &ac);
2083 /* update status string */
2084 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2085 strncpy(pb_battery_info[PB_BATT_STATUS], "AC", sizeof(pb_battery_info[PB_BATT_STATUS]));
2086 } else if (ac && (flags & PMU_BATT_PRESENT)
2087 && !(flags & PMU_BATT_CHARGING)) {
2088 strncpy(pb_battery_info[PB_BATT_STATUS], "charged", sizeof(pb_battery_info[PB_BATT_STATUS]));
2089 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2090 strncpy(pb_battery_info[PB_BATT_STATUS], "charging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2092 strncpy(pb_battery_info[PB_BATT_STATUS], "discharging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2095 /* update percentage string */
2096 if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT)
2097 && !(flags & PMU_BATT_CHARGING)) {
2098 snprintf(pb_battery_info[PB_BATT_PERCENT],
2099 sizeof(pb_battery_info[PB_BATT_PERCENT]), "100%%");
2100 } else if (timeval == 0) {
2101 snprintf(pb_battery_info[PB_BATT_PERCENT],
2102 sizeof(pb_battery_info[PB_BATT_PERCENT]), "unknown");
2104 snprintf(pb_battery_info[PB_BATT_PERCENT],
2105 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2106 (charge * 100) / max_charge);
2109 /* update time string */
2110 if (timeval == 0) { /* fully charged or battery not present */
2111 snprintf(pb_battery_info[PB_BATT_TIME],
2112 sizeof(pb_battery_info[PB_BATT_TIME]), "unknown");
2113 } else if (timeval < 60 * 60) { /* don't show secs */
2114 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2115 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2117 format_seconds(pb_battery_info[PB_BATT_TIME],
2118 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2121 snprintf(buffer, n, "%s", pb_battery_info[i]);
2124 void update_top(void)
2126 process_find_top(info.cpu, info.memu, info.time
2131 info.first_process = get_first_process();
2134 void update_entropy(void)
2137 const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2138 const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2141 info.entropy.entropy_avail = 0;
2142 info.entropy.poolsize = 0;
2144 if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2148 if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2153 fscanf(fp1, "%u", &info.entropy.entropy_avail);
2154 fscanf(fp2, "%u", &info.entropy.poolsize);
2159 info.mask |= (1 << INFO_ENTROPY);
2162 const char *get_disk_protect_queue(const char *disk)
2168 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2169 if (access(path, F_OK)) {
2170 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2172 if ((fp = fopen(path, "r")) == NULL)
2174 if (fscanf(fp, "%d\n", &state) != 1) {
2179 return (state > 0) ? "frozen" : "free ";
2182 void update_diskio(void)
2186 char buf[512], devbuf[64];
2187 unsigned int major, minor;
2189 struct diskio_stat *cur;
2190 unsigned int reads, writes;
2191 unsigned int total_reads = 0, total_writes = 0;
2194 stats.current_read = 0;
2195 stats.current_write = 0;
2197 if (!(fp = open_file("/proc/diskstats", &rep))) {
2201 /* read reads and writes from all disks (minor = 0), including cd-roms
2202 * and floppies, and sum them up */
2203 while (fgets(buf, 512, fp)) {
2204 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
2205 &minor, devbuf, &reads, &writes);
2206 /* ignore subdevices (they have only 3 matching entries in their line)
2207 * and virtual devices (LVM, network block devices, RAM disks, Loopback)
2209 * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
2210 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
2211 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
2212 total_reads += reads;
2213 total_writes += writes;
2215 col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
2216 &major, &minor, devbuf, &reads, &writes);
2217 if (col_count != 5) {
2222 while (cur && strcmp(devbuf, cur->dev))
2226 update_diskio_values(cur, reads, writes);
2228 update_diskio_values(&stats, total_reads, total_writes);