1 /* Conky, a system monitor, based on torsmo
3 * Any original torsmo code is licensed under the BSD license
5 * All code written since the fork of torsmo is licensed under the GPL
7 * Please see COPYING for details
9 * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
10 * Copyright (c) 2007 Toni Spets
11 * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
13 * All rights reserved.
15 * This program is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation, either version 3 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27 * vim: ts=4 sw=4 noet ai cindent syntax=c
40 #include <sys/types.h>
41 #include <sys/sysinfo.h>
43 #ifndef HAVE_CLOCK_GETTIME
48 // #include <assert.h>
52 #include <sys/ioctl.h>
53 #include <sys/socket.h>
54 #include <netinet/in.h>
55 #include <linux/sockios.h>
57 #include <arpa/inet.h>
61 #include <linux/route.h>
64 /* The following ifdefs were adapted from gkrellm */
65 #include <linux/major.h>
67 #if !defined(MD_MAJOR)
71 #if !defined(LVM_BLK_MAJOR)
72 #define LVM_BLK_MAJOR 58
75 #if !defined(NBD_MAJOR)
83 #define SHORTSTAT_TEMPL "%*s %llu %llu %llu"
84 #define LONGSTAT_TEMPL "%*s %llu %llu %llu "
86 /* This flag tells the linux routines to use the /proc system where possible,
87 * even if other api's are available, e.g. sysinfo() or getloadavg().
88 * the reason for this is to allow for /proc-based distributed monitoring.
89 * using a flag in this manner creates less confusing code. */
90 static int prefer_proc = 0;
92 void prepare_update(void)
96 void update_uptime(void)
100 struct sysinfo s_info;
103 info.uptime = (double) s_info.uptime;
110 if (!(fp = open_file("/proc/uptime", &rep))) {
114 fscanf(fp, "%lf", &info.uptime);
117 info.mask |= (1 << INFO_UPTIME);
120 int check_mount(char *s)
123 FILE *mtab = fopen("/etc/mtab", "r");
126 char buf1[256], buf2[128];
128 while (fgets(buf1, 256, mtab)) {
129 sscanf(buf1, "%*s %128s", buf2);
130 if (!strcmp(s, buf2)) {
137 ERR("Could not open mtab");
142 /* these things are also in sysinfo except Buffers:
143 * (that's why I'm reading them from proc) */
145 void update_meminfo(void)
150 /* unsigned int a; */
153 info.mem = info.memmax = info.swap = info.swapfree = info.swapmax = info.bufmem =
154 info.buffers = info.cached = info.memfree = info.memeasyfree = 0;
156 if (!(meminfo_fp = open_file("/proc/meminfo", &rep))) {
160 while (!feof(meminfo_fp)) {
161 if (fgets(buf, 255, meminfo_fp) == NULL) {
165 if (strncmp(buf, "MemTotal:", 9) == 0) {
166 sscanf(buf, "%*s %llu", &info.memmax);
167 } else if (strncmp(buf, "MemFree:", 8) == 0) {
168 sscanf(buf, "%*s %llu", &info.memfree);
169 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
170 sscanf(buf, "%*s %llu", &info.swapmax);
171 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
172 sscanf(buf, "%*s %llu", &info.swapfree);
173 } else if (strncmp(buf, "Buffers:", 8) == 0) {
174 sscanf(buf, "%*s %llu", &info.buffers);
175 } else if (strncmp(buf, "Cached:", 7) == 0) {
176 sscanf(buf, "%*s %llu", &info.cached);
180 info.mem = info.memmax - info.memfree;
181 info.memeasyfree = info.memfree;
182 info.swap = info.swapmax - info.swapfree;
184 info.bufmem = info.cached + info.buffers;
186 info.mask |= (1 << INFO_MEM) | (1 << INFO_BUFFERS);
191 int get_laptop_mode(void)
196 if ((fp = fopen("/proc/sys/vm/laptop_mode", "r")) != NULL)
197 fscanf(fp, "%d\n", &val);
203 * # cat /sys/block/sda/queue/scheduler
204 * noop [anticipatory] cfq
206 char *get_ioscheduler(char *disk)
212 return strndup("n/a", text_buffer_size);
214 snprintf(buf, 127, "/sys/block/%s/queue/scheduler", disk);
215 if ((fp = fopen(buf, "r")) == NULL) {
216 return strndup("n/a", text_buffer_size);
219 fscanf(fp, "%127s", buf);
221 buf[strlen(buf) - 1] = '\0';
223 return strndup(buf + 1, text_buffer_size);
227 return strndup("n/a", text_buffer_size);
230 #define COND_FREE(x) if(x) free(x); x = 0
231 #define SAVE_SET_STRING(x, y) \
232 if (x && strcmp((char *)x, (char *)y)) { \
234 x = strndup("multiple", text_buffer_size); \
236 x = strndup(y, text_buffer_size); \
239 void update_gateway_info_failure(const char *reason)
244 //2 pointers to 1 location causes a crash when we try to free them both
245 info.gw_info.iface = strndup("failed", text_buffer_size);
246 info.gw_info.ip = strndup("failed", text_buffer_size);
250 /* Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT */
251 #define RT_ENTRY_FORMAT "%63s %lx %lx %x %*d %*d %*d %lx %*d %*d %*d\n"
253 void update_gateway_info(void)
258 unsigned long dest, gate, mask;
261 struct gateway_info *gw_info = &info.gw_info;
263 COND_FREE(gw_info->iface);
264 COND_FREE(gw_info->ip);
267 if ((fp = fopen("/proc/net/route", "r")) == NULL) {
268 update_gateway_info_failure("fopen()");
272 /* skip over the table header line, which is always present */
273 fscanf(fp, "%*[^\n]\n");
276 if(fscanf(fp, RT_ENTRY_FORMAT,
277 iface, &dest, &gate, &flags, &mask) != 5) {
278 update_gateway_info_failure("fscanf()");
281 if (!(dest || mask) && ((flags & RTF_GATEWAY) || !gate) ) {
283 SAVE_SET_STRING(gw_info->iface, iface)
285 SAVE_SET_STRING(gw_info->ip, inet_ntoa(ina))
292 void update_net_stats(void)
296 static char first = 1;
298 // FIXME: arbitrary size chosen to keep code simple.
300 unsigned int curtmp1, curtmp2;
307 // wireless info variables
308 int skfd, has_bitrate = 0;
309 struct wireless_info *winfo;
314 delta = current_update_time - last_update_time;
315 if (delta <= 0.0001) {
319 /* open file and ignore first two lines */
320 if (!(net_dev_fp = open_file("/proc/net/dev", &rep))) {
325 fgets(buf, 255, net_dev_fp); /* garbage */
326 fgets(buf, 255, net_dev_fp); /* garbage (field names) */
328 /* read each interface */
329 for (i2 = 0; i2 < 16; i2++) {
333 long long r, t, last_recv, last_trans;
335 if (fgets(buf, 255, net_dev_fp) == NULL) {
339 while (isspace((int) *p)) {
345 while (*p && *p != ':') {
354 ns = get_net_stat(s, NULL, NULL);
356 memset(&(ns->addr.sa_data), 0, 14);
358 memset(ns->addrs, 0, 17 * 16 + 1); /* Up to 17 chars per ip, max 16 interfaces. Nasty memory usage... */
360 last_recv = ns->recv;
361 last_trans = ns->trans;
363 /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
364 sscanf(p, "%lld %*d %*d %*d %*d %*d %*d %*d %lld",
367 /* if recv or trans is less than last time, an overflow happened */
368 if (r < ns->last_read_recv) {
371 ns->recv += (r - ns->last_read_recv);
373 ns->last_read_recv = r;
375 if (t < ns->last_read_trans) {
378 ns->trans += (t - ns->last_read_trans);
380 ns->last_read_trans = t;
382 /*** ip addr patch ***/
383 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
385 conf.ifc_buf = malloc(sizeof(struct ifreq) * 16);
386 conf.ifc_len = sizeof(struct ifreq) * 16;
387 memset(conf.ifc_buf, 0, conf.ifc_len);
389 ioctl((long) i, SIOCGIFCONF, &conf);
391 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
392 struct net_stat *ns2;
394 if (!(((struct ifreq *) conf.ifc_buf) + k))
398 ((struct ifreq *) conf.ifc_buf)[k].ifr_ifrn.ifrn_name, NULL, NULL);
399 ns2->addr = ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.ifru_addr;
400 sprintf(temp_addr, "%u.%u.%u.%u, ",
401 ns2->addr.sa_data[2] & 255,
402 ns2->addr.sa_data[3] & 255,
403 ns2->addr.sa_data[4] & 255,
404 ns2->addr.sa_data[5] & 255);
405 if(NULL == strstr(ns2->addrs, temp_addr))
406 strncpy(ns2->addrs + strlen(ns2->addrs), temp_addr, 17);
413 /*** end ip addr patch ***/
416 /* calculate speeds */
417 ns->net_rec[0] = (ns->recv - last_recv) / delta;
418 ns->net_trans[0] = (ns->trans - last_trans) / delta;
425 #pragma omp parallel for reduction(+:curtmp1, curtmp2) schedule(dynamic,10)
426 #endif /* HAVE_OPENMP */
427 for (i = 0; i < info.net_avg_samples; i++) {
428 curtmp1 = curtmp1 + ns->net_rec[i];
429 curtmp2 = curtmp2 + ns->net_trans[i];
437 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
438 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
439 if (info.net_avg_samples > 1) {
441 #pragma omp parallel for schedule(dynamic,10)
442 #endif /* HAVE_OPENMP */
443 for (i = info.net_avg_samples; i > 1; i--) {
444 ns->net_rec[i - 1] = ns->net_rec[i - 2];
445 ns->net_trans[i - 1] = ns->net_trans[i - 2];
450 /* update wireless info */
451 winfo = malloc(sizeof(struct wireless_info));
452 memset(winfo, 0, sizeof(struct wireless_info));
454 skfd = iw_sockets_open();
455 if (iw_get_basic_config(skfd, s, &(winfo->b)) > -1) {
457 // set present winfo variables
458 if (iw_get_stats(skfd, s, &(winfo->stats),
459 &winfo->range, winfo->has_range) >= 0) {
460 winfo->has_stats = 1;
462 if (iw_get_range_info(skfd, s, &(winfo->range)) >= 0) {
463 winfo->has_range = 1;
465 if (iw_get_ext(skfd, s, SIOCGIWAP, &wrq) >= 0) {
466 winfo->has_ap_addr = 1;
467 memcpy(&(winfo->ap_addr), &(wrq.u.ap_addr), sizeof(sockaddr));
471 if (iw_get_ext(skfd, s, SIOCGIWRATE, &wrq) >= 0) {
472 memcpy(&(winfo->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
473 iw_print_bitrate(ns->bitrate, 16, winfo->bitrate.value);
478 if (winfo->has_range && winfo->has_stats
479 && ((winfo->stats.qual.level != 0)
480 || (winfo->stats.qual.updated & IW_QUAL_DBM))) {
481 if (!(winfo->stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
482 ns->link_qual = winfo->stats.qual.qual;
483 ns->link_qual_max = winfo->range.max_qual.qual;
488 if (winfo->has_ap_addr) {
489 iw_sawap_ntop(&winfo->ap_addr, ns->ap);
493 if (winfo->b.has_essid) {
494 if (winfo->b.essid_on) {
495 snprintf(ns->essid, 32, "%s", winfo->b.essid);
497 snprintf(ns->essid, 32, "off/any");
501 snprintf(ns->mode, 16, "%s", iw_operation_mode[winfo->b.mode]);
503 iw_sockets_close(skfd);
511 info.mask |= (1 << INFO_NET);
516 void update_total_processes(void)
520 struct sysinfo s_info;
523 info.procs = s_info.procs;
530 if (!(fp = open_file("/proc/loadavg", &rep))) {
534 fscanf(fp, "%*f %*f %*f %*d/%hu", &info.procs);
537 info.mask |= (1 << INFO_PROCS);
540 #define CPU_SAMPLE_COUNT 15
542 unsigned long long cpu_user;
543 unsigned long long cpu_system;
544 unsigned long long cpu_nice;
545 unsigned long long cpu_idle;
546 unsigned long long cpu_iowait;
547 unsigned long long cpu_irq;
548 unsigned long long cpu_softirq;
549 unsigned long long cpu_steal;
550 unsigned long long cpu_total;
551 unsigned long long cpu_active_total;
552 unsigned long long cpu_last_total;
553 unsigned long long cpu_last_active_total;
554 double cpu_val[CPU_SAMPLE_COUNT];
556 static short cpu_setup = 0;
558 /* Determine if this kernel gives us "extended" statistics information in
560 * Kernels around 2.5 and earlier only reported user, system, nice, and
561 * idle values in proc stat.
562 * Kernels around 2.6 and greater report these PLUS iowait, irq, softirq,
564 void determine_longstat(char *buf)
566 unsigned long long iowait = 0;
568 KFLAG_SETOFF(KFLAG_IS_LONGSTAT);
569 /* scanf will either return -1 or 1 because there is only 1 assignment */
570 if (sscanf(buf, "%*s %*d %*d %*d %*d %llu", &iowait) > 0) {
571 KFLAG_SETON(KFLAG_IS_LONGSTAT);
575 void get_cpu_count(void)
581 if (info.cpu_usage) {
585 if (!(stat_fp = open_file("/proc/stat", &rep))) {
591 while (!feof(stat_fp)) {
592 if (fgets(buf, 255, stat_fp) == NULL) {
596 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
597 if (info.cpu_count == 0) {
598 determine_longstat(buf);
603 info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
608 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
609 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
611 inline static void update_stat(void)
615 static struct cpu_info *cpu = NULL;
620 const char *stat_template = NULL;
621 unsigned int malloc_cpu_size = 0;
622 extern void* global_cpu;
624 /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
625 if (!cpu_setup || !info.cpu_usage) {
630 if (!stat_template) {
632 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
636 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
637 cpu = malloc(malloc_cpu_size);
638 memset(cpu, 0, malloc_cpu_size);
642 if (!(stat_fp = open_file("/proc/stat", &rep))) {
644 if (info.cpu_usage) {
645 memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
651 while (!feof(stat_fp)) {
652 if (fgets(buf, 255, stat_fp) == NULL) {
656 if (strncmp(buf, "procs_running ", 14) == 0) {
657 sscanf(buf, "%*s %hu", &info.run_procs);
658 info.mask |= (1 << INFO_RUN_PROCS);
659 } else if (strncmp(buf, "cpu", 3) == 0) {
661 if (isdigit(buf[3])) {
662 idx = atoi(&buf[3]) + 1;
666 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
667 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
668 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
669 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
670 &(cpu[idx].cpu_steal));
672 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
673 cpu[idx].cpu_system + cpu[idx].cpu_idle +
674 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
675 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
677 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
678 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
679 info.mask |= (1 << INFO_CPU);
681 delta = current_update_time - last_update_time;
683 if (delta <= 0.001) {
687 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
688 cpu[idx].cpu_last_active_total) /
689 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
692 #pragma omp parallel for reduction(+:curtmp) schedule(dynamic,10)
693 #endif /* HAVE_OPENMP */
694 for (i = 0; i < info.cpu_avg_samples; i++) {
695 curtmp = curtmp + cpu[idx].cpu_val[i];
697 /* TESTING -- I've removed this, because I don't think it is right.
698 * You shouldn't divide by the cpu count here ...
699 * removing for testing */
701 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
704 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
706 /* TESTING -- this line replaces the prev. "suspect" if/else */
707 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
709 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
710 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
712 #pragma omp parallel for schedule(dynamic,10)
713 #endif /* HAVE_OPENMP */
714 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
715 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
722 void update_running_processes(void)
727 void update_cpu_usage(void)
732 void update_load_average(void)
734 #ifdef HAVE_GETLOADAVG
739 info.loadavg[0] = (float) v[0];
740 info.loadavg[1] = (float) v[1];
741 info.loadavg[2] = (float) v[2];
748 if (!(fp = open_file("/proc/loadavg", &rep))) {
749 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
752 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
756 info.mask |= (1 << INFO_LOADAVG);
759 #define PROC_I8K "/proc/i8k"
760 #define I8K_DELIM " "
761 static char *i8k_procbuf = NULL;
762 void update_i8k(void)
767 i8k_procbuf = (char *) malloc(128 * sizeof(char));
769 if ((fp = fopen(PROC_I8K, "r")) == NULL) {
770 CRIT_ERR(NULL, NULL, "/proc/i8k doesn't exist! use insmod to make sure the kernel "
771 "driver is loaded...");
774 memset(&i8k_procbuf[0], 0, 128);
775 if (fread(&i8k_procbuf[0], sizeof(char), 128, fp) == 0) {
776 ERR("something wrong with /proc/i8k...");
781 i8k.version = strtok(&i8k_procbuf[0], I8K_DELIM);
782 i8k.bios = strtok(NULL, I8K_DELIM);
783 i8k.serial = strtok(NULL, I8K_DELIM);
784 i8k.cpu_temp = strtok(NULL, I8K_DELIM);
785 i8k.left_fan_status = strtok(NULL, I8K_DELIM);
786 i8k.right_fan_status = strtok(NULL, I8K_DELIM);
787 i8k.left_fan_rpm = strtok(NULL, I8K_DELIM);
788 i8k.right_fan_rpm = strtok(NULL, I8K_DELIM);
789 i8k.ac_status = strtok(NULL, I8K_DELIM);
790 i8k.buttons_status = strtok(NULL, I8K_DELIM);
793 /***********************************************************/
794 /***********************************************************/
795 /***********************************************************/
797 static int no_dots(const struct dirent *d)
799 if (d->d_name[0] == '.') {
805 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
807 struct dirent **namelist;
810 n = scandir(dir, &namelist, no_dots, alphasort);
813 ERR("scandir for %s: %s", dir, strerror(errno));
824 strncpy(s, namelist[0]->d_name, 255);
828 #pragma omp parallel for schedule(dynamic,10)
829 #endif /* HAVE_OPENMP */
830 for (i = 0; i < n; i++) {
839 int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
840 int *divisor, char *devtype)
848 memset(buf, 0, sizeof(buf));
850 /* if device is NULL or *, get first */
851 if (dev == NULL || strcmp(dev, "*") == 0) {
854 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
860 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
862 /* buf holds result from get_first_file_in_a_directory() above,
863 * e.g. "hwmon0" -- append "/device" */
864 strcat(buf, "/device");
866 /* dev holds device number N as a string,
867 * e.g. "0", -- convert to "hwmon0/device" */
868 sprintf(buf, "hwmon%s/device", dev);
873 /* At least the acpitz hwmon doesn't have a 'device' subdir,
874 * so check it's existence and strip it from buf otherwise. */
875 snprintf(path, 255, "%s%s", dir, dev);
876 if (stat(path, &st)) {
877 buf[strlen(buf) - 7] = 0;
880 /* change vol to in, tempf to temp */
881 if (strcmp(type, "vol") == 0) {
883 } else if (strcmp(type, "tempf") == 0) {
887 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
888 strncpy(devtype, path, 255);
891 fd = open(path, O_RDONLY);
893 CRIT_ERR(NULL, NULL, "can't open '%s': %s\nplease check your device or remove this "
894 "var from "PACKAGE_NAME, path, strerror(errno));
897 if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
898 || strcmp(type, "tempf") == 0) {
903 /* fan does not use *_div as a read divisor */
904 if (strcmp("fan", type) == 0) {
908 /* test if *_div file exist, open it and use it as divisor */
909 if (strcmp(type, "tempf") == 0) {
910 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
912 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
915 divfd = open(path, O_RDONLY);
921 divn = read(divfd, divbuf, 63);
922 /* should read until n == 0 but I doubt that kernel will give these
923 * in multiple pieces. :) */
925 ERR("open_sysfs_sensor(): can't read from sysfs");
928 *divisor = atoi(divbuf);
937 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 ERR("get_sysfs_info(): read from %s failed\n", devtype);
964 *fd = open(devtype, O_RDONLY);
966 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 /* Prior to kernel version 2.6.12, the CPU fan speed was available in
997 * ADT746X_FAN_OLD, whereas later kernel versions provide this information in
999 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
1000 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
1002 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
1005 char adt746x_fan_state[64];
1008 if (!p_client_buffer || client_buffer_size <= 0) {
1012 if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
1013 && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL) {
1014 sprintf(adt746x_fan_state, "adt746x not found");
1016 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
1017 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
1021 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_fan_state);
1024 /* Prior to kernel version 2.6.12, the CPU temperature was found in
1025 * ADT746X_CPU_OLD, whereas later kernel versions provide this information in
1027 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
1028 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
1030 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
1033 char adt746x_cpu_state[64];
1036 if (!p_client_buffer || client_buffer_size <= 0) {
1040 if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
1041 && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL) {
1042 sprintf(adt746x_cpu_state, "adt746x not found");
1044 fscanf(fp, "%2s", adt746x_cpu_state);
1048 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state);
1051 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1052 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1054 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1055 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1056 const char *p_format, int divisor, unsigned int cpu)
1064 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1070 char current_freq_file[128];
1072 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1074 f = fopen(current_freq_file, "r");
1076 /* if there's a cpufreq /sys node, read the current frequency from
1077 * this node and divide by 1000 to get Mhz. */
1078 if (fgets(s, sizeof(s), f)) {
1079 s[strlen(s) - 1] = '\0';
1080 freq = strtod(s, NULL);
1083 snprintf(p_client_buffer, client_buffer_size, p_format,
1084 (freq / 1000) / divisor);
1089 // open the CPU information file
1090 f = open_file("/proc/cpuinfo", &rep);
1092 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1097 while (fgets(s, sizeof(s), f) != NULL) {
1099 #if defined(__i386) || defined(__x86_64)
1100 // and search for the cpu mhz
1101 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1103 #if defined(__alpha)
1104 // different on alpha
1105 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1107 // this is different on ppc for some reason
1108 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1109 #endif // defined(__alpha)
1110 #endif // defined(__i386) || defined(__x86_64)
1112 // copy just the number
1113 strcpy(frequency, strchr(s, ':') + 2);
1114 #if defined(__alpha)
1116 frequency[strlen(frequency) - 6] = '\0';
1117 // kernel reports in Hz
1118 freq = strtod(frequency, NULL) / 1000000;
1121 frequency[strlen(frequency) - 1] = '\0';
1122 freq = strtod(frequency, NULL);
1126 if (strncmp(s, "processor", 9) == 0) {
1133 snprintf(p_client_buffer, client_buffer_size, p_format,
1134 (float) freq / divisor);
1138 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1140 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1150 * Peter Tarjan (ptarjan@citromail.hu) */
1152 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1153 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1154 const char *p_format, int divisor, unsigned int cpu)
1160 char current_freq_file[128];
1163 /* build the voltage file name */
1165 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1168 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1173 /* read the current cpu frequency from the /sys node */
1174 f = fopen(current_freq_file, "r");
1176 if (fgets(s, sizeof(s), f)) {
1177 s[strlen(s) - 1] = '\0';
1178 freq = strtod(s, NULL);
1182 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1183 perror("get_voltage()");
1190 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1193 /* use the current cpu frequency to find the corresponding voltage */
1194 f = fopen(current_freq_file, "r");
1200 if (fgets(line, 255, f) == NULL) {
1203 sscanf(line, "%d %d", &freq_comp, &voltage);
1204 if (freq_comp == freq) {
1210 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1211 perror("get_voltage()");
1217 snprintf(p_client_buffer, client_buffer_size, p_format,
1218 (float) voltage / divisor);
1222 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1224 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1231 if (!p_client_buffer || client_buffer_size <= 0) {
1235 /* yeah, slow... :/ */
1236 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1237 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1241 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1243 fp = open_file(buf2, &rep);
1245 snprintf(p_client_buffer, client_buffer_size,
1246 "can't open fan's state file");
1249 memset(buf, 0, sizeof(buf));
1250 fscanf(fp, "%*s %99s", buf);
1253 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1256 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1257 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1258 /* Linux 2.6.25 onwards ac adapter info is in
1259 /sys/class/power_supply/AC/
1260 On my system I get the following.
1261 /sys/class/power_supply/AC/uevent:
1262 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1265 POWER_SUPPLY_NAME=AC
1266 POWER_SUPPLY_TYPE=Mains
1267 POWER_SUPPLY_ONLINE=1
1270 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1278 if (!p_client_buffer || client_buffer_size <= 0) {
1282 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1283 fp = open_file(buf2, &rep);
1285 /* sysfs processing */
1287 if (fgets(buf, sizeof(buf), fp) == NULL)
1290 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1292 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1293 snprintf(p_client_buffer, client_buffer_size,
1294 "%s-line", (online ? "on" : "off"));
1300 /* yeah, slow... :/ */
1301 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1302 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1306 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1308 fp = open_file(buf2, &rep);
1310 snprintf(p_client_buffer, client_buffer_size,
1311 "No ac adapter found.... where is it?");
1314 memset(buf, 0, sizeof(buf));
1315 fscanf(fp, "%*s %99s", buf);
1318 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1323 /proc/acpi/thermal_zone/THRM/cooling_mode
1324 cooling mode: active
1325 /proc/acpi/thermal_zone/THRM/polling_frequency
1327 /proc/acpi/thermal_zone/THRM/state
1329 /proc/acpi/thermal_zone/THRM/temperature
1331 /proc/acpi/thermal_zone/THRM/trip_points
1333 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1336 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1337 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1339 int open_acpi_temperature(const char *name)
1345 if (name == NULL || strcmp(name, "*") == 0) {
1348 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1354 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1356 fd = open(path, O_RDONLY);
1358 ERR("can't open '%s': %s", path, strerror(errno));
1364 static double last_acpi_temp;
1365 static double last_acpi_temp_time;
1367 double get_acpi_temperature(int fd)
1373 /* don't update acpi temperature too often */
1374 if (current_update_time - last_acpi_temp_time < 11.32) {
1375 return last_acpi_temp;
1377 last_acpi_temp_time = current_update_time;
1379 /* seek to beginning */
1380 lseek(fd, 0, SEEK_SET);
1387 n = read(fd, buf, 255);
1389 ERR("can't read fd %d: %s", fd, strerror(errno));
1392 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1396 return last_acpi_temp;
1400 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1402 design capacity: 4400 mAh
1403 last full capacity: 4064 mAh
1404 battery technology: rechargeable
1405 design voltage: 14800 mV
1406 design capacity warning: 300 mAh
1407 design capacity low: 200 mAh
1408 capacity granularity 1: 32 mAh
1409 capacity granularity 2: 32 mAh
1411 serial number: 16922
1417 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1420 charging state: unknown
1422 remaining capacity: 4064 mAh
1423 present voltage: 16608 mV
1427 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1428 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1429 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1430 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1431 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1433 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1434 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1436 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1437 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1440 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1441 Linux 2.6.24 onwards battery info is in
1442 /sys/class/power_supply/BAT0/
1443 On my system I get the following.
1444 /sys/class/power_supply/BAT0/uevent:
1445 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1447 PHYSDEVDRIVER=battery
1448 POWER_SUPPLY_NAME=BAT0
1449 POWER_SUPPLY_TYPE=Battery
1450 POWER_SUPPLY_STATUS=Discharging
1451 POWER_SUPPLY_PRESENT=1
1452 POWER_SUPPLY_TECHNOLOGY=Li-ion
1453 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1454 POWER_SUPPLY_VOLTAGE_NOW=10780000
1455 POWER_SUPPLY_CURRENT_NOW=13970000
1456 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1457 POWER_SUPPLY_ENERGY_FULL=27370000
1458 POWER_SUPPLY_ENERGY_NOW=11810000
1459 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1460 POWER_SUPPLY_MANUFACTURER=Panasonic
1461 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1464 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1465 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1466 #define APM_PATH "/proc/apm"
1467 #define MAX_BATTERY_COUNT 4
1469 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1470 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1471 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1473 static int batteries_initialized = 0;
1474 static char batteries[MAX_BATTERY_COUNT][32];
1476 static int acpi_last_full[MAX_BATTERY_COUNT];
1477 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1479 /* e.g. "charging 75%" */
1480 static char last_battery_str[MAX_BATTERY_COUNT][64];
1482 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1484 static double last_battery_time[MAX_BATTERY_COUNT];
1486 static int last_battery_perct[MAX_BATTERY_COUNT];
1487 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1489 void init_batteries(void)
1493 if (batteries_initialized) {
1497 #pragma omp parallel for schedule(dynamic,10)
1498 #endif /* HAVE_OPENMP */
1499 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1500 batteries[idx][0] = '\0';
1502 batteries_initialized = 1;
1505 int get_battery_idx(const char *bat)
1509 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1510 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1515 /* if not found, enter a new entry */
1516 if (!strlen(batteries[idx])) {
1517 snprintf(batteries[idx], 31, "%s", bat);
1523 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1525 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1527 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1528 char acpi_path[128];
1529 char sysfs_path[128];
1531 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1532 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1536 idx = get_battery_idx(bat);
1538 /* don't update battery too often */
1539 if (current_update_time - last_battery_time[idx] < 29.5) {
1540 set_return_value(buffer, n, item, idx);
1544 last_battery_time[idx] = current_update_time;
1546 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1547 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1549 /* first try SYSFS if that fails try ACPI */
1551 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1552 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1555 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1556 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1559 if (sysfs_bat_fp[idx] != NULL) {
1561 int present_rate = -1;
1562 int remaining_capacity = -1;
1563 char charging_state[64];
1566 strcpy(charging_state, "unknown");
1568 while (!feof(sysfs_bat_fp[idx])) {
1570 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1573 /* let's just hope units are ok */
1574 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1575 strcpy(present, "yes");
1576 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1577 strcpy(present, "no");
1578 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1579 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1580 /* present_rate is not the same as the
1581 current flowing now but it is the same value
1582 which was used in the past. so we continue
1584 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1585 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1586 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1587 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1588 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1589 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1590 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1591 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1592 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1593 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1596 fclose(sysfs_bat_fp[idx]);
1597 sysfs_bat_fp[idx] = NULL;
1599 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1600 if (remaining_capacity > acpi_last_full[idx])
1601 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1604 if (strcmp(present, "No") == 0) {
1605 strncpy(last_battery_str[idx], "not present", 64);
1608 else if (strcmp(charging_state, "Charging") == 0) {
1609 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1610 /* e.g. charging 75% */
1611 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1612 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1614 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1615 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1616 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1617 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1618 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1619 snprintf(last_battery_time_str[idx],
1620 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1622 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1623 snprintf(last_battery_time_str[idx],
1624 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1628 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1629 if (present_rate > 0) {
1630 /* e.g. discharging 35% */
1631 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1632 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1634 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1635 (long) (((float) remaining_capacity / present_rate) * 3600));
1636 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1637 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1638 snprintf(last_battery_time_str[idx],
1639 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1641 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1643 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1644 snprintf(last_battery_time_str[idx],
1645 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1649 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1650 else if (strncmp(charging_state, "Charged", 64) == 0 || strncmp(charging_state, "Full", 64) == 0) {
1651 /* Below happens with the second battery on my X40,
1652 * when the second one is empty and the first one
1654 if (remaining_capacity == 0)
1655 strcpy(last_battery_str[idx], "empty");
1657 strcpy(last_battery_str[idx], "charged");
1659 /* unknown, probably full / AC */
1661 if (acpi_last_full[idx] != 0
1662 && remaining_capacity != acpi_last_full[idx])
1663 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1664 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1666 strncpy(last_battery_str[idx], "AC", 64);
1668 } else if (acpi_bat_fp[idx] != NULL) {
1670 int present_rate = -1;
1671 int remaining_capacity = -1;
1672 char charging_state[64];
1675 /* read last full capacity if it's zero */
1676 if (acpi_last_full[idx] == 0) {
1677 static int rep3 = 0;
1681 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1682 fp = open_file(path, &rep3);
1687 if (fgets(b, 256, fp) == NULL) {
1690 if (sscanf(b, "last full capacity: %d",
1691 &acpi_last_full[idx]) != 0) {
1700 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1702 strcpy(charging_state, "unknown");
1704 while (!feof(acpi_bat_fp[idx])) {
1707 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1711 /* let's just hope units are ok */
1712 if (strncmp(buf, "present:", 8) == 0) {
1713 sscanf(buf, "present: %4s", present);
1714 } else if (strncmp(buf, "charging state:", 15) == 0) {
1715 sscanf(buf, "charging state: %63s", charging_state);
1716 } else if (strncmp(buf, "present rate:", 13) == 0) {
1717 sscanf(buf, "present rate: %d", &present_rate);
1718 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1719 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1722 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1723 if (remaining_capacity > acpi_last_full[idx]) {
1724 /* normalize to 100% */
1725 acpi_last_full[idx] = remaining_capacity;
1729 if (strcmp(present, "no") == 0) {
1730 strncpy(last_battery_str[idx], "not present", 64);
1732 } else if (strcmp(charging_state, "charging") == 0) {
1733 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1734 /* e.g. charging 75% */
1735 snprintf(last_battery_str[idx],
1736 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1737 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1739 format_seconds(last_battery_time_str[idx],
1740 sizeof(last_battery_time_str[idx]) - 1,
1741 (long) (((acpi_last_full[idx] - remaining_capacity) *
1742 3600) / present_rate));
1743 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1744 snprintf(last_battery_str[idx],
1745 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1746 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1747 snprintf(last_battery_time_str[idx],
1748 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1750 strncpy(last_battery_str[idx], "charging",
1751 sizeof(last_battery_str[idx]) - 1);
1752 snprintf(last_battery_time_str[idx],
1753 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1756 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1757 if (present_rate > 0) {
1758 /* e.g. discharging 35% */
1759 snprintf(last_battery_str[idx],
1760 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1761 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1763 format_seconds(last_battery_time_str[idx],
1764 sizeof(last_battery_time_str[idx]) - 1,
1765 (long) ((remaining_capacity * 3600) / present_rate));
1766 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1767 snprintf(last_battery_str[idx],
1768 sizeof(last_battery_str[idx]) - 1, "full");
1769 snprintf(last_battery_time_str[idx],
1770 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1772 snprintf(last_battery_str[idx],
1773 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1774 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1775 snprintf(last_battery_time_str[idx],
1776 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1779 } else if (strncmp(charging_state, "charged", 64) == 0) {
1780 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1781 /* Below happens with the second battery on my X40,
1782 * when the second one is empty and the first one being charged. */
1783 if (remaining_capacity == 0) {
1784 strcpy(last_battery_str[idx], "empty");
1786 strcpy(last_battery_str[idx], "charged");
1788 /* unknown, probably full / AC */
1790 if (strncmp(charging_state, "Full", 64) == 0) {
1791 strncpy(last_battery_str[idx], "full", 64);
1792 } else if (acpi_last_full[idx] != 0
1793 && remaining_capacity != acpi_last_full[idx]) {
1794 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1795 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1797 strncpy(last_battery_str[idx], "AC", 64);
1800 fclose(acpi_bat_fp[idx]);
1801 acpi_bat_fp[idx] = NULL;
1804 if (apm_bat_fp[idx] == NULL) {
1805 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1808 if (apm_bat_fp[idx] != NULL) {
1809 unsigned int ac, status, flag;
1812 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1813 &ac, &status, &flag, &life);
1816 /* could check now that there is ac */
1817 snprintf(last_battery_str[idx], 64, "AC");
1819 /* could check that status == 3 here? */
1820 } else if (ac && life != 100) {
1821 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1823 snprintf(last_battery_str[idx], 64, "%d%%", life);
1826 /* it seemed to buffer it so file must be closed (or could use
1827 * syscalls directly but I don't feel like coding it now) */
1828 fclose(apm_bat_fp[idx]);
1829 apm_bat_fp[idx] = NULL;
1832 set_return_value(buffer, n, item, idx);
1835 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1838 case BATTERY_STATUS:
1839 snprintf(buffer, n, "%s", last_battery_str[idx]);
1842 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1849 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1851 get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1852 if (0 == strncmp("charging", buffer, 8)) {
1854 memmove(buffer + 1, buffer + 8, n - 8);
1855 } else if (0 == strncmp("discharging", buffer, 11)) {
1857 memmove(buffer + 1, buffer + 11, n - 11);
1858 } else if (0 == strncmp("charged", buffer, 7)) {
1860 memmove(buffer + 1, buffer + 7, n - 7);
1861 } else if (0 == strncmp("not present", buffer, 11)) {
1863 memmove(buffer + 1, buffer + 11, n - 11);
1864 } else if (0 == strncmp("empty", buffer, 5)) {
1866 memmove(buffer + 1, buffer + 5, n - 5);
1867 } else if (0 != strncmp("AC", buffer, 2)) {
1869 memmove(buffer + 1, buffer + 11, n - 11);
1873 int get_battery_perct(const char *bat)
1877 char acpi_path[128];
1878 char sysfs_path[128];
1879 int remaining_capacity = -1;
1881 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1882 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1886 idx = get_battery_idx(bat);
1888 /* don't update battery too often */
1889 if (current_update_time - last_battery_perct_time[idx] < 30) {
1890 return last_battery_perct[idx];
1892 last_battery_perct_time[idx] = current_update_time;
1894 /* Only check for SYSFS or ACPI */
1896 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1897 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1901 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1902 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1905 if (sysfs_bat_fp[idx] != NULL) {
1907 while (!feof(sysfs_bat_fp[idx])) {
1909 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1912 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1913 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1914 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1915 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1916 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1917 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1918 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1919 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1923 fclose(sysfs_bat_fp[idx]);
1924 sysfs_bat_fp[idx] = NULL;
1926 } else if (acpi_bat_fp[idx] != NULL) {
1928 /* read last full capacity if it's zero */
1929 if (acpi_design_capacity[idx] == 0) {
1934 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1935 fp = open_file(path, &rep2);
1940 if (fgets(b, 256, fp) == NULL) {
1943 if (sscanf(b, "last full capacity: %d",
1944 &acpi_design_capacity[idx]) != 0) {
1952 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1954 while (!feof(acpi_bat_fp[idx])) {
1957 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1961 if (buf[0] == 'r') {
1962 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1966 if (remaining_capacity < 0) {
1969 /* compute the battery percentage */
1970 last_battery_perct[idx] =
1971 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
1972 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
1973 return last_battery_perct[idx];
1976 int get_battery_perct_bar(const char *bar)
1980 get_battery_perct(bar);
1981 idx = get_battery_idx(bar);
1982 return (int) (last_battery_perct[idx] * 2.56 - 1);
1985 /* On Apple powerbook and ibook:
1986 $ cat /proc/pmu/battery_0
1993 $ cat /proc/pmu/info
1994 PMU driver version : 2
1995 PMU firmware version : 0c
2000 /* defines as in <linux/pmu.h> */
2001 #define PMU_BATT_PRESENT 0x00000001
2002 #define PMU_BATT_CHARGING 0x00000002
2004 static FILE *pmu_battery_fp;
2005 static FILE *pmu_info_fp;
2006 static char pb_battery_info[3][32];
2007 static double pb_battery_info_update;
2009 #define PMU_PATH "/proc/pmu"
2010 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2013 const char *batt_path = PMU_PATH "/battery_0";
2014 const char *info_path = PMU_PATH "/info";
2016 int charge, max_charge, ac = -1;
2019 /* don't update battery too often */
2020 if (current_update_time - pb_battery_info_update < 29.5) {
2021 snprintf(buffer, n, "%s", pb_battery_info[i]);
2024 pb_battery_info_update = current_update_time;
2026 if (pmu_battery_fp == NULL) {
2027 pmu_battery_fp = open_file(batt_path, &rep);
2028 if (pmu_battery_fp == NULL) {
2033 if (pmu_battery_fp != NULL) {
2034 rewind(pmu_battery_fp);
2035 while (!feof(pmu_battery_fp)) {
2038 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2042 if (buf[0] == 'f') {
2043 sscanf(buf, "flags : %8x", &flags);
2044 } else if (buf[0] == 'c' && buf[1] == 'h') {
2045 sscanf(buf, "charge : %d", &charge);
2046 } else if (buf[0] == 'm') {
2047 sscanf(buf, "max_charge : %d", &max_charge);
2048 } else if (buf[0] == 't') {
2049 sscanf(buf, "time rem. : %ld", &timeval);
2053 if (pmu_info_fp == NULL) {
2054 pmu_info_fp = open_file(info_path, &rep);
2055 if (pmu_info_fp == NULL) {
2060 if (pmu_info_fp != NULL) {
2061 rewind(pmu_info_fp);
2062 while (!feof(pmu_info_fp)) {
2065 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2068 if (buf[0] == 'A') {
2069 sscanf(buf, "AC Power : %d", &ac);
2073 /* update status string */
2074 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2075 strncpy(pb_battery_info[PB_BATT_STATUS], "AC", sizeof(pb_battery_info[PB_BATT_STATUS]));
2076 } else if (ac && (flags & PMU_BATT_PRESENT)
2077 && !(flags & PMU_BATT_CHARGING)) {
2078 strncpy(pb_battery_info[PB_BATT_STATUS], "charged", sizeof(pb_battery_info[PB_BATT_STATUS]));
2079 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2080 strncpy(pb_battery_info[PB_BATT_STATUS], "charging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2082 strncpy(pb_battery_info[PB_BATT_STATUS], "discharging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2085 /* update percentage string */
2086 if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT)
2087 && !(flags & PMU_BATT_CHARGING)) {
2088 snprintf(pb_battery_info[PB_BATT_PERCENT],
2089 sizeof(pb_battery_info[PB_BATT_PERCENT]), "100%%");
2090 } else if (timeval == 0) {
2091 snprintf(pb_battery_info[PB_BATT_PERCENT],
2092 sizeof(pb_battery_info[PB_BATT_PERCENT]), "unknown");
2094 snprintf(pb_battery_info[PB_BATT_PERCENT],
2095 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2096 (charge * 100) / max_charge);
2099 /* update time string */
2100 if (timeval == 0) { /* fully charged or battery not present */
2101 snprintf(pb_battery_info[PB_BATT_TIME],
2102 sizeof(pb_battery_info[PB_BATT_TIME]), "unknown");
2103 } else if (timeval < 60 * 60) { /* don't show secs */
2104 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2105 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2107 format_seconds(pb_battery_info[PB_BATT_TIME],
2108 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2111 snprintf(buffer, n, "%s", pb_battery_info[i]);
2114 void update_top(void)
2116 process_find_top(info.cpu, info.memu, info.time
2121 info.first_process = get_first_process();
2124 void update_entropy(void)
2127 const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2128 const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2131 info.entropy.entropy_avail = 0;
2132 info.entropy.poolsize = 0;
2134 if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2138 if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2143 fscanf(fp1, "%u", &info.entropy.entropy_avail);
2144 fscanf(fp2, "%u", &info.entropy.poolsize);
2149 info.mask |= (1 << INFO_ENTROPY);
2152 const char *get_disk_protect_queue(const char *disk)
2158 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2159 if (access(path, F_OK)) {
2160 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2162 if ((fp = fopen(path, "r")) == NULL)
2164 if (fscanf(fp, "%d\n", &state) != 1) {
2169 return (state > 0) ? "frozen" : "free ";
2172 void update_diskio(void)
2176 char buf[512], devbuf[64];
2177 unsigned int major, minor;
2179 struct diskio_stat *cur;
2180 unsigned int reads, writes;
2181 unsigned int total_reads = 0, total_writes = 0;
2184 stats.current_read = 0;
2185 stats.current_write = 0;
2187 if (!(fp = open_file("/proc/diskstats", &rep))) {
2191 /* read reads and writes from all disks (minor = 0), including cd-roms
2192 * and floppies, and sum them up */
2193 while (fgets(buf, 512, fp)) {
2194 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
2195 &minor, devbuf, &reads, &writes);
2196 /* ignore subdevices (they have only 3 matching entries in their line)
2197 * and virtual devices (LVM, network block devices, RAM disks, Loopback)
2199 * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
2200 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
2201 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
2202 total_reads += reads;
2203 total_writes += writes;
2205 col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
2206 &major, &minor, devbuf, &reads, &writes);
2207 if (col_count != 5) {
2212 while (cur && strcmp(devbuf, cur->dev))
2216 update_diskio_values(cur, reads, writes);
2218 update_diskio_values(&stats, total_reads, total_writes);