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);
938 double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
946 lseek(*fd, 0, SEEK_SET);
952 n = read(*fd, buf, 63);
953 /* should read until n == 0 but I doubt that kernel will give these
954 * in multiple pieces. :) */
956 NORM_ERR("get_sysfs_info(): read from %s failed\n", devtype);
965 *fd = open(devtype, O_RDONLY);
967 NORM_ERR("can't open '%s': %s", devtype, strerror(errno));
970 /* My dirty hack for computing CPU value
971 * Filedil, from forums.gentoo.org */
972 /* if (strstr(devtype, "temp1_input") != NULL) {
973 return -15.096 + 1.4893 * (val / 1000.0);
976 /* divide voltage and temperature by 1000 */
977 /* or if any other divisor is given, use that */
978 if (strcmp(type, "tempf") == 0) {
980 return ((val / divisor + 40) * 9.0 / 5) - 40;
981 } else if (divisor) {
982 return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
984 return ((val + 40) * 9.0 / 5) - 40;
988 return val / divisor;
989 } else if (divisor) {
997 /* Prior to kernel version 2.6.12, the CPU fan speed was available in
998 * ADT746X_FAN_OLD, whereas later kernel versions provide this information in
1000 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
1001 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
1003 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
1006 char adt746x_fan_state[64];
1009 if (!p_client_buffer || client_buffer_size <= 0) {
1013 if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
1014 && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL) {
1015 sprintf(adt746x_fan_state, "adt746x not found");
1017 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
1018 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
1022 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_fan_state);
1025 /* Prior to kernel version 2.6.12, the CPU temperature was found in
1026 * ADT746X_CPU_OLD, whereas later kernel versions provide this information in
1028 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
1029 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
1031 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
1034 char adt746x_cpu_state[64];
1037 if (!p_client_buffer || client_buffer_size <= 0) {
1041 if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
1042 && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL) {
1043 sprintf(adt746x_cpu_state, "adt746x not found");
1045 fscanf(fp, "%2s", adt746x_cpu_state);
1049 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state);
1052 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1053 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1055 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1056 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1057 const char *p_format, int divisor, unsigned int cpu)
1065 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1071 char current_freq_file[128];
1073 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1075 f = fopen(current_freq_file, "r");
1077 /* if there's a cpufreq /sys node, read the current frequency from
1078 * this node and divide by 1000 to get Mhz. */
1079 if (fgets(s, sizeof(s), f)) {
1080 s[strlen(s) - 1] = '\0';
1081 freq = strtod(s, NULL);
1084 snprintf(p_client_buffer, client_buffer_size, p_format,
1085 (freq / 1000) / divisor);
1090 // open the CPU information file
1091 f = open_file("/proc/cpuinfo", &rep);
1093 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1098 while (fgets(s, sizeof(s), f) != NULL) {
1100 #if defined(__i386) || defined(__x86_64)
1101 // and search for the cpu mhz
1102 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1104 #if defined(__alpha)
1105 // different on alpha
1106 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1108 // this is different on ppc for some reason
1109 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1110 #endif // defined(__alpha)
1111 #endif // defined(__i386) || defined(__x86_64)
1113 // copy just the number
1114 strcpy(frequency, strchr(s, ':') + 2);
1115 #if defined(__alpha)
1117 frequency[strlen(frequency) - 6] = '\0';
1118 // kernel reports in Hz
1119 freq = strtod(frequency, NULL) / 1000000;
1122 frequency[strlen(frequency) - 1] = '\0';
1123 freq = strtod(frequency, NULL);
1127 if (strncmp(s, "processor", 9) == 0) {
1134 snprintf(p_client_buffer, client_buffer_size, p_format,
1135 (float) freq / divisor);
1139 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1141 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1151 * Peter Tarjan (ptarjan@citromail.hu) */
1153 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1154 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1155 const char *p_format, int divisor, unsigned int cpu)
1161 char current_freq_file[128];
1164 /* build the voltage file name */
1166 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1169 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1174 /* read the current cpu frequency from the /sys node */
1175 f = fopen(current_freq_file, "r");
1177 if (fgets(s, sizeof(s), f)) {
1178 s[strlen(s) - 1] = '\0';
1179 freq = strtod(s, NULL);
1183 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1184 perror("get_voltage()");
1191 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1194 /* use the current cpu frequency to find the corresponding voltage */
1195 f = fopen(current_freq_file, "r");
1201 if (fgets(line, 255, f) == NULL) {
1204 sscanf(line, "%d %d", &freq_comp, &voltage);
1205 if (freq_comp == freq) {
1211 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1212 perror("get_voltage()");
1218 snprintf(p_client_buffer, client_buffer_size, p_format,
1219 (float) voltage / divisor);
1223 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1225 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1232 if (!p_client_buffer || client_buffer_size <= 0) {
1236 /* yeah, slow... :/ */
1237 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1238 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1242 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1244 fp = open_file(buf2, &rep);
1246 snprintf(p_client_buffer, client_buffer_size,
1247 "can't open fan's state file");
1250 memset(buf, 0, sizeof(buf));
1251 fscanf(fp, "%*s %99s", buf);
1254 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1257 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1258 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1259 /* Linux 2.6.25 onwards ac adapter info is in
1260 /sys/class/power_supply/AC/
1261 On my system I get the following.
1262 /sys/class/power_supply/AC/uevent:
1263 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1266 POWER_SUPPLY_NAME=AC
1267 POWER_SUPPLY_TYPE=Mains
1268 POWER_SUPPLY_ONLINE=1
1271 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1279 if (!p_client_buffer || client_buffer_size <= 0) {
1283 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1284 fp = open_file(buf2, &rep);
1286 /* sysfs processing */
1288 if (fgets(buf, sizeof(buf), fp) == NULL)
1291 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1293 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1294 snprintf(p_client_buffer, client_buffer_size,
1295 "%s-line", (online ? "on" : "off"));
1301 /* yeah, slow... :/ */
1302 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1303 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1307 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1309 fp = open_file(buf2, &rep);
1311 snprintf(p_client_buffer, client_buffer_size,
1312 "No ac adapter found.... where is it?");
1315 memset(buf, 0, sizeof(buf));
1316 fscanf(fp, "%*s %99s", buf);
1319 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1324 /proc/acpi/thermal_zone/THRM/cooling_mode
1325 cooling mode: active
1326 /proc/acpi/thermal_zone/THRM/polling_frequency
1328 /proc/acpi/thermal_zone/THRM/state
1330 /proc/acpi/thermal_zone/THRM/temperature
1332 /proc/acpi/thermal_zone/THRM/trip_points
1334 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1337 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1338 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1340 int open_acpi_temperature(const char *name)
1346 if (name == NULL || strcmp(name, "*") == 0) {
1349 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1355 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1357 fd = open(path, O_RDONLY);
1359 NORM_ERR("can't open '%s': %s", path, strerror(errno));
1365 static double last_acpi_temp;
1366 static double last_acpi_temp_time;
1368 double get_acpi_temperature(int fd)
1374 /* don't update acpi temperature too often */
1375 if (current_update_time - last_acpi_temp_time < 11.32) {
1376 return last_acpi_temp;
1378 last_acpi_temp_time = current_update_time;
1380 /* seek to beginning */
1381 lseek(fd, 0, SEEK_SET);
1388 n = read(fd, buf, 255);
1390 NORM_ERR("can't read fd %d: %s", fd, strerror(errno));
1393 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1397 return last_acpi_temp;
1401 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1403 design capacity: 4400 mAh
1404 last full capacity: 4064 mAh
1405 battery technology: rechargeable
1406 design voltage: 14800 mV
1407 design capacity warning: 300 mAh
1408 design capacity low: 200 mAh
1409 capacity granularity 1: 32 mAh
1410 capacity granularity 2: 32 mAh
1412 serial number: 16922
1418 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1421 charging state: unknown
1423 remaining capacity: 4064 mAh
1424 present voltage: 16608 mV
1428 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1429 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1430 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1431 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1432 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1434 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1435 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1437 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1438 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1441 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1442 Linux 2.6.24 onwards battery info is in
1443 /sys/class/power_supply/BAT0/
1444 On my system I get the following.
1445 /sys/class/power_supply/BAT0/uevent:
1446 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1448 PHYSDEVDRIVER=battery
1449 POWER_SUPPLY_NAME=BAT0
1450 POWER_SUPPLY_TYPE=Battery
1451 POWER_SUPPLY_STATUS=Discharging
1452 POWER_SUPPLY_PRESENT=1
1453 POWER_SUPPLY_TECHNOLOGY=Li-ion
1454 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1455 POWER_SUPPLY_VOLTAGE_NOW=10780000
1456 POWER_SUPPLY_CURRENT_NOW=13970000
1457 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1458 POWER_SUPPLY_ENERGY_FULL=27370000
1459 POWER_SUPPLY_ENERGY_NOW=11810000
1460 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1461 POWER_SUPPLY_MANUFACTURER=Panasonic
1462 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1465 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1466 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1467 #define APM_PATH "/proc/apm"
1468 #define MAX_BATTERY_COUNT 4
1470 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1471 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1472 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1474 static int batteries_initialized = 0;
1475 static char batteries[MAX_BATTERY_COUNT][32];
1477 static int acpi_last_full[MAX_BATTERY_COUNT];
1478 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1480 /* e.g. "charging 75%" */
1481 static char last_battery_str[MAX_BATTERY_COUNT][64];
1483 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1485 static double last_battery_time[MAX_BATTERY_COUNT];
1487 static int last_battery_perct[MAX_BATTERY_COUNT];
1488 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1490 void init_batteries(void)
1494 if (batteries_initialized) {
1498 #pragma omp parallel for schedule(dynamic,10)
1499 #endif /* HAVE_OPENMP */
1500 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1501 batteries[idx][0] = '\0';
1503 batteries_initialized = 1;
1506 int get_battery_idx(const char *bat)
1510 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1511 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1516 /* if not found, enter a new entry */
1517 if (!strlen(batteries[idx])) {
1518 snprintf(batteries[idx], 31, "%s", bat);
1524 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1526 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1528 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1529 char acpi_path[128];
1530 char sysfs_path[128];
1532 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1533 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1537 idx = get_battery_idx(bat);
1539 /* don't update battery too often */
1540 if (current_update_time - last_battery_time[idx] < 29.5) {
1541 set_return_value(buffer, n, item, idx);
1545 last_battery_time[idx] = current_update_time;
1547 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1548 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1550 /* first try SYSFS if that fails try ACPI */
1552 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1553 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1556 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1557 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1560 if (sysfs_bat_fp[idx] != NULL) {
1562 int present_rate = -1;
1563 int remaining_capacity = -1;
1564 char charging_state[64];
1567 strcpy(charging_state, "unknown");
1569 while (!feof(sysfs_bat_fp[idx])) {
1571 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1574 /* let's just hope units are ok */
1575 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1576 strcpy(present, "yes");
1577 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1578 strcpy(present, "no");
1579 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1580 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1581 /* present_rate is not the same as the
1582 current flowing now but it is the same value
1583 which was used in the past. so we continue
1585 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1586 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1587 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1588 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1589 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1590 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1591 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1592 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1593 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1594 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1597 fclose(sysfs_bat_fp[idx]);
1598 sysfs_bat_fp[idx] = NULL;
1600 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1601 if (remaining_capacity > acpi_last_full[idx])
1602 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1605 if (strcmp(present, "No") == 0) {
1606 strncpy(last_battery_str[idx], "not present", 64);
1609 else if (strcmp(charging_state, "Charging") == 0) {
1610 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1611 /* e.g. charging 75% */
1612 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1613 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1615 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1616 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1617 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1618 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1619 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1620 snprintf(last_battery_time_str[idx],
1621 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1623 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1624 snprintf(last_battery_time_str[idx],
1625 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1629 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1630 if (present_rate > 0) {
1631 /* e.g. discharging 35% */
1632 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1633 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1635 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1636 (long) (((float) remaining_capacity / present_rate) * 3600));
1637 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1638 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1639 snprintf(last_battery_time_str[idx],
1640 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1642 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1644 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1645 snprintf(last_battery_time_str[idx],
1646 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1650 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1651 else if (strncmp(charging_state, "Charged", 64) == 0 || strncmp(charging_state, "Full", 64) == 0) {
1652 /* Below happens with the second battery on my X40,
1653 * when the second one is empty and the first one
1655 if (remaining_capacity == 0)
1656 strcpy(last_battery_str[idx], "empty");
1658 strcpy(last_battery_str[idx], "charged");
1660 /* unknown, probably full / AC */
1662 if (acpi_last_full[idx] != 0
1663 && remaining_capacity != acpi_last_full[idx])
1664 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1665 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1667 strncpy(last_battery_str[idx], "AC", 64);
1669 } else if (acpi_bat_fp[idx] != NULL) {
1671 int present_rate = -1;
1672 int remaining_capacity = -1;
1673 char charging_state[64];
1676 /* read last full capacity if it's zero */
1677 if (acpi_last_full[idx] == 0) {
1678 static int rep3 = 0;
1682 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1683 fp = open_file(path, &rep3);
1688 if (fgets(b, 256, fp) == NULL) {
1691 if (sscanf(b, "last full capacity: %d",
1692 &acpi_last_full[idx]) != 0) {
1701 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1703 strcpy(charging_state, "unknown");
1705 while (!feof(acpi_bat_fp[idx])) {
1708 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1712 /* let's just hope units are ok */
1713 if (strncmp(buf, "present:", 8) == 0) {
1714 sscanf(buf, "present: %4s", present);
1715 } else if (strncmp(buf, "charging state:", 15) == 0) {
1716 sscanf(buf, "charging state: %63s", charging_state);
1717 } else if (strncmp(buf, "present rate:", 13) == 0) {
1718 sscanf(buf, "present rate: %d", &present_rate);
1719 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1720 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1723 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1724 if (remaining_capacity > acpi_last_full[idx]) {
1725 /* normalize to 100% */
1726 acpi_last_full[idx] = remaining_capacity;
1730 if (strcmp(present, "no") == 0) {
1731 strncpy(last_battery_str[idx], "not present", 64);
1733 } else if (strcmp(charging_state, "charging") == 0) {
1734 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1735 /* e.g. charging 75% */
1736 snprintf(last_battery_str[idx],
1737 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1738 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1740 format_seconds(last_battery_time_str[idx],
1741 sizeof(last_battery_time_str[idx]) - 1,
1742 (long) (((acpi_last_full[idx] - remaining_capacity) *
1743 3600) / present_rate));
1744 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1745 snprintf(last_battery_str[idx],
1746 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1747 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1748 snprintf(last_battery_time_str[idx],
1749 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1751 strncpy(last_battery_str[idx], "charging",
1752 sizeof(last_battery_str[idx]) - 1);
1753 snprintf(last_battery_time_str[idx],
1754 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1757 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1758 if (present_rate > 0) {
1759 /* e.g. discharging 35% */
1760 snprintf(last_battery_str[idx],
1761 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1762 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1764 format_seconds(last_battery_time_str[idx],
1765 sizeof(last_battery_time_str[idx]) - 1,
1766 (long) ((remaining_capacity * 3600) / present_rate));
1767 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1768 snprintf(last_battery_str[idx],
1769 sizeof(last_battery_str[idx]) - 1, "full");
1770 snprintf(last_battery_time_str[idx],
1771 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1773 snprintf(last_battery_str[idx],
1774 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1775 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1776 snprintf(last_battery_time_str[idx],
1777 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1780 } else if (strncmp(charging_state, "charged", 64) == 0) {
1781 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1782 /* Below happens with the second battery on my X40,
1783 * when the second one is empty and the first one being charged. */
1784 if (remaining_capacity == 0) {
1785 strcpy(last_battery_str[idx], "empty");
1787 strcpy(last_battery_str[idx], "charged");
1789 /* unknown, probably full / AC */
1791 if (strncmp(charging_state, "Full", 64) == 0) {
1792 strncpy(last_battery_str[idx], "full", 64);
1793 } else if (acpi_last_full[idx] != 0
1794 && remaining_capacity != acpi_last_full[idx]) {
1795 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1796 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1798 strncpy(last_battery_str[idx], "AC", 64);
1801 fclose(acpi_bat_fp[idx]);
1802 acpi_bat_fp[idx] = NULL;
1805 if (apm_bat_fp[idx] == NULL) {
1806 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1809 if (apm_bat_fp[idx] != NULL) {
1810 unsigned int ac, status, flag;
1813 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1814 &ac, &status, &flag, &life);
1817 /* could check now that there is ac */
1818 snprintf(last_battery_str[idx], 64, "AC");
1820 /* could check that status == 3 here? */
1821 } else if (ac && life != 100) {
1822 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1824 snprintf(last_battery_str[idx], 64, "%d%%", life);
1827 /* it seemed to buffer it so file must be closed (or could use
1828 * syscalls directly but I don't feel like coding it now) */
1829 fclose(apm_bat_fp[idx]);
1830 apm_bat_fp[idx] = NULL;
1833 set_return_value(buffer, n, item, idx);
1836 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1839 case BATTERY_STATUS:
1840 snprintf(buffer, n, "%s", last_battery_str[idx]);
1843 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1850 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1852 get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1853 if (0 == strncmp("charging", buffer, 8)) {
1855 memmove(buffer + 1, buffer + 8, n - 8);
1856 } else if (0 == strncmp("discharging", buffer, 11)) {
1858 memmove(buffer + 1, buffer + 11, n - 11);
1859 } else if (0 == strncmp("charged", buffer, 7)) {
1861 memmove(buffer + 1, buffer + 7, n - 7);
1862 } else if (0 == strncmp("not present", buffer, 11)) {
1864 memmove(buffer + 1, buffer + 11, n - 11);
1865 } else if (0 == strncmp("empty", buffer, 5)) {
1867 memmove(buffer + 1, buffer + 5, n - 5);
1868 } else if (0 != strncmp("AC", buffer, 2)) {
1870 memmove(buffer + 1, buffer + 11, n - 11);
1874 int get_battery_perct(const char *bat)
1878 char acpi_path[128];
1879 char sysfs_path[128];
1880 int remaining_capacity = -1;
1882 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1883 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1887 idx = get_battery_idx(bat);
1889 /* don't update battery too often */
1890 if (current_update_time - last_battery_perct_time[idx] < 30) {
1891 return last_battery_perct[idx];
1893 last_battery_perct_time[idx] = current_update_time;
1895 /* Only check for SYSFS or ACPI */
1897 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1898 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1902 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1903 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1906 if (sysfs_bat_fp[idx] != NULL) {
1908 while (!feof(sysfs_bat_fp[idx])) {
1910 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1913 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1914 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1915 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1916 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1917 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1918 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1919 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1920 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1924 fclose(sysfs_bat_fp[idx]);
1925 sysfs_bat_fp[idx] = NULL;
1927 } else if (acpi_bat_fp[idx] != NULL) {
1929 /* read last full capacity if it's zero */
1930 if (acpi_design_capacity[idx] == 0) {
1935 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1936 fp = open_file(path, &rep2);
1941 if (fgets(b, 256, fp) == NULL) {
1944 if (sscanf(b, "last full capacity: %d",
1945 &acpi_design_capacity[idx]) != 0) {
1953 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1955 while (!feof(acpi_bat_fp[idx])) {
1958 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1962 if (buf[0] == 'r') {
1963 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1967 if (remaining_capacity < 0) {
1970 /* compute the battery percentage */
1971 last_battery_perct[idx] =
1972 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
1973 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
1974 return last_battery_perct[idx];
1977 int get_battery_perct_bar(const char *bar)
1981 get_battery_perct(bar);
1982 idx = get_battery_idx(bar);
1983 return (int) (last_battery_perct[idx] * 2.56 - 1);
1986 /* On Apple powerbook and ibook:
1987 $ cat /proc/pmu/battery_0
1994 $ cat /proc/pmu/info
1995 PMU driver version : 2
1996 PMU firmware version : 0c
2001 /* defines as in <linux/pmu.h> */
2002 #define PMU_BATT_PRESENT 0x00000001
2003 #define PMU_BATT_CHARGING 0x00000002
2005 static FILE *pmu_battery_fp;
2006 static FILE *pmu_info_fp;
2007 static char pb_battery_info[3][32];
2008 static double pb_battery_info_update;
2010 #define PMU_PATH "/proc/pmu"
2011 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2014 const char *batt_path = PMU_PATH "/battery_0";
2015 const char *info_path = PMU_PATH "/info";
2017 int charge, max_charge, ac = -1;
2020 /* don't update battery too often */
2021 if (current_update_time - pb_battery_info_update < 29.5) {
2022 snprintf(buffer, n, "%s", pb_battery_info[i]);
2025 pb_battery_info_update = current_update_time;
2027 if (pmu_battery_fp == NULL) {
2028 pmu_battery_fp = open_file(batt_path, &rep);
2029 if (pmu_battery_fp == NULL) {
2034 if (pmu_battery_fp != NULL) {
2035 rewind(pmu_battery_fp);
2036 while (!feof(pmu_battery_fp)) {
2039 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2043 if (buf[0] == 'f') {
2044 sscanf(buf, "flags : %8x", &flags);
2045 } else if (buf[0] == 'c' && buf[1] == 'h') {
2046 sscanf(buf, "charge : %d", &charge);
2047 } else if (buf[0] == 'm') {
2048 sscanf(buf, "max_charge : %d", &max_charge);
2049 } else if (buf[0] == 't') {
2050 sscanf(buf, "time rem. : %ld", &timeval);
2054 if (pmu_info_fp == NULL) {
2055 pmu_info_fp = open_file(info_path, &rep);
2056 if (pmu_info_fp == NULL) {
2061 if (pmu_info_fp != NULL) {
2062 rewind(pmu_info_fp);
2063 while (!feof(pmu_info_fp)) {
2066 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2069 if (buf[0] == 'A') {
2070 sscanf(buf, "AC Power : %d", &ac);
2074 /* update status string */
2075 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2076 strncpy(pb_battery_info[PB_BATT_STATUS], "AC", sizeof(pb_battery_info[PB_BATT_STATUS]));
2077 } else if (ac && (flags & PMU_BATT_PRESENT)
2078 && !(flags & PMU_BATT_CHARGING)) {
2079 strncpy(pb_battery_info[PB_BATT_STATUS], "charged", sizeof(pb_battery_info[PB_BATT_STATUS]));
2080 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2081 strncpy(pb_battery_info[PB_BATT_STATUS], "charging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2083 strncpy(pb_battery_info[PB_BATT_STATUS], "discharging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2086 /* update percentage string */
2087 if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT)
2088 && !(flags & PMU_BATT_CHARGING)) {
2089 snprintf(pb_battery_info[PB_BATT_PERCENT],
2090 sizeof(pb_battery_info[PB_BATT_PERCENT]), "100%%");
2091 } else if (timeval == 0) {
2092 snprintf(pb_battery_info[PB_BATT_PERCENT],
2093 sizeof(pb_battery_info[PB_BATT_PERCENT]), "unknown");
2095 snprintf(pb_battery_info[PB_BATT_PERCENT],
2096 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2097 (charge * 100) / max_charge);
2100 /* update time string */
2101 if (timeval == 0) { /* fully charged or battery not present */
2102 snprintf(pb_battery_info[PB_BATT_TIME],
2103 sizeof(pb_battery_info[PB_BATT_TIME]), "unknown");
2104 } else if (timeval < 60 * 60) { /* don't show secs */
2105 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2106 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2108 format_seconds(pb_battery_info[PB_BATT_TIME],
2109 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2112 snprintf(buffer, n, "%s", pb_battery_info[i]);
2115 void update_top(void)
2117 process_find_top(info.cpu, info.memu, info.time
2122 info.first_process = get_first_process();
2125 void update_entropy(void)
2128 const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2129 const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2132 info.entropy.entropy_avail = 0;
2133 info.entropy.poolsize = 0;
2135 if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2139 if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2144 fscanf(fp1, "%u", &info.entropy.entropy_avail);
2145 fscanf(fp2, "%u", &info.entropy.poolsize);
2150 info.mask |= (1 << INFO_ENTROPY);
2153 const char *get_disk_protect_queue(const char *disk)
2159 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2160 if (access(path, F_OK)) {
2161 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2163 if ((fp = fopen(path, "r")) == NULL)
2165 if (fscanf(fp, "%d\n", &state) != 1) {
2170 return (state > 0) ? "frozen" : "free ";
2173 void update_diskio(void)
2177 char buf[512], devbuf[64];
2178 unsigned int major, minor;
2180 struct diskio_stat *cur;
2181 unsigned int reads, writes;
2182 unsigned int total_reads = 0, total_writes = 0;
2185 stats.current_read = 0;
2186 stats.current_write = 0;
2188 if (!(fp = open_file("/proc/diskstats", &rep))) {
2192 /* read reads and writes from all disks (minor = 0), including cd-roms
2193 * and floppies, and sum them up */
2194 while (fgets(buf, 512, fp)) {
2195 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
2196 &minor, devbuf, &reads, &writes);
2197 /* ignore subdevices (they have only 3 matching entries in their line)
2198 * and virtual devices (LVM, network block devices, RAM disks, Loopback)
2200 * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
2201 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
2202 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
2203 total_reads += reads;
2204 total_writes += writes;
2206 col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
2207 &major, &minor, devbuf, &reads, &writes);
2208 if (col_count != 5) {
2213 while (cur && strcmp(devbuf, cur->dev))
2217 update_diskio_values(cur, reads, writes);
2219 update_diskio_values(&stats, total_reads, total_writes);