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);
121 int check_mount(char *s)
124 FILE *mtab = fopen("/etc/mtab", "r");
127 char buf1[256], buf2[128];
129 while (fgets(buf1, 256, mtab)) {
130 sscanf(buf1, "%*s %128s", buf2);
131 if (!strcmp(s, buf2)) {
138 NORM_ERR("Could not open mtab");
143 /* these things are also in sysinfo except Buffers:
144 * (that's why I'm reading them from proc) */
146 void update_meminfo(void)
151 /* unsigned int a; */
154 info.mem = info.memmax = info.swap = info.swapfree = info.swapmax = info.bufmem =
155 info.buffers = info.cached = info.memfree = info.memeasyfree = 0;
157 if (!(meminfo_fp = open_file("/proc/meminfo", &rep))) {
161 while (!feof(meminfo_fp)) {
162 if (fgets(buf, 255, meminfo_fp) == NULL) {
166 if (strncmp(buf, "MemTotal:", 9) == 0) {
167 sscanf(buf, "%*s %llu", &info.memmax);
168 } else if (strncmp(buf, "MemFree:", 8) == 0) {
169 sscanf(buf, "%*s %llu", &info.memfree);
170 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
171 sscanf(buf, "%*s %llu", &info.swapmax);
172 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
173 sscanf(buf, "%*s %llu", &info.swapfree);
174 } else if (strncmp(buf, "Buffers:", 8) == 0) {
175 sscanf(buf, "%*s %llu", &info.buffers);
176 } else if (strncmp(buf, "Cached:", 7) == 0) {
177 sscanf(buf, "%*s %llu", &info.cached);
181 info.mem = info.memmax - info.memfree;
182 info.memeasyfree = info.memfree;
183 info.swap = info.swapmax - info.swapfree;
185 info.bufmem = info.cached + info.buffers;
190 int get_laptop_mode(void)
195 if ((fp = fopen("/proc/sys/vm/laptop_mode", "r")) != NULL)
196 fscanf(fp, "%d\n", &val);
202 * # cat /sys/block/sda/queue/scheduler
203 * noop [anticipatory] cfq
205 char *get_ioscheduler(char *disk)
211 return strndup("n/a", text_buffer_size);
213 snprintf(buf, 127, "/sys/block/%s/queue/scheduler", disk);
214 if ((fp = fopen(buf, "r")) == NULL) {
215 return strndup("n/a", text_buffer_size);
218 fscanf(fp, "%127s", buf);
220 buf[strlen(buf) - 1] = '\0';
222 return strndup(buf + 1, text_buffer_size);
226 return strndup("n/a", text_buffer_size);
229 #define COND_FREE(x) if(x) free(x); x = 0
230 #define SAVE_SET_STRING(x, y) \
231 if (x && strcmp((char *)x, (char *)y)) { \
233 x = strndup("multiple", text_buffer_size); \
235 x = strndup(y, text_buffer_size); \
238 void update_gateway_info_failure(const char *reason)
243 //2 pointers to 1 location causes a crash when we try to free them both
244 info.gw_info.iface = strndup("failed", text_buffer_size);
245 info.gw_info.ip = strndup("failed", text_buffer_size);
249 /* Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT */
250 #define RT_ENTRY_FORMAT "%63s %lx %lx %x %*d %*d %*d %lx %*d %*d %*d\n"
252 void update_gateway_info(void)
257 unsigned long dest, gate, mask;
260 struct gateway_info *gw_info = &info.gw_info;
262 COND_FREE(gw_info->iface);
263 COND_FREE(gw_info->ip);
266 if ((fp = fopen("/proc/net/route", "r")) == NULL) {
267 update_gateway_info_failure("fopen()");
271 /* skip over the table header line, which is always present */
272 fscanf(fp, "%*[^\n]\n");
275 if(fscanf(fp, RT_ENTRY_FORMAT,
276 iface, &dest, &gate, &flags, &mask) != 5) {
277 update_gateway_info_failure("fscanf()");
280 if (!(dest || mask) && ((flags & RTF_GATEWAY) || !gate) ) {
282 SAVE_SET_STRING(gw_info->iface, iface)
284 SAVE_SET_STRING(gw_info->ip, inet_ntoa(ina))
291 void update_net_stats(void)
295 static char first = 1;
297 // FIXME: arbitrary size chosen to keep code simple.
299 unsigned int curtmp1, curtmp2;
306 // wireless info variables
307 int skfd, has_bitrate = 0;
308 struct wireless_info *winfo;
313 delta = current_update_time - last_update_time;
314 if (delta <= 0.0001) {
318 /* open file and ignore first two lines */
319 if (!(net_dev_fp = open_file("/proc/net/dev", &rep))) {
324 fgets(buf, 255, net_dev_fp); /* garbage */
325 fgets(buf, 255, net_dev_fp); /* garbage (field names) */
327 /* read each interface */
328 for (i2 = 0; i2 < 16; i2++) {
332 long long r, t, last_recv, last_trans;
334 if (fgets(buf, 255, net_dev_fp) == NULL) {
338 while (isspace((int) *p)) {
344 while (*p && *p != ':') {
353 ns = get_net_stat(s, NULL, NULL);
355 memset(&(ns->addr.sa_data), 0, 14);
357 memset(ns->addrs, 0, 17 * 16 + 1); /* Up to 17 chars per ip, max 16 interfaces. Nasty memory usage... */
359 last_recv = ns->recv;
360 last_trans = ns->trans;
362 /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
363 sscanf(p, "%lld %*d %*d %*d %*d %*d %*d %*d %lld",
366 /* if recv or trans is less than last time, an overflow happened */
367 if (r < ns->last_read_recv) {
370 ns->recv += (r - ns->last_read_recv);
372 ns->last_read_recv = r;
374 if (t < ns->last_read_trans) {
377 ns->trans += (t - ns->last_read_trans);
379 ns->last_read_trans = t;
381 /*** ip addr patch ***/
382 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
384 conf.ifc_buf = malloc(sizeof(struct ifreq) * 16);
385 conf.ifc_len = sizeof(struct ifreq) * 16;
386 memset(conf.ifc_buf, 0, conf.ifc_len);
388 ioctl((long) i, SIOCGIFCONF, &conf);
390 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
391 struct net_stat *ns2;
393 if (!(((struct ifreq *) conf.ifc_buf) + k))
397 ((struct ifreq *) conf.ifc_buf)[k].ifr_ifrn.ifrn_name, NULL, NULL);
398 ns2->addr = ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.ifru_addr;
399 sprintf(temp_addr, "%u.%u.%u.%u, ",
400 ns2->addr.sa_data[2] & 255,
401 ns2->addr.sa_data[3] & 255,
402 ns2->addr.sa_data[4] & 255,
403 ns2->addr.sa_data[5] & 255);
404 if(NULL == strstr(ns2->addrs, temp_addr))
405 strncpy(ns2->addrs + strlen(ns2->addrs), temp_addr, 17);
412 /*** end ip addr patch ***/
415 /* calculate speeds */
416 ns->net_rec[0] = (ns->recv - last_recv) / delta;
417 ns->net_trans[0] = (ns->trans - last_trans) / delta;
424 #pragma omp parallel for reduction(+:curtmp1, curtmp2) schedule(dynamic,10)
425 #endif /* HAVE_OPENMP */
426 for (i = 0; i < info.net_avg_samples; i++) {
427 curtmp1 = curtmp1 + ns->net_rec[i];
428 curtmp2 = curtmp2 + ns->net_trans[i];
436 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
437 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
438 if (info.net_avg_samples > 1) {
440 #pragma omp parallel for schedule(dynamic,10)
441 #endif /* HAVE_OPENMP */
442 for (i = info.net_avg_samples; i > 1; i--) {
443 ns->net_rec[i - 1] = ns->net_rec[i - 2];
444 ns->net_trans[i - 1] = ns->net_trans[i - 2];
449 /* update wireless info */
450 winfo = malloc(sizeof(struct wireless_info));
451 memset(winfo, 0, sizeof(struct wireless_info));
453 skfd = iw_sockets_open();
454 if (iw_get_basic_config(skfd, s, &(winfo->b)) > -1) {
456 // set present winfo variables
457 if (iw_get_stats(skfd, s, &(winfo->stats),
458 &winfo->range, winfo->has_range) >= 0) {
459 winfo->has_stats = 1;
461 if (iw_get_range_info(skfd, s, &(winfo->range)) >= 0) {
462 winfo->has_range = 1;
464 if (iw_get_ext(skfd, s, SIOCGIWAP, &wrq) >= 0) {
465 winfo->has_ap_addr = 1;
466 memcpy(&(winfo->ap_addr), &(wrq.u.ap_addr), sizeof(sockaddr));
470 if (iw_get_ext(skfd, s, SIOCGIWRATE, &wrq) >= 0) {
471 memcpy(&(winfo->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
472 iw_print_bitrate(ns->bitrate, 16, winfo->bitrate.value);
477 if (winfo->has_range && winfo->has_stats
478 && ((winfo->stats.qual.level != 0)
479 || (winfo->stats.qual.updated & IW_QUAL_DBM))) {
480 if (!(winfo->stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
481 ns->link_qual = winfo->stats.qual.qual;
482 ns->link_qual_max = winfo->range.max_qual.qual;
487 if (winfo->has_ap_addr) {
488 iw_sawap_ntop(&winfo->ap_addr, ns->ap);
492 if (winfo->b.has_essid) {
493 if (winfo->b.essid_on) {
494 snprintf(ns->essid, 32, "%s", winfo->b.essid);
496 snprintf(ns->essid, 32, "off/any");
500 snprintf(ns->mode, 16, "%s", iw_operation_mode[winfo->b.mode]);
502 iw_sockets_close(skfd);
513 void update_total_processes(void)
517 struct sysinfo s_info;
520 info.procs = s_info.procs;
527 if (!(fp = open_file("/proc/loadavg", &rep))) {
531 fscanf(fp, "%*f %*f %*f %*d/%hu", &info.procs);
536 #define CPU_SAMPLE_COUNT 15
538 unsigned long long cpu_user;
539 unsigned long long cpu_system;
540 unsigned long long cpu_nice;
541 unsigned long long cpu_idle;
542 unsigned long long cpu_iowait;
543 unsigned long long cpu_irq;
544 unsigned long long cpu_softirq;
545 unsigned long long cpu_steal;
546 unsigned long long cpu_total;
547 unsigned long long cpu_active_total;
548 unsigned long long cpu_last_total;
549 unsigned long long cpu_last_active_total;
550 double cpu_val[CPU_SAMPLE_COUNT];
552 static short cpu_setup = 0;
554 /* Determine if this kernel gives us "extended" statistics information in
556 * Kernels around 2.5 and earlier only reported user, system, nice, and
557 * idle values in proc stat.
558 * Kernels around 2.6 and greater report these PLUS iowait, irq, softirq,
560 void determine_longstat(char *buf)
562 unsigned long long iowait = 0;
564 KFLAG_SETOFF(KFLAG_IS_LONGSTAT);
565 /* scanf will either return -1 or 1 because there is only 1 assignment */
566 if (sscanf(buf, "%*s %*d %*d %*d %*d %llu", &iowait) > 0) {
567 KFLAG_SETON(KFLAG_IS_LONGSTAT);
571 void get_cpu_count(void)
577 if (info.cpu_usage) {
581 if (!(stat_fp = open_file("/proc/stat", &rep))) {
587 while (!feof(stat_fp)) {
588 if (fgets(buf, 255, stat_fp) == NULL) {
592 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
593 if (info.cpu_count == 0) {
594 determine_longstat(buf);
599 info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
604 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
605 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
607 inline static void update_stat(void)
611 static struct cpu_info *cpu = NULL;
616 const char *stat_template = NULL;
617 unsigned int malloc_cpu_size = 0;
618 extern void* global_cpu;
620 /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
621 if (!cpu_setup || !info.cpu_usage) {
626 if (!stat_template) {
628 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
632 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
633 cpu = malloc(malloc_cpu_size);
634 memset(cpu, 0, malloc_cpu_size);
638 if (!(stat_fp = open_file("/proc/stat", &rep))) {
640 if (info.cpu_usage) {
641 memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
647 while (!feof(stat_fp)) {
648 if (fgets(buf, 255, stat_fp) == NULL) {
652 if (strncmp(buf, "procs_running ", 14) == 0) {
653 sscanf(buf, "%*s %hu", &info.run_procs);
654 } else if (strncmp(buf, "cpu", 3) == 0) {
656 if (isdigit(buf[3])) {
657 idx = atoi(&buf[3]) + 1;
661 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
662 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
663 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
664 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
665 &(cpu[idx].cpu_steal));
667 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
668 cpu[idx].cpu_system + cpu[idx].cpu_idle +
669 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
670 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
672 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
673 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
675 delta = current_update_time - last_update_time;
677 if (delta <= 0.001) {
681 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
682 cpu[idx].cpu_last_active_total) /
683 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
686 #pragma omp parallel for reduction(+:curtmp) schedule(dynamic,10)
687 #endif /* HAVE_OPENMP */
688 for (i = 0; i < info.cpu_avg_samples; i++) {
689 curtmp = curtmp + cpu[idx].cpu_val[i];
691 /* TESTING -- I've removed this, because I don't think it is right.
692 * You shouldn't divide by the cpu count here ...
693 * removing for testing */
695 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
698 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
700 /* TESTING -- this line replaces the prev. "suspect" if/else */
701 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
703 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
704 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
706 #pragma omp parallel for schedule(dynamic,10)
707 #endif /* HAVE_OPENMP */
708 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
709 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
716 void update_running_processes(void)
721 void update_cpu_usage(void)
726 void update_load_average(void)
728 #ifdef HAVE_GETLOADAVG
733 info.loadavg[0] = (float) v[0];
734 info.loadavg[1] = (float) v[1];
735 info.loadavg[2] = (float) v[2];
742 if (!(fp = open_file("/proc/loadavg", &rep))) {
743 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
746 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
752 #define PROC_I8K "/proc/i8k"
753 #define I8K_DELIM " "
754 static char *i8k_procbuf = NULL;
755 void update_i8k(void)
760 i8k_procbuf = (char *) malloc(128 * sizeof(char));
762 if ((fp = fopen(PROC_I8K, "r")) == NULL) {
763 CRIT_ERR(NULL, NULL, "/proc/i8k doesn't exist! use insmod to make sure the kernel "
764 "driver is loaded...");
767 memset(&i8k_procbuf[0], 0, 128);
768 if (fread(&i8k_procbuf[0], sizeof(char), 128, fp) == 0) {
769 NORM_ERR("something wrong with /proc/i8k...");
774 i8k.version = strtok(&i8k_procbuf[0], I8K_DELIM);
775 i8k.bios = strtok(NULL, I8K_DELIM);
776 i8k.serial = strtok(NULL, I8K_DELIM);
777 i8k.cpu_temp = strtok(NULL, I8K_DELIM);
778 i8k.left_fan_status = strtok(NULL, I8K_DELIM);
779 i8k.right_fan_status = strtok(NULL, I8K_DELIM);
780 i8k.left_fan_rpm = strtok(NULL, I8K_DELIM);
781 i8k.right_fan_rpm = strtok(NULL, I8K_DELIM);
782 i8k.ac_status = strtok(NULL, I8K_DELIM);
783 i8k.buttons_status = strtok(NULL, I8K_DELIM);
786 /***********************************************************/
787 /***********************************************************/
788 /***********************************************************/
790 static int no_dots(const struct dirent *d)
792 if (d->d_name[0] == '.') {
798 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
800 struct dirent **namelist;
803 n = scandir(dir, &namelist, no_dots, alphasort);
806 NORM_ERR("scandir for %s: %s", dir, strerror(errno));
817 strncpy(s, namelist[0]->d_name, 255);
821 #pragma omp parallel for schedule(dynamic,10)
822 #endif /* HAVE_OPENMP */
823 for (i = 0; i < n; i++) {
832 int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
833 int *divisor, char *devtype)
841 memset(buf, 0, sizeof(buf));
843 /* if device is NULL or *, get first */
844 if (dev == NULL || strcmp(dev, "*") == 0) {
847 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
853 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
855 /* buf holds result from get_first_file_in_a_directory() above,
856 * e.g. "hwmon0" -- append "/device" */
857 strcat(buf, "/device");
859 /* dev holds device number N as a string,
860 * e.g. "0", -- convert to "hwmon0/device" */
861 sprintf(buf, "hwmon%s/device", dev);
866 /* At least the acpitz hwmon doesn't have a 'device' subdir,
867 * so check it's existence and strip it from buf otherwise. */
868 snprintf(path, 255, "%s%s", dir, dev);
869 if (stat(path, &st)) {
870 buf[strlen(buf) - 7] = 0;
873 /* change vol to in, tempf to temp */
874 if (strcmp(type, "vol") == 0) {
876 } else if (strcmp(type, "tempf") == 0) {
880 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
881 strncpy(devtype, path, 255);
884 fd = open(path, O_RDONLY);
886 CRIT_ERR(NULL, NULL, "can't open '%s': %s\nplease check your device or remove this "
887 "var from "PACKAGE_NAME, path, strerror(errno));
890 if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
891 || strcmp(type, "tempf") == 0) {
896 /* fan does not use *_div as a read divisor */
897 if (strcmp("fan", type) == 0) {
901 /* test if *_div file exist, open it and use it as divisor */
902 if (strcmp(type, "tempf") == 0) {
903 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
905 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
908 divfd = open(path, O_RDONLY);
914 divn = read(divfd, divbuf, 63);
915 /* should read until n == 0 but I doubt that kernel will give these
916 * in multiple pieces. :) */
918 NORM_ERR("open_sysfs_sensor(): can't read from sysfs");
921 *divisor = atoi(divbuf);
929 double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
937 lseek(*fd, 0, SEEK_SET);
943 n = read(*fd, buf, 63);
944 /* should read until n == 0 but I doubt that kernel will give these
945 * in multiple pieces. :) */
947 NORM_ERR("get_sysfs_info(): read from %s failed\n", devtype);
956 *fd = open(devtype, O_RDONLY);
958 NORM_ERR("can't open '%s': %s", devtype, strerror(errno));
961 /* My dirty hack for computing CPU value
962 * Filedil, from forums.gentoo.org */
963 /* if (strstr(devtype, "temp1_input") != NULL) {
964 return -15.096 + 1.4893 * (val / 1000.0);
967 /* divide voltage and temperature by 1000 */
968 /* or if any other divisor is given, use that */
969 if (strcmp(type, "tempf") == 0) {
971 return ((val / divisor + 40) * 9.0 / 5) - 40;
972 } else if (divisor) {
973 return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
975 return ((val + 40) * 9.0 / 5) - 40;
979 return val / divisor;
980 } else if (divisor) {
988 /* Prior to kernel version 2.6.12, the CPU fan speed was available in
989 * ADT746X_FAN_OLD, whereas later kernel versions provide this information in
991 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
992 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
994 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
997 char adt746x_fan_state[64];
1000 if (!p_client_buffer || client_buffer_size <= 0) {
1004 if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
1005 && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL) {
1006 sprintf(adt746x_fan_state, "adt746x not found");
1008 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
1009 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
1013 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_fan_state);
1016 /* Prior to kernel version 2.6.12, the CPU temperature was found in
1017 * ADT746X_CPU_OLD, whereas later kernel versions provide this information in
1019 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
1020 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
1022 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
1025 char adt746x_cpu_state[64];
1028 if (!p_client_buffer || client_buffer_size <= 0) {
1032 if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
1033 && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL) {
1034 sprintf(adt746x_cpu_state, "adt746x not found");
1036 fscanf(fp, "%2s", adt746x_cpu_state);
1040 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state);
1043 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1044 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1046 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1047 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1048 const char *p_format, int divisor, unsigned int cpu)
1056 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1062 char current_freq_file[128];
1064 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1066 f = fopen(current_freq_file, "r");
1068 /* if there's a cpufreq /sys node, read the current frequency from
1069 * this node and divide by 1000 to get Mhz. */
1070 if (fgets(s, sizeof(s), f)) {
1071 s[strlen(s) - 1] = '\0';
1072 freq = strtod(s, NULL);
1075 snprintf(p_client_buffer, client_buffer_size, p_format,
1076 (freq / 1000) / divisor);
1081 // open the CPU information file
1082 f = open_file("/proc/cpuinfo", &rep);
1084 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1089 while (fgets(s, sizeof(s), f) != NULL) {
1091 #if defined(__i386) || defined(__x86_64)
1092 // and search for the cpu mhz
1093 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1095 #if defined(__alpha)
1096 // different on alpha
1097 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1099 // this is different on ppc for some reason
1100 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1101 #endif // defined(__alpha)
1102 #endif // defined(__i386) || defined(__x86_64)
1104 // copy just the number
1105 strcpy(frequency, strchr(s, ':') + 2);
1106 #if defined(__alpha)
1108 frequency[strlen(frequency) - 6] = '\0';
1109 // kernel reports in Hz
1110 freq = strtod(frequency, NULL) / 1000000;
1113 frequency[strlen(frequency) - 1] = '\0';
1114 freq = strtod(frequency, NULL);
1118 if (strncmp(s, "processor", 9) == 0) {
1125 snprintf(p_client_buffer, client_buffer_size, p_format,
1126 (float) freq / divisor);
1130 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1132 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1142 * Peter Tarjan (ptarjan@citromail.hu) */
1144 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1145 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1146 const char *p_format, int divisor, unsigned int cpu)
1152 char current_freq_file[128];
1155 /* build the voltage file name */
1157 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1160 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1165 /* read the current cpu frequency from the /sys node */
1166 f = fopen(current_freq_file, "r");
1168 if (fgets(s, sizeof(s), f)) {
1169 s[strlen(s) - 1] = '\0';
1170 freq = strtod(s, NULL);
1174 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1175 perror("get_voltage()");
1182 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1185 /* use the current cpu frequency to find the corresponding voltage */
1186 f = fopen(current_freq_file, "r");
1192 if (fgets(line, 255, f) == NULL) {
1195 sscanf(line, "%d %d", &freq_comp, &voltage);
1196 if (freq_comp == freq) {
1202 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1203 perror("get_voltage()");
1209 snprintf(p_client_buffer, client_buffer_size, p_format,
1210 (float) voltage / divisor);
1214 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1216 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1223 if (!p_client_buffer || client_buffer_size <= 0) {
1227 /* yeah, slow... :/ */
1228 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1229 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1233 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1235 fp = open_file(buf2, &rep);
1237 snprintf(p_client_buffer, client_buffer_size,
1238 "can't open fan's state file");
1241 memset(buf, 0, sizeof(buf));
1242 fscanf(fp, "%*s %99s", buf);
1245 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1248 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1249 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1250 /* Linux 2.6.25 onwards ac adapter info is in
1251 /sys/class/power_supply/AC/
1252 On my system I get the following.
1253 /sys/class/power_supply/AC/uevent:
1254 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1257 POWER_SUPPLY_NAME=AC
1258 POWER_SUPPLY_TYPE=Mains
1259 POWER_SUPPLY_ONLINE=1
1262 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1270 if (!p_client_buffer || client_buffer_size <= 0) {
1274 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1275 fp = open_file(buf2, &rep);
1277 /* sysfs processing */
1279 if (fgets(buf, sizeof(buf), fp) == NULL)
1282 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1284 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1285 snprintf(p_client_buffer, client_buffer_size,
1286 "%s-line", (online ? "on" : "off"));
1292 /* yeah, slow... :/ */
1293 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1294 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1298 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1300 fp = open_file(buf2, &rep);
1302 snprintf(p_client_buffer, client_buffer_size,
1303 "No ac adapter found.... where is it?");
1306 memset(buf, 0, sizeof(buf));
1307 fscanf(fp, "%*s %99s", buf);
1310 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1315 /proc/acpi/thermal_zone/THRM/cooling_mode
1316 cooling mode: active
1317 /proc/acpi/thermal_zone/THRM/polling_frequency
1319 /proc/acpi/thermal_zone/THRM/state
1321 /proc/acpi/thermal_zone/THRM/temperature
1323 /proc/acpi/thermal_zone/THRM/trip_points
1325 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1328 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1329 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1331 int open_acpi_temperature(const char *name)
1337 if (name == NULL || strcmp(name, "*") == 0) {
1340 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1346 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1348 fd = open(path, O_RDONLY);
1350 NORM_ERR("can't open '%s': %s", path, strerror(errno));
1356 static double last_acpi_temp;
1357 static double last_acpi_temp_time;
1359 double get_acpi_temperature(int fd)
1365 /* don't update acpi temperature too often */
1366 if (current_update_time - last_acpi_temp_time < 11.32) {
1367 return last_acpi_temp;
1369 last_acpi_temp_time = current_update_time;
1371 /* seek to beginning */
1372 lseek(fd, 0, SEEK_SET);
1379 n = read(fd, buf, 255);
1381 NORM_ERR("can't read fd %d: %s", fd, strerror(errno));
1384 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1388 return last_acpi_temp;
1392 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1394 design capacity: 4400 mAh
1395 last full capacity: 4064 mAh
1396 battery technology: rechargeable
1397 design voltage: 14800 mV
1398 design capacity warning: 300 mAh
1399 design capacity low: 200 mAh
1400 capacity granularity 1: 32 mAh
1401 capacity granularity 2: 32 mAh
1403 serial number: 16922
1409 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1412 charging state: unknown
1414 remaining capacity: 4064 mAh
1415 present voltage: 16608 mV
1419 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1420 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1421 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1422 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1423 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1425 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1426 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1428 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1429 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1432 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1433 Linux 2.6.24 onwards battery info is in
1434 /sys/class/power_supply/BAT0/
1435 On my system I get the following.
1436 /sys/class/power_supply/BAT0/uevent:
1437 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1439 PHYSDEVDRIVER=battery
1440 POWER_SUPPLY_NAME=BAT0
1441 POWER_SUPPLY_TYPE=Battery
1442 POWER_SUPPLY_STATUS=Discharging
1443 POWER_SUPPLY_PRESENT=1
1444 POWER_SUPPLY_TECHNOLOGY=Li-ion
1445 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1446 POWER_SUPPLY_VOLTAGE_NOW=10780000
1447 POWER_SUPPLY_CURRENT_NOW=13970000
1448 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1449 POWER_SUPPLY_ENERGY_FULL=27370000
1450 POWER_SUPPLY_ENERGY_NOW=11810000
1451 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1452 POWER_SUPPLY_MANUFACTURER=Panasonic
1453 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1456 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1457 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1458 #define APM_PATH "/proc/apm"
1459 #define MAX_BATTERY_COUNT 4
1461 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1462 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1463 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1465 static int batteries_initialized = 0;
1466 static char batteries[MAX_BATTERY_COUNT][32];
1468 static int acpi_last_full[MAX_BATTERY_COUNT];
1469 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1471 /* e.g. "charging 75%" */
1472 static char last_battery_str[MAX_BATTERY_COUNT][64];
1474 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1476 static double last_battery_time[MAX_BATTERY_COUNT];
1478 static int last_battery_perct[MAX_BATTERY_COUNT];
1479 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1481 void init_batteries(void)
1485 if (batteries_initialized) {
1489 #pragma omp parallel for schedule(dynamic,10)
1490 #endif /* HAVE_OPENMP */
1491 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1492 batteries[idx][0] = '\0';
1494 batteries_initialized = 1;
1497 int get_battery_idx(const char *bat)
1501 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1502 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1507 /* if not found, enter a new entry */
1508 if (!strlen(batteries[idx])) {
1509 snprintf(batteries[idx], 31, "%s", bat);
1515 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1517 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1519 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1520 char acpi_path[128];
1521 char sysfs_path[128];
1523 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1524 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1528 idx = get_battery_idx(bat);
1530 /* don't update battery too often */
1531 if (current_update_time - last_battery_time[idx] < 29.5) {
1532 set_return_value(buffer, n, item, idx);
1536 last_battery_time[idx] = current_update_time;
1538 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1539 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1541 /* first try SYSFS if that fails try ACPI */
1543 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1544 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1547 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1548 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1551 if (sysfs_bat_fp[idx] != NULL) {
1553 int present_rate = -1;
1554 int remaining_capacity = -1;
1555 char charging_state[64];
1558 strcpy(charging_state, "unknown");
1560 while (!feof(sysfs_bat_fp[idx])) {
1562 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1565 /* let's just hope units are ok */
1566 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1567 strcpy(present, "yes");
1568 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1569 strcpy(present, "no");
1570 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1571 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1572 /* present_rate is not the same as the
1573 current flowing now but it is the same value
1574 which was used in the past. so we continue
1576 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1577 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1578 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1579 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1580 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1581 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1582 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1583 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1584 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1585 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1588 fclose(sysfs_bat_fp[idx]);
1589 sysfs_bat_fp[idx] = NULL;
1591 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1592 if (remaining_capacity > acpi_last_full[idx])
1593 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1596 if (strcmp(present, "No") == 0) {
1597 strncpy(last_battery_str[idx], "not present", 64);
1600 else if (strcmp(charging_state, "Charging") == 0) {
1601 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1602 /* e.g. charging 75% */
1603 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1604 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1606 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1607 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1608 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1609 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1610 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1611 snprintf(last_battery_time_str[idx],
1612 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1614 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1615 snprintf(last_battery_time_str[idx],
1616 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1620 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1621 if (present_rate > 0) {
1622 /* e.g. discharging 35% */
1623 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1624 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1626 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1627 (long) (((float) remaining_capacity / present_rate) * 3600));
1628 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1629 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1630 snprintf(last_battery_time_str[idx],
1631 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1633 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1635 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1636 snprintf(last_battery_time_str[idx],
1637 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1641 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1642 else if (strncmp(charging_state, "Charged", 64) == 0 || strncmp(charging_state, "Full", 64) == 0) {
1643 /* Below happens with the second battery on my X40,
1644 * when the second one is empty and the first one
1646 if (remaining_capacity == 0)
1647 strcpy(last_battery_str[idx], "empty");
1649 strcpy(last_battery_str[idx], "charged");
1651 /* unknown, probably full / AC */
1653 if (acpi_last_full[idx] != 0
1654 && remaining_capacity != acpi_last_full[idx])
1655 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1656 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1658 strncpy(last_battery_str[idx], "AC", 64);
1660 } else if (acpi_bat_fp[idx] != NULL) {
1662 int present_rate = -1;
1663 int remaining_capacity = -1;
1664 char charging_state[64];
1667 /* read last full capacity if it's zero */
1668 if (acpi_last_full[idx] == 0) {
1669 static int rep3 = 0;
1673 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1674 fp = open_file(path, &rep3);
1679 if (fgets(b, 256, fp) == NULL) {
1682 if (sscanf(b, "last full capacity: %d",
1683 &acpi_last_full[idx]) != 0) {
1692 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1694 strcpy(charging_state, "unknown");
1696 while (!feof(acpi_bat_fp[idx])) {
1699 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1703 /* let's just hope units are ok */
1704 if (strncmp(buf, "present:", 8) == 0) {
1705 sscanf(buf, "present: %4s", present);
1706 } else if (strncmp(buf, "charging state:", 15) == 0) {
1707 sscanf(buf, "charging state: %63s", charging_state);
1708 } else if (strncmp(buf, "present rate:", 13) == 0) {
1709 sscanf(buf, "present rate: %d", &present_rate);
1710 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1711 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1714 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1715 if (remaining_capacity > acpi_last_full[idx]) {
1716 /* normalize to 100% */
1717 acpi_last_full[idx] = remaining_capacity;
1721 if (strcmp(present, "no") == 0) {
1722 strncpy(last_battery_str[idx], "not present", 64);
1724 } else if (strcmp(charging_state, "charging") == 0) {
1725 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1726 /* e.g. charging 75% */
1727 snprintf(last_battery_str[idx],
1728 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1729 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1731 format_seconds(last_battery_time_str[idx],
1732 sizeof(last_battery_time_str[idx]) - 1,
1733 (long) (((acpi_last_full[idx] - remaining_capacity) *
1734 3600) / present_rate));
1735 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1736 snprintf(last_battery_str[idx],
1737 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1738 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1739 snprintf(last_battery_time_str[idx],
1740 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1742 strncpy(last_battery_str[idx], "charging",
1743 sizeof(last_battery_str[idx]) - 1);
1744 snprintf(last_battery_time_str[idx],
1745 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1748 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1749 if (present_rate > 0) {
1750 /* e.g. discharging 35% */
1751 snprintf(last_battery_str[idx],
1752 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1753 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1755 format_seconds(last_battery_time_str[idx],
1756 sizeof(last_battery_time_str[idx]) - 1,
1757 (long) ((remaining_capacity * 3600) / present_rate));
1758 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1759 snprintf(last_battery_str[idx],
1760 sizeof(last_battery_str[idx]) - 1, "full");
1761 snprintf(last_battery_time_str[idx],
1762 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1764 snprintf(last_battery_str[idx],
1765 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1766 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1767 snprintf(last_battery_time_str[idx],
1768 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1771 } else if (strncmp(charging_state, "charged", 64) == 0) {
1772 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1773 /* Below happens with the second battery on my X40,
1774 * when the second one is empty and the first one being charged. */
1775 if (remaining_capacity == 0) {
1776 strcpy(last_battery_str[idx], "empty");
1778 strcpy(last_battery_str[idx], "charged");
1780 /* unknown, probably full / AC */
1782 if (strncmp(charging_state, "Full", 64) == 0) {
1783 strncpy(last_battery_str[idx], "full", 64);
1784 } else if (acpi_last_full[idx] != 0
1785 && remaining_capacity != acpi_last_full[idx]) {
1786 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1787 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1789 strncpy(last_battery_str[idx], "AC", 64);
1792 fclose(acpi_bat_fp[idx]);
1793 acpi_bat_fp[idx] = NULL;
1796 if (apm_bat_fp[idx] == NULL) {
1797 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1800 if (apm_bat_fp[idx] != NULL) {
1801 unsigned int ac, status, flag;
1804 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1805 &ac, &status, &flag, &life);
1808 /* could check now that there is ac */
1809 snprintf(last_battery_str[idx], 64, "AC");
1811 /* could check that status == 3 here? */
1812 } else if (ac && life != 100) {
1813 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1815 snprintf(last_battery_str[idx], 64, "%d%%", life);
1818 /* it seemed to buffer it so file must be closed (or could use
1819 * syscalls directly but I don't feel like coding it now) */
1820 fclose(apm_bat_fp[idx]);
1821 apm_bat_fp[idx] = NULL;
1824 set_return_value(buffer, n, item, idx);
1827 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1830 case BATTERY_STATUS:
1831 snprintf(buffer, n, "%s", last_battery_str[idx]);
1834 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1841 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1843 get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1844 if (0 == strncmp("charging", buffer, 8)) {
1846 memmove(buffer + 1, buffer + 8, n - 8);
1847 } else if (0 == strncmp("discharging", buffer, 11)) {
1849 memmove(buffer + 1, buffer + 11, n - 11);
1850 } else if (0 == strncmp("charged", buffer, 7)) {
1852 memmove(buffer + 1, buffer + 7, n - 7);
1853 } else if (0 == strncmp("not present", buffer, 11)) {
1855 memmove(buffer + 1, buffer + 11, n - 11);
1856 } else if (0 == strncmp("empty", buffer, 5)) {
1858 memmove(buffer + 1, buffer + 5, n - 5);
1859 } else if (0 != strncmp("AC", buffer, 2)) {
1861 memmove(buffer + 1, buffer + 11, n - 11);
1865 int get_battery_perct(const char *bat)
1869 char acpi_path[128];
1870 char sysfs_path[128];
1871 int remaining_capacity = -1;
1873 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1874 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1878 idx = get_battery_idx(bat);
1880 /* don't update battery too often */
1881 if (current_update_time - last_battery_perct_time[idx] < 30) {
1882 return last_battery_perct[idx];
1884 last_battery_perct_time[idx] = current_update_time;
1886 /* Only check for SYSFS or ACPI */
1888 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1889 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1893 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1894 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1897 if (sysfs_bat_fp[idx] != NULL) {
1899 while (!feof(sysfs_bat_fp[idx])) {
1901 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1904 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1905 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1906 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1907 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1908 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1909 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1910 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1911 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1915 fclose(sysfs_bat_fp[idx]);
1916 sysfs_bat_fp[idx] = NULL;
1918 } else if (acpi_bat_fp[idx] != NULL) {
1920 /* read last full capacity if it's zero */
1921 if (acpi_design_capacity[idx] == 0) {
1926 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1927 fp = open_file(path, &rep2);
1932 if (fgets(b, 256, fp) == NULL) {
1935 if (sscanf(b, "last full capacity: %d",
1936 &acpi_design_capacity[idx]) != 0) {
1944 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1946 while (!feof(acpi_bat_fp[idx])) {
1949 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1953 if (buf[0] == 'r') {
1954 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1958 if (remaining_capacity < 0) {
1961 /* compute the battery percentage */
1962 last_battery_perct[idx] =
1963 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
1964 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
1965 return last_battery_perct[idx];
1968 int get_battery_perct_bar(const char *bar)
1972 get_battery_perct(bar);
1973 idx = get_battery_idx(bar);
1974 return (int) (last_battery_perct[idx] * 2.56 - 1);
1977 /* On Apple powerbook and ibook:
1978 $ cat /proc/pmu/battery_0
1985 $ cat /proc/pmu/info
1986 PMU driver version : 2
1987 PMU firmware version : 0c
1992 /* defines as in <linux/pmu.h> */
1993 #define PMU_BATT_PRESENT 0x00000001
1994 #define PMU_BATT_CHARGING 0x00000002
1996 static FILE *pmu_battery_fp;
1997 static FILE *pmu_info_fp;
1998 static char pb_battery_info[3][32];
1999 static double pb_battery_info_update;
2001 #define PMU_PATH "/proc/pmu"
2002 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2005 const char *batt_path = PMU_PATH "/battery_0";
2006 const char *info_path = PMU_PATH "/info";
2008 int charge, max_charge, ac = -1;
2011 /* don't update battery too often */
2012 if (current_update_time - pb_battery_info_update < 29.5) {
2013 snprintf(buffer, n, "%s", pb_battery_info[i]);
2016 pb_battery_info_update = current_update_time;
2018 if (pmu_battery_fp == NULL) {
2019 pmu_battery_fp = open_file(batt_path, &rep);
2020 if (pmu_battery_fp == NULL) {
2025 if (pmu_battery_fp != NULL) {
2026 rewind(pmu_battery_fp);
2027 while (!feof(pmu_battery_fp)) {
2030 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2034 if (buf[0] == 'f') {
2035 sscanf(buf, "flags : %8x", &flags);
2036 } else if (buf[0] == 'c' && buf[1] == 'h') {
2037 sscanf(buf, "charge : %d", &charge);
2038 } else if (buf[0] == 'm') {
2039 sscanf(buf, "max_charge : %d", &max_charge);
2040 } else if (buf[0] == 't') {
2041 sscanf(buf, "time rem. : %ld", &timeval);
2045 if (pmu_info_fp == NULL) {
2046 pmu_info_fp = open_file(info_path, &rep);
2047 if (pmu_info_fp == NULL) {
2052 if (pmu_info_fp != NULL) {
2053 rewind(pmu_info_fp);
2054 while (!feof(pmu_info_fp)) {
2057 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2060 if (buf[0] == 'A') {
2061 sscanf(buf, "AC Power : %d", &ac);
2065 /* update status string */
2066 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2067 strncpy(pb_battery_info[PB_BATT_STATUS], "AC", sizeof(pb_battery_info[PB_BATT_STATUS]));
2068 } else if (ac && (flags & PMU_BATT_PRESENT)
2069 && !(flags & PMU_BATT_CHARGING)) {
2070 strncpy(pb_battery_info[PB_BATT_STATUS], "charged", sizeof(pb_battery_info[PB_BATT_STATUS]));
2071 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2072 strncpy(pb_battery_info[PB_BATT_STATUS], "charging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2074 strncpy(pb_battery_info[PB_BATT_STATUS], "discharging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2077 /* update percentage string */
2078 if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT)
2079 && !(flags & PMU_BATT_CHARGING)) {
2080 snprintf(pb_battery_info[PB_BATT_PERCENT],
2081 sizeof(pb_battery_info[PB_BATT_PERCENT]), "100%%");
2082 } else if (timeval == 0) {
2083 snprintf(pb_battery_info[PB_BATT_PERCENT],
2084 sizeof(pb_battery_info[PB_BATT_PERCENT]), "unknown");
2086 snprintf(pb_battery_info[PB_BATT_PERCENT],
2087 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2088 (charge * 100) / max_charge);
2091 /* update time string */
2092 if (timeval == 0) { /* fully charged or battery not present */
2093 snprintf(pb_battery_info[PB_BATT_TIME],
2094 sizeof(pb_battery_info[PB_BATT_TIME]), "unknown");
2095 } else if (timeval < 60 * 60) { /* don't show secs */
2096 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2097 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2099 format_seconds(pb_battery_info[PB_BATT_TIME],
2100 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2103 snprintf(buffer, n, "%s", pb_battery_info[i]);
2106 void update_top(void)
2108 process_find_top(info.cpu, info.memu, info.time
2113 info.first_process = get_first_process();
2116 void update_entropy(void)
2119 const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2120 const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2123 info.entropy.entropy_avail = 0;
2124 info.entropy.poolsize = 0;
2126 if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2130 if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2135 fscanf(fp1, "%u", &info.entropy.entropy_avail);
2136 fscanf(fp2, "%u", &info.entropy.poolsize);
2142 const char *get_disk_protect_queue(const char *disk)
2148 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2149 if (access(path, F_OK)) {
2150 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2152 if ((fp = fopen(path, "r")) == NULL)
2154 if (fscanf(fp, "%d\n", &state) != 1) {
2159 return (state > 0) ? "frozen" : "free ";
2162 void update_diskio(void)
2166 char buf[512], devbuf[64];
2167 unsigned int major, minor;
2169 struct diskio_stat *cur;
2170 unsigned int reads, writes;
2171 unsigned int total_reads = 0, total_writes = 0;
2174 stats.current_read = 0;
2175 stats.current_write = 0;
2177 if (!(fp = open_file("/proc/diskstats", &rep))) {
2181 /* read reads and writes from all disks (minor = 0), including cd-roms
2182 * and floppies, and sum them up */
2183 while (fgets(buf, 512, fp)) {
2184 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
2185 &minor, devbuf, &reads, &writes);
2186 /* ignore subdevices (they have only 3 matching entries in their line)
2187 * and virtual devices (LVM, network block devices, RAM disks, Loopback)
2189 * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
2190 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
2191 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
2192 total_reads += reads;
2193 total_writes += writes;
2195 col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
2196 &major, &minor, devbuf, &reads, &writes);
2197 if (col_count != 5) {
2202 while (cur && strcmp(devbuf, cur->dev))
2206 update_diskio_values(cur, reads, writes);
2208 update_diskio_values(&stats, total_reads, total_writes);