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 inline static void update_stat(void)
617 static struct cpu_info *cpu = NULL;
622 const char *stat_template = NULL;
623 unsigned int malloc_cpu_size = 0;
624 extern void* global_cpu;
626 /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
627 if (!cpu_setup || !info.cpu_usage) {
632 if (!stat_template) {
634 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
638 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
639 cpu = malloc(malloc_cpu_size);
640 memset(cpu, 0, malloc_cpu_size);
644 if (!(stat_fp = open_file("/proc/stat", &rep))) {
646 if (info.cpu_usage) {
647 memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
653 while (!feof(stat_fp)) {
654 if (fgets(buf, 255, stat_fp) == NULL) {
658 if (strncmp(buf, "procs_running ", 14) == 0) {
659 sscanf(buf, "%*s %hu", &info.run_procs);
660 info.mask |= (1 << INFO_RUN_PROCS);
661 } else if (strncmp(buf, "cpu", 3) == 0) {
663 if (isdigit(buf[3])) {
664 idx = atoi(&buf[3]) + 1;
668 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
669 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
670 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
671 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
672 &(cpu[idx].cpu_steal));
674 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
675 cpu[idx].cpu_system + cpu[idx].cpu_idle +
676 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
677 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
679 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
680 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
681 info.mask |= (1 << INFO_CPU);
683 delta = current_update_time - last_update_time;
685 if (delta <= 0.001) {
689 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
690 cpu[idx].cpu_last_active_total) /
691 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
694 #pragma omp parallel for reduction(+:curtmp) schedule(dynamic,10)
695 #endif /* HAVE_OPENMP */
696 for (i = 0; i < info.cpu_avg_samples; i++) {
697 curtmp = curtmp + cpu[idx].cpu_val[i];
699 /* TESTING -- I've removed this, because I don't think it is right.
700 * You shouldn't divide by the cpu count here ...
701 * removing for testing */
703 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
706 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
708 /* TESTING -- this line replaces the prev. "suspect" if/else */
709 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
711 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
712 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
714 #pragma omp parallel for schedule(dynamic,10)
715 #endif /* HAVE_OPENMP */
716 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
717 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
724 void update_running_processes(void)
729 void update_cpu_usage(void)
734 void update_load_average(void)
736 #ifdef HAVE_GETLOADAVG
741 info.loadavg[0] = (float) v[0];
742 info.loadavg[1] = (float) v[1];
743 info.loadavg[2] = (float) v[2];
750 if (!(fp = open_file("/proc/loadavg", &rep))) {
751 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
754 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
758 info.mask |= (1 << INFO_LOADAVG);
761 #define PROC_I8K "/proc/i8k"
762 #define I8K_DELIM " "
763 static char *i8k_procbuf = NULL;
764 void update_i8k(void)
769 i8k_procbuf = (char *) malloc(128 * sizeof(char));
771 if ((fp = fopen(PROC_I8K, "r")) == NULL) {
772 CRIT_ERR(NULL, NULL, "/proc/i8k doesn't exist! use insmod to make sure the kernel "
773 "driver is loaded...");
776 memset(&i8k_procbuf[0], 0, 128);
777 if (fread(&i8k_procbuf[0], sizeof(char), 128, fp) == 0) {
778 NORM_ERR("something wrong with /proc/i8k...");
783 i8k.version = strtok(&i8k_procbuf[0], I8K_DELIM);
784 i8k.bios = strtok(NULL, I8K_DELIM);
785 i8k.serial = strtok(NULL, I8K_DELIM);
786 i8k.cpu_temp = strtok(NULL, I8K_DELIM);
787 i8k.left_fan_status = strtok(NULL, I8K_DELIM);
788 i8k.right_fan_status = strtok(NULL, I8K_DELIM);
789 i8k.left_fan_rpm = strtok(NULL, I8K_DELIM);
790 i8k.right_fan_rpm = strtok(NULL, I8K_DELIM);
791 i8k.ac_status = strtok(NULL, I8K_DELIM);
792 i8k.buttons_status = strtok(NULL, I8K_DELIM);
795 /***********************************************************/
796 /***********************************************************/
797 /***********************************************************/
799 static int no_dots(const struct dirent *d)
801 if (d->d_name[0] == '.') {
807 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
809 struct dirent **namelist;
812 n = scandir(dir, &namelist, no_dots, alphasort);
815 NORM_ERR("scandir for %s: %s", dir, strerror(errno));
826 strncpy(s, namelist[0]->d_name, 255);
830 #pragma omp parallel for schedule(dynamic,10)
831 #endif /* HAVE_OPENMP */
832 for (i = 0; i < n; i++) {
841 int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
842 int *divisor, char *devtype)
850 memset(buf, 0, sizeof(buf));
852 /* if device is NULL or *, get first */
853 if (dev == NULL || strcmp(dev, "*") == 0) {
856 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
862 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
864 /* buf holds result from get_first_file_in_a_directory() above,
865 * e.g. "hwmon0" -- append "/device" */
866 strcat(buf, "/device");
868 /* dev holds device number N as a string,
869 * e.g. "0", -- convert to "hwmon0/device" */
870 sprintf(buf, "hwmon%s/device", dev);
875 /* At least the acpitz hwmon doesn't have a 'device' subdir,
876 * so check it's existence and strip it from buf otherwise. */
877 snprintf(path, 255, "%s%s", dir, dev);
878 if (stat(path, &st)) {
879 buf[strlen(buf) - 7] = 0;
882 /* change vol to in, tempf to temp */
883 if (strcmp(type, "vol") == 0) {
885 } else if (strcmp(type, "tempf") == 0) {
889 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
890 strncpy(devtype, path, 255);
893 fd = open(path, O_RDONLY);
895 CRIT_ERR(NULL, NULL, "can't open '%s': %s\nplease check your device or remove this "
896 "var from "PACKAGE_NAME, path, strerror(errno));
899 if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
900 || strcmp(type, "tempf") == 0) {
905 /* fan does not use *_div as a read divisor */
906 if (strcmp("fan", type) == 0) {
910 /* test if *_div file exist, open it and use it as divisor */
911 if (strcmp(type, "tempf") == 0) {
912 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
914 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
917 divfd = open(path, O_RDONLY);
923 divn = read(divfd, divbuf, 63);
924 /* should read until n == 0 but I doubt that kernel will give these
925 * in multiple pieces. :) */
927 NORM_ERR("open_sysfs_sensor(): can't read from sysfs");
930 *divisor = atoi(divbuf);
939 double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
947 lseek(*fd, 0, SEEK_SET);
953 n = read(*fd, buf, 63);
954 /* should read until n == 0 but I doubt that kernel will give these
955 * in multiple pieces. :) */
957 NORM_ERR("get_sysfs_info(): read from %s failed\n", devtype);
966 *fd = open(devtype, O_RDONLY);
968 NORM_ERR("can't open '%s': %s", devtype, strerror(errno));
971 /* My dirty hack for computing CPU value
972 * Filedil, from forums.gentoo.org */
973 /* if (strstr(devtype, "temp1_input") != NULL) {
974 return -15.096 + 1.4893 * (val / 1000.0);
977 /* divide voltage and temperature by 1000 */
978 /* or if any other divisor is given, use that */
979 if (strcmp(type, "tempf") == 0) {
981 return ((val / divisor + 40) * 9.0 / 5) - 40;
982 } else if (divisor) {
983 return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
985 return ((val + 40) * 9.0 / 5) - 40;
989 return val / divisor;
990 } else if (divisor) {
998 /* Prior to kernel version 2.6.12, the CPU fan speed was available in
999 * ADT746X_FAN_OLD, whereas later kernel versions provide this information in
1001 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
1002 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
1004 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
1007 char adt746x_fan_state[64];
1010 if (!p_client_buffer || client_buffer_size <= 0) {
1014 if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
1015 && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL) {
1016 sprintf(adt746x_fan_state, "adt746x not found");
1018 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
1019 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
1023 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_fan_state);
1026 /* Prior to kernel version 2.6.12, the CPU temperature was found in
1027 * ADT746X_CPU_OLD, whereas later kernel versions provide this information in
1029 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
1030 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
1032 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
1035 char adt746x_cpu_state[64];
1038 if (!p_client_buffer || client_buffer_size <= 0) {
1042 if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
1043 && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL) {
1044 sprintf(adt746x_cpu_state, "adt746x not found");
1046 fscanf(fp, "%2s", adt746x_cpu_state);
1050 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state);
1053 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1054 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1056 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1057 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1058 const char *p_format, int divisor, unsigned int cpu)
1066 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1072 char current_freq_file[128];
1074 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1076 f = fopen(current_freq_file, "r");
1078 /* if there's a cpufreq /sys node, read the current frequency from
1079 * this node and divide by 1000 to get Mhz. */
1080 if (fgets(s, sizeof(s), f)) {
1081 s[strlen(s) - 1] = '\0';
1082 freq = strtod(s, NULL);
1085 snprintf(p_client_buffer, client_buffer_size, p_format,
1086 (freq / 1000) / divisor);
1091 // open the CPU information file
1092 f = open_file("/proc/cpuinfo", &rep);
1094 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1099 while (fgets(s, sizeof(s), f) != NULL) {
1101 #if defined(__i386) || defined(__x86_64)
1102 // and search for the cpu mhz
1103 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1105 #if defined(__alpha)
1106 // different on alpha
1107 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1109 // this is different on ppc for some reason
1110 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1111 #endif // defined(__alpha)
1112 #endif // defined(__i386) || defined(__x86_64)
1114 // copy just the number
1115 strcpy(frequency, strchr(s, ':') + 2);
1116 #if defined(__alpha)
1118 frequency[strlen(frequency) - 6] = '\0';
1119 // kernel reports in Hz
1120 freq = strtod(frequency, NULL) / 1000000;
1123 frequency[strlen(frequency) - 1] = '\0';
1124 freq = strtod(frequency, NULL);
1128 if (strncmp(s, "processor", 9) == 0) {
1135 snprintf(p_client_buffer, client_buffer_size, p_format,
1136 (float) freq / divisor);
1140 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1142 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1152 * Peter Tarjan (ptarjan@citromail.hu) */
1154 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1155 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1156 const char *p_format, int divisor, unsigned int cpu)
1162 char current_freq_file[128];
1165 /* build the voltage file name */
1167 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1170 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1175 /* read the current cpu frequency from the /sys node */
1176 f = fopen(current_freq_file, "r");
1178 if (fgets(s, sizeof(s), f)) {
1179 s[strlen(s) - 1] = '\0';
1180 freq = strtod(s, NULL);
1184 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1185 perror("get_voltage()");
1192 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1195 /* use the current cpu frequency to find the corresponding voltage */
1196 f = fopen(current_freq_file, "r");
1202 if (fgets(line, 255, f) == NULL) {
1205 sscanf(line, "%d %d", &freq_comp, &voltage);
1206 if (freq_comp == freq) {
1212 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1213 perror("get_voltage()");
1219 snprintf(p_client_buffer, client_buffer_size, p_format,
1220 (float) voltage / divisor);
1224 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1226 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1233 if (!p_client_buffer || client_buffer_size <= 0) {
1237 /* yeah, slow... :/ */
1238 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1239 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1243 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1245 fp = open_file(buf2, &rep);
1247 snprintf(p_client_buffer, client_buffer_size,
1248 "can't open fan's state file");
1251 memset(buf, 0, sizeof(buf));
1252 fscanf(fp, "%*s %99s", buf);
1255 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1258 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1259 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1260 /* Linux 2.6.25 onwards ac adapter info is in
1261 /sys/class/power_supply/AC/
1262 On my system I get the following.
1263 /sys/class/power_supply/AC/uevent:
1264 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1267 POWER_SUPPLY_NAME=AC
1268 POWER_SUPPLY_TYPE=Mains
1269 POWER_SUPPLY_ONLINE=1
1272 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1280 if (!p_client_buffer || client_buffer_size <= 0) {
1284 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1285 fp = open_file(buf2, &rep);
1287 /* sysfs processing */
1289 if (fgets(buf, sizeof(buf), fp) == NULL)
1292 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1294 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1295 snprintf(p_client_buffer, client_buffer_size,
1296 "%s-line", (online ? "on" : "off"));
1302 /* yeah, slow... :/ */
1303 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1304 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1308 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1310 fp = open_file(buf2, &rep);
1312 snprintf(p_client_buffer, client_buffer_size,
1313 "No ac adapter found.... where is it?");
1316 memset(buf, 0, sizeof(buf));
1317 fscanf(fp, "%*s %99s", buf);
1320 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1325 /proc/acpi/thermal_zone/THRM/cooling_mode
1326 cooling mode: active
1327 /proc/acpi/thermal_zone/THRM/polling_frequency
1329 /proc/acpi/thermal_zone/THRM/state
1331 /proc/acpi/thermal_zone/THRM/temperature
1333 /proc/acpi/thermal_zone/THRM/trip_points
1335 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1338 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1339 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1341 int open_acpi_temperature(const char *name)
1347 if (name == NULL || strcmp(name, "*") == 0) {
1350 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1356 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1358 fd = open(path, O_RDONLY);
1360 NORM_ERR("can't open '%s': %s", path, strerror(errno));
1366 static double last_acpi_temp;
1367 static double last_acpi_temp_time;
1369 double get_acpi_temperature(int fd)
1375 /* don't update acpi temperature too often */
1376 if (current_update_time - last_acpi_temp_time < 11.32) {
1377 return last_acpi_temp;
1379 last_acpi_temp_time = current_update_time;
1381 /* seek to beginning */
1382 lseek(fd, 0, SEEK_SET);
1389 n = read(fd, buf, 255);
1391 NORM_ERR("can't read fd %d: %s", fd, strerror(errno));
1394 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1398 return last_acpi_temp;
1402 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1404 design capacity: 4400 mAh
1405 last full capacity: 4064 mAh
1406 battery technology: rechargeable
1407 design voltage: 14800 mV
1408 design capacity warning: 300 mAh
1409 design capacity low: 200 mAh
1410 capacity granularity 1: 32 mAh
1411 capacity granularity 2: 32 mAh
1413 serial number: 16922
1419 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1422 charging state: unknown
1424 remaining capacity: 4064 mAh
1425 present voltage: 16608 mV
1429 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1430 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1431 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1432 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1433 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1435 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1436 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1438 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1439 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1442 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1443 Linux 2.6.24 onwards battery info is in
1444 /sys/class/power_supply/BAT0/
1445 On my system I get the following.
1446 /sys/class/power_supply/BAT0/uevent:
1447 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1449 PHYSDEVDRIVER=battery
1450 POWER_SUPPLY_NAME=BAT0
1451 POWER_SUPPLY_TYPE=Battery
1452 POWER_SUPPLY_STATUS=Discharging
1453 POWER_SUPPLY_PRESENT=1
1454 POWER_SUPPLY_TECHNOLOGY=Li-ion
1455 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1456 POWER_SUPPLY_VOLTAGE_NOW=10780000
1457 POWER_SUPPLY_CURRENT_NOW=13970000
1458 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1459 POWER_SUPPLY_ENERGY_FULL=27370000
1460 POWER_SUPPLY_ENERGY_NOW=11810000
1461 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1462 POWER_SUPPLY_MANUFACTURER=Panasonic
1463 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1466 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1467 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1468 #define APM_PATH "/proc/apm"
1469 #define MAX_BATTERY_COUNT 4
1471 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1472 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1473 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1475 static int batteries_initialized = 0;
1476 static char batteries[MAX_BATTERY_COUNT][32];
1478 static int acpi_last_full[MAX_BATTERY_COUNT];
1479 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1481 /* e.g. "charging 75%" */
1482 static char last_battery_str[MAX_BATTERY_COUNT][64];
1484 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1486 static double last_battery_time[MAX_BATTERY_COUNT];
1488 static int last_battery_perct[MAX_BATTERY_COUNT];
1489 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1491 void init_batteries(void)
1495 if (batteries_initialized) {
1499 #pragma omp parallel for schedule(dynamic,10)
1500 #endif /* HAVE_OPENMP */
1501 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1502 batteries[idx][0] = '\0';
1504 batteries_initialized = 1;
1507 int get_battery_idx(const char *bat)
1511 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1512 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1517 /* if not found, enter a new entry */
1518 if (!strlen(batteries[idx])) {
1519 snprintf(batteries[idx], 31, "%s", bat);
1525 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1527 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1529 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1530 char acpi_path[128];
1531 char sysfs_path[128];
1533 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1534 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1538 idx = get_battery_idx(bat);
1540 /* don't update battery too often */
1541 if (current_update_time - last_battery_time[idx] < 29.5) {
1542 set_return_value(buffer, n, item, idx);
1546 last_battery_time[idx] = current_update_time;
1548 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1549 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1551 /* first try SYSFS if that fails try ACPI */
1553 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1554 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1557 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1558 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1561 if (sysfs_bat_fp[idx] != NULL) {
1563 int present_rate = -1;
1564 int remaining_capacity = -1;
1565 char charging_state[64];
1568 strcpy(charging_state, "unknown");
1570 while (!feof(sysfs_bat_fp[idx])) {
1572 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1575 /* let's just hope units are ok */
1576 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1577 strcpy(present, "yes");
1578 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1579 strcpy(present, "no");
1580 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1581 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1582 /* present_rate is not the same as the
1583 current flowing now but it is the same value
1584 which was used in the past. so we continue
1586 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1587 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1588 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1589 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1590 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1591 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1592 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1593 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1594 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1595 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1598 fclose(sysfs_bat_fp[idx]);
1599 sysfs_bat_fp[idx] = NULL;
1601 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1602 if (remaining_capacity > acpi_last_full[idx])
1603 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1606 if (strcmp(present, "No") == 0) {
1607 strncpy(last_battery_str[idx], "not present", 64);
1610 else if (strcmp(charging_state, "Charging") == 0) {
1611 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1612 /* e.g. charging 75% */
1613 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1614 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1616 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1617 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1618 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1619 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1620 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1621 snprintf(last_battery_time_str[idx],
1622 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1624 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1625 snprintf(last_battery_time_str[idx],
1626 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1630 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1631 if (present_rate > 0) {
1632 /* e.g. discharging 35% */
1633 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1634 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1636 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1637 (long) (((float) remaining_capacity / present_rate) * 3600));
1638 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1639 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1640 snprintf(last_battery_time_str[idx],
1641 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1643 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1645 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1646 snprintf(last_battery_time_str[idx],
1647 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1651 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1652 else if (strncmp(charging_state, "Charged", 64) == 0 || strncmp(charging_state, "Full", 64) == 0) {
1653 /* Below happens with the second battery on my X40,
1654 * when the second one is empty and the first one
1656 if (remaining_capacity == 0)
1657 strcpy(last_battery_str[idx], "empty");
1659 strcpy(last_battery_str[idx], "charged");
1661 /* unknown, probably full / AC */
1663 if (acpi_last_full[idx] != 0
1664 && remaining_capacity != acpi_last_full[idx])
1665 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1666 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1668 strncpy(last_battery_str[idx], "AC", 64);
1670 } else if (acpi_bat_fp[idx] != NULL) {
1672 int present_rate = -1;
1673 int remaining_capacity = -1;
1674 char charging_state[64];
1677 /* read last full capacity if it's zero */
1678 if (acpi_last_full[idx] == 0) {
1679 static int rep3 = 0;
1683 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1684 fp = open_file(path, &rep3);
1689 if (fgets(b, 256, fp) == NULL) {
1692 if (sscanf(b, "last full capacity: %d",
1693 &acpi_last_full[idx]) != 0) {
1702 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1704 strcpy(charging_state, "unknown");
1706 while (!feof(acpi_bat_fp[idx])) {
1709 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1713 /* let's just hope units are ok */
1714 if (strncmp(buf, "present:", 8) == 0) {
1715 sscanf(buf, "present: %4s", present);
1716 } else if (strncmp(buf, "charging state:", 15) == 0) {
1717 sscanf(buf, "charging state: %63s", charging_state);
1718 } else if (strncmp(buf, "present rate:", 13) == 0) {
1719 sscanf(buf, "present rate: %d", &present_rate);
1720 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1721 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1724 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1725 if (remaining_capacity > acpi_last_full[idx]) {
1726 /* normalize to 100% */
1727 acpi_last_full[idx] = remaining_capacity;
1731 if (strcmp(present, "no") == 0) {
1732 strncpy(last_battery_str[idx], "not present", 64);
1734 } else if (strcmp(charging_state, "charging") == 0) {
1735 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1736 /* e.g. charging 75% */
1737 snprintf(last_battery_str[idx],
1738 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1739 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1741 format_seconds(last_battery_time_str[idx],
1742 sizeof(last_battery_time_str[idx]) - 1,
1743 (long) (((acpi_last_full[idx] - remaining_capacity) *
1744 3600) / present_rate));
1745 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1746 snprintf(last_battery_str[idx],
1747 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1748 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1749 snprintf(last_battery_time_str[idx],
1750 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1752 strncpy(last_battery_str[idx], "charging",
1753 sizeof(last_battery_str[idx]) - 1);
1754 snprintf(last_battery_time_str[idx],
1755 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1758 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1759 if (present_rate > 0) {
1760 /* e.g. discharging 35% */
1761 snprintf(last_battery_str[idx],
1762 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1763 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1765 format_seconds(last_battery_time_str[idx],
1766 sizeof(last_battery_time_str[idx]) - 1,
1767 (long) ((remaining_capacity * 3600) / present_rate));
1768 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1769 snprintf(last_battery_str[idx],
1770 sizeof(last_battery_str[idx]) - 1, "full");
1771 snprintf(last_battery_time_str[idx],
1772 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1774 snprintf(last_battery_str[idx],
1775 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1776 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1777 snprintf(last_battery_time_str[idx],
1778 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1781 } else if (strncmp(charging_state, "charged", 64) == 0) {
1782 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1783 /* Below happens with the second battery on my X40,
1784 * when the second one is empty and the first one being charged. */
1785 if (remaining_capacity == 0) {
1786 strcpy(last_battery_str[idx], "empty");
1788 strcpy(last_battery_str[idx], "charged");
1790 /* unknown, probably full / AC */
1792 if (strncmp(charging_state, "Full", 64) == 0) {
1793 strncpy(last_battery_str[idx], "full", 64);
1794 } else if (acpi_last_full[idx] != 0
1795 && remaining_capacity != acpi_last_full[idx]) {
1796 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1797 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1799 strncpy(last_battery_str[idx], "AC", 64);
1802 fclose(acpi_bat_fp[idx]);
1803 acpi_bat_fp[idx] = NULL;
1806 if (apm_bat_fp[idx] == NULL) {
1807 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1810 if (apm_bat_fp[idx] != NULL) {
1811 unsigned int ac, status, flag;
1814 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1815 &ac, &status, &flag, &life);
1818 /* could check now that there is ac */
1819 snprintf(last_battery_str[idx], 64, "AC");
1821 /* could check that status == 3 here? */
1822 } else if (ac && life != 100) {
1823 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1825 snprintf(last_battery_str[idx], 64, "%d%%", life);
1828 /* it seemed to buffer it so file must be closed (or could use
1829 * syscalls directly but I don't feel like coding it now) */
1830 fclose(apm_bat_fp[idx]);
1831 apm_bat_fp[idx] = NULL;
1834 set_return_value(buffer, n, item, idx);
1837 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1840 case BATTERY_STATUS:
1841 snprintf(buffer, n, "%s", last_battery_str[idx]);
1844 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1851 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1853 get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1854 if (0 == strncmp("charging", buffer, 8)) {
1856 memmove(buffer + 1, buffer + 8, n - 8);
1857 } else if (0 == strncmp("discharging", buffer, 11)) {
1859 memmove(buffer + 1, buffer + 11, n - 11);
1860 } else if (0 == strncmp("charged", buffer, 7)) {
1862 memmove(buffer + 1, buffer + 7, n - 7);
1863 } else if (0 == strncmp("not present", buffer, 11)) {
1865 memmove(buffer + 1, buffer + 11, n - 11);
1866 } else if (0 == strncmp("empty", buffer, 5)) {
1868 memmove(buffer + 1, buffer + 5, n - 5);
1869 } else if (0 != strncmp("AC", buffer, 2)) {
1871 memmove(buffer + 1, buffer + 11, n - 11);
1875 int get_battery_perct(const char *bat)
1879 char acpi_path[128];
1880 char sysfs_path[128];
1881 int remaining_capacity = -1;
1883 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1884 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1888 idx = get_battery_idx(bat);
1890 /* don't update battery too often */
1891 if (current_update_time - last_battery_perct_time[idx] < 30) {
1892 return last_battery_perct[idx];
1894 last_battery_perct_time[idx] = current_update_time;
1896 /* Only check for SYSFS or ACPI */
1898 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1899 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1903 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1904 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1907 if (sysfs_bat_fp[idx] != NULL) {
1909 while (!feof(sysfs_bat_fp[idx])) {
1911 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1914 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1915 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1916 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1917 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1918 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1919 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1920 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1921 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1925 fclose(sysfs_bat_fp[idx]);
1926 sysfs_bat_fp[idx] = NULL;
1928 } else if (acpi_bat_fp[idx] != NULL) {
1930 /* read last full capacity if it's zero */
1931 if (acpi_design_capacity[idx] == 0) {
1936 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1937 fp = open_file(path, &rep2);
1942 if (fgets(b, 256, fp) == NULL) {
1945 if (sscanf(b, "last full capacity: %d",
1946 &acpi_design_capacity[idx]) != 0) {
1954 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1956 while (!feof(acpi_bat_fp[idx])) {
1959 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1963 if (buf[0] == 'r') {
1964 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1968 if (remaining_capacity < 0) {
1971 /* compute the battery percentage */
1972 last_battery_perct[idx] =
1973 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
1974 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
1975 return last_battery_perct[idx];
1978 int get_battery_perct_bar(const char *bar)
1982 get_battery_perct(bar);
1983 idx = get_battery_idx(bar);
1984 return (int) (last_battery_perct[idx] * 2.56 - 1);
1987 /* On Apple powerbook and ibook:
1988 $ cat /proc/pmu/battery_0
1995 $ cat /proc/pmu/info
1996 PMU driver version : 2
1997 PMU firmware version : 0c
2002 /* defines as in <linux/pmu.h> */
2003 #define PMU_BATT_PRESENT 0x00000001
2004 #define PMU_BATT_CHARGING 0x00000002
2006 static FILE *pmu_battery_fp;
2007 static FILE *pmu_info_fp;
2008 static char pb_battery_info[3][32];
2009 static double pb_battery_info_update;
2011 #define PMU_PATH "/proc/pmu"
2012 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2015 const char *batt_path = PMU_PATH "/battery_0";
2016 const char *info_path = PMU_PATH "/info";
2018 int charge, max_charge, ac = -1;
2021 /* don't update battery too often */
2022 if (current_update_time - pb_battery_info_update < 29.5) {
2023 snprintf(buffer, n, "%s", pb_battery_info[i]);
2026 pb_battery_info_update = current_update_time;
2028 if (pmu_battery_fp == NULL) {
2029 pmu_battery_fp = open_file(batt_path, &rep);
2030 if (pmu_battery_fp == NULL) {
2035 if (pmu_battery_fp != NULL) {
2036 rewind(pmu_battery_fp);
2037 while (!feof(pmu_battery_fp)) {
2040 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2044 if (buf[0] == 'f') {
2045 sscanf(buf, "flags : %8x", &flags);
2046 } else if (buf[0] == 'c' && buf[1] == 'h') {
2047 sscanf(buf, "charge : %d", &charge);
2048 } else if (buf[0] == 'm') {
2049 sscanf(buf, "max_charge : %d", &max_charge);
2050 } else if (buf[0] == 't') {
2051 sscanf(buf, "time rem. : %ld", &timeval);
2055 if (pmu_info_fp == NULL) {
2056 pmu_info_fp = open_file(info_path, &rep);
2057 if (pmu_info_fp == NULL) {
2062 if (pmu_info_fp != NULL) {
2063 rewind(pmu_info_fp);
2064 while (!feof(pmu_info_fp)) {
2067 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2070 if (buf[0] == 'A') {
2071 sscanf(buf, "AC Power : %d", &ac);
2075 /* update status string */
2076 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2077 strncpy(pb_battery_info[PB_BATT_STATUS], "AC", sizeof(pb_battery_info[PB_BATT_STATUS]));
2078 } else if (ac && (flags & PMU_BATT_PRESENT)
2079 && !(flags & PMU_BATT_CHARGING)) {
2080 strncpy(pb_battery_info[PB_BATT_STATUS], "charged", sizeof(pb_battery_info[PB_BATT_STATUS]));
2081 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2082 strncpy(pb_battery_info[PB_BATT_STATUS], "charging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2084 strncpy(pb_battery_info[PB_BATT_STATUS], "discharging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2087 /* update percentage string */
2088 if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT)
2089 && !(flags & PMU_BATT_CHARGING)) {
2090 snprintf(pb_battery_info[PB_BATT_PERCENT],
2091 sizeof(pb_battery_info[PB_BATT_PERCENT]), "100%%");
2092 } else if (timeval == 0) {
2093 snprintf(pb_battery_info[PB_BATT_PERCENT],
2094 sizeof(pb_battery_info[PB_BATT_PERCENT]), "unknown");
2096 snprintf(pb_battery_info[PB_BATT_PERCENT],
2097 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2098 (charge * 100) / max_charge);
2101 /* update time string */
2102 if (timeval == 0) { /* fully charged or battery not present */
2103 snprintf(pb_battery_info[PB_BATT_TIME],
2104 sizeof(pb_battery_info[PB_BATT_TIME]), "unknown");
2105 } else if (timeval < 60 * 60) { /* don't show secs */
2106 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2107 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2109 format_seconds(pb_battery_info[PB_BATT_TIME],
2110 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2113 snprintf(buffer, n, "%s", pb_battery_info[i]);
2116 void update_top(void)
2118 process_find_top(info.cpu, info.memu, info.time
2123 info.first_process = get_first_process();
2126 void update_entropy(void)
2129 const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2130 const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2133 info.entropy.entropy_avail = 0;
2134 info.entropy.poolsize = 0;
2136 if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2140 if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2145 fscanf(fp1, "%u", &info.entropy.entropy_avail);
2146 fscanf(fp2, "%u", &info.entropy.poolsize);
2151 info.mask |= (1 << INFO_ENTROPY);
2154 const char *get_disk_protect_queue(const char *disk)
2160 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2161 if (access(path, F_OK)) {
2162 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2164 if ((fp = fopen(path, "r")) == NULL)
2166 if (fscanf(fp, "%d\n", &state) != 1) {
2171 return (state > 0) ? "frozen" : "free ";
2174 void update_diskio(void)
2178 char buf[512], devbuf[64];
2179 unsigned int major, minor;
2181 struct diskio_stat *cur;
2182 unsigned int reads, writes;
2183 unsigned int total_reads = 0, total_writes = 0;
2186 stats.current_read = 0;
2187 stats.current_write = 0;
2189 if (!(fp = open_file("/proc/diskstats", &rep))) {
2193 /* read reads and writes from all disks (minor = 0), including cd-roms
2194 * and floppies, and sum them up */
2195 while (fgets(buf, 512, fp)) {
2196 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
2197 &minor, devbuf, &reads, &writes);
2198 /* ignore subdevices (they have only 3 matching entries in their line)
2199 * and virtual devices (LVM, network block devices, RAM disks, Loopback)
2201 * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
2202 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
2203 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
2204 total_reads += reads;
2205 total_writes += writes;
2207 col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
2208 &major, &minor, devbuf, &reads, &writes);
2209 if (col_count != 5) {
2214 while (cur && strcmp(devbuf, cur->dev))
2218 update_diskio_values(cur, reads, writes);
2220 update_diskio_values(&stats, total_reads, total_writes);