1 /* Conky, a system monitor, based on torsmo
3 * Any original torsmo code is licensed under the BSD license
5 * All code written since the fork of torsmo is licensed under the GPL
7 * Please see COPYING for details
9 * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
10 * Copyright (c) 2007 Toni Spets
11 * Copyright (c) 2005-2008 Brenden Matthews, Philip Kovacs, et. al.
13 * All rights reserved.
15 * This program is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation, either version 3 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program. If not, see <http://www.gnu.org/licenses/>.
37 #include <sys/types.h>
38 #include <sys/sysinfo.h>
40 #ifndef HAVE_CLOCK_GETTIME
45 // #include <assert.h>
49 #include <sys/ioctl.h>
50 #include <sys/socket.h>
51 #include <netinet/in.h>
52 #include <linux/sockios.h>
54 #include <arpa/inet.h>
58 #include <linux/route.h>
65 #define SHORTSTAT_TEMPL "%*s %llu %llu %llu"
66 #define LONGSTAT_TEMPL "%*s %llu %llu %llu "
68 static int show_nice_processes;
70 /* This flag tells the linux routines to use the /proc system where possible,
71 * even if other api's are available, e.g. sysinfo() or getloadavg().
72 * the reason for this is to allow for /proc-based distributed monitoring.
73 * using a flag in this manner creates less confusing code. */
74 static int prefer_proc = 0;
76 void prepare_update(void)
80 void update_uptime(void)
84 struct sysinfo s_info;
87 info.uptime = (double) s_info.uptime;
94 if (!(fp = open_file("/proc/uptime", &rep))) {
98 fscanf(fp, "%lf", &info.uptime);
101 info.mask |= (1 << INFO_UPTIME);
104 int check_mount(char *s)
107 FILE *mtab = fopen("/etc/mtab", "r");
110 char buf1[256], buf2[128];
112 while (fgets(buf1, 256, mtab)) {
113 sscanf(buf1, "%*s %128s", buf2);
114 if (!strcmp(s, buf2)) {
121 ERR("Could not open mtab");
126 /* these things are also in sysinfo except Buffers:
127 * (that's why I'm reading them from proc) */
129 void update_meminfo(void)
134 /* unsigned int a; */
137 info.mem = info.memmax = info.swap = info.swapmax = info.bufmem =
138 info.buffers = info.cached = info.memfree = info.memeasyfree = 0;
140 if (!(meminfo_fp = open_file("/proc/meminfo", &rep))) {
144 while (!feof(meminfo_fp)) {
145 if (fgets(buf, 255, meminfo_fp) == NULL) {
149 if (strncmp(buf, "MemTotal:", 9) == 0) {
150 sscanf(buf, "%*s %llu", &info.memmax);
151 } else if (strncmp(buf, "MemFree:", 8) == 0) {
152 sscanf(buf, "%*s %llu", &info.memfree);
153 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
154 sscanf(buf, "%*s %llu", &info.swapmax);
155 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
156 sscanf(buf, "%*s %llu", &info.swap);
157 } else if (strncmp(buf, "Buffers:", 8) == 0) {
158 sscanf(buf, "%*s %llu", &info.buffers);
159 } else if (strncmp(buf, "Cached:", 7) == 0) {
160 sscanf(buf, "%*s %llu", &info.cached);
164 info.mem = info.memmax - info.memfree;
165 info.memeasyfree = info.memfree;
166 info.swap = info.swapmax - info.swap;
168 info.bufmem = info.cached + info.buffers;
170 info.mask |= (1 << INFO_MEM) | (1 << INFO_BUFFERS);
175 int get_laptop_mode(void)
180 if ((fp = fopen("/proc/sys/vm/laptop_mode", "r")) != NULL)
181 fscanf(fp, "%d\n", &val);
187 * # cat /sys/block/sda/queue/scheduler
188 * noop [anticipatory] cfq
190 char *get_ioscheduler(char *disk)
196 return strndup("n/a", text_buffer_size);
198 snprintf(buf, 127, "/sys/block/%s/queue/scheduler", disk);
199 if ((fp = fopen(buf, "r")) == NULL) {
200 return strndup("n/a", text_buffer_size);
203 fscanf(fp, "%127s", buf);
205 buf[strlen(buf) - 1] = '\0';
207 return strndup(buf + 1, text_buffer_size);
211 return strndup("n/a", text_buffer_size);
214 int interface_up(const char *dev)
219 if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
220 CRIT_ERR("could not create sockfd");
223 strncpy(ifr.ifr_name, dev, IFNAMSIZ);
224 if (ioctl(fd, SIOCGIFFLAGS, &ifr)) {
225 /* if device does not exist, treat like not up */
227 perror("SIOCGIFFLAGS");
231 if (!(ifr.ifr_flags & IFF_UP)) /* iface is not up */
233 if (ifup_strictness == IFUP_UP)
236 if (!(ifr.ifr_flags & IFF_RUNNING))
238 if (ifup_strictness == IFUP_LINK)
241 if (ioctl(fd, SIOCGIFADDR, &ifr)) {
242 perror("SIOCGIFADDR");
245 if (((struct sockaddr_in *)&(ifr.ifr_ifru.ifru_addr))->sin_addr.s_addr)
256 #define COND_FREE(x) if(x) free(x); x = 0
257 #define SAVE_SET_STRING(x, y) \
258 if (x && strcmp((char *)x, (char *)y)) { \
260 x = strndup("multiple", text_buffer_size); \
262 x = strndup(y, text_buffer_size); \
265 void update_gateway_info_failure(const char *reason)
270 //2 pointers to 1 location causes a crash when we try to free them both
271 info.gw_info.iface = strndup("failed", text_buffer_size);
272 info.gw_info.ip = strndup("failed", text_buffer_size);
276 /* Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT */
277 #define RT_ENTRY_FORMAT "%63s %lx %lx %x %*d %*d %*d %lx %*d %*d %*d\n"
279 void update_gateway_info(void)
284 unsigned long dest, gate, mask;
287 struct gateway_info *gw_info = &info.gw_info;
289 COND_FREE(gw_info->iface);
290 COND_FREE(gw_info->ip);
293 if ((fp = fopen("/proc/net/route", "r")) == NULL) {
294 update_gateway_info_failure("fopen()");
298 /* skip over the table header line, which is always present */
299 fscanf(fp, "%*[^\n]\n");
302 if(fscanf(fp, RT_ENTRY_FORMAT,
303 iface, &dest, &gate, &flags, &mask) != 5) {
304 update_gateway_info_failure("fscanf()");
307 if (!(dest || mask) && ((flags & RTF_GATEWAY) || !gate) ) {
309 SAVE_SET_STRING(gw_info->iface, iface)
311 SAVE_SET_STRING(gw_info->ip, inet_ntoa(ina))
318 void update_net_stats(void)
323 // FIXME: arbitrary size chosen to keep code simple.
325 unsigned int curtmp1, curtmp2;
332 // wireless info variables
333 int skfd, has_bitrate = 0;
334 struct wireless_info *winfo;
339 delta = current_update_time - last_update_time;
340 if (delta <= 0.0001) {
344 /* open file and ignore first two lines */
345 if (!(net_dev_fp = open_file("/proc/net/dev", &rep))) {
350 fgets(buf, 255, net_dev_fp); /* garbage */
351 fgets(buf, 255, net_dev_fp); /* garbage (field names) */
353 /* read each interface */
354 for (i2 = 0; i2 < 16; i2++) {
358 long long r, t, last_recv, last_trans;
360 if (fgets(buf, 255, net_dev_fp) == NULL) {
364 while (isspace((int) *p)) {
370 while (*p && *p != ':') {
379 ns = get_net_stat(s);
381 memset(&(ns->addr.sa_data), 0, 14);
383 if(NULL == ns->addrs)
384 ns->addrs = (char*) malloc(17 * 16 + 1);
385 if(NULL != ns->addrs)
386 memset(ns->addrs, 0, 17 * 16 + 1); /* Up to 17 chars per ip, max 16 interfaces. Nasty memory usage... */
388 last_recv = ns->recv;
389 last_trans = ns->trans;
391 /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
392 sscanf(p, "%lld %*d %*d %*d %*d %*d %*d %*d %lld",
395 /* if recv or trans is less than last time, an overflow happened */
396 if (r < ns->last_read_recv) {
399 ns->recv += (r - ns->last_read_recv);
401 ns->last_read_recv = r;
403 if (t < ns->last_read_trans) {
406 ns->trans += (t - ns->last_read_trans);
408 ns->last_read_trans = t;
410 /*** ip addr patch ***/
411 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
413 conf.ifc_buf = malloc(sizeof(struct ifreq) * 16);
414 conf.ifc_len = sizeof(struct ifreq) * 16;
415 memset(conf.ifc_buf, 0, conf.ifc_len);
417 ioctl((long) i, SIOCGIFCONF, &conf);
419 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
420 struct net_stat *ns2;
422 if (!(((struct ifreq *) conf.ifc_buf) + k))
426 ((struct ifreq *) conf.ifc_buf)[k].ifr_ifrn.ifrn_name);
427 ns2->addr = ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.ifru_addr;
428 if(NULL != ns2->addrs) {
429 sprintf(temp_addr, "%u.%u.%u.%u, ",
430 ns2->addr.sa_data[2] & 255,
431 ns2->addr.sa_data[3] & 255,
432 ns2->addr.sa_data[4] & 255,
433 ns2->addr.sa_data[5] & 255);
434 if(NULL == strstr(ns2->addrs, temp_addr))
435 strncpy(ns2->addrs + strlen(ns2->addrs), temp_addr, 17);
443 /*** end ip addr patch ***/
445 /* calculate speeds */
446 ns->net_rec[0] = (ns->recv - last_recv) / delta;
447 ns->net_trans[0] = (ns->trans - last_trans) / delta;
451 for (i = 0; (unsigned) i < info.net_avg_samples; i++) {
452 curtmp1 += ns->net_rec[i];
453 curtmp2 += ns->net_trans[i];
461 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
462 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
463 if (info.net_avg_samples > 1) {
464 for (i = info.net_avg_samples; i > 1; i--) {
465 ns->net_rec[i - 1] = ns->net_rec[i - 2];
466 ns->net_trans[i - 1] = ns->net_trans[i - 2];
471 /* update wireless info */
472 winfo = malloc(sizeof(struct wireless_info));
473 memset(winfo, 0, sizeof(struct wireless_info));
475 skfd = iw_sockets_open();
476 if (iw_get_basic_config(skfd, s, &(winfo->b)) > -1) {
478 // set present winfo variables
479 if (iw_get_stats(skfd, s, &(winfo->stats),
480 &winfo->range, winfo->has_range) >= 0) {
481 winfo->has_stats = 1;
483 if (iw_get_range_info(skfd, s, &(winfo->range)) >= 0) {
484 winfo->has_range = 1;
486 if (iw_get_ext(skfd, s, SIOCGIWAP, &wrq) >= 0) {
487 winfo->has_ap_addr = 1;
488 memcpy(&(winfo->ap_addr), &(wrq.u.ap_addr), sizeof(sockaddr));
492 if (iw_get_ext(skfd, s, SIOCGIWRATE, &wrq) >= 0) {
493 memcpy(&(winfo->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
494 iw_print_bitrate(ns->bitrate, 16, winfo->bitrate.value);
499 if (winfo->has_range && winfo->has_stats
500 && ((winfo->stats.qual.level != 0)
501 || (winfo->stats.qual.updated & IW_QUAL_DBM))) {
502 if (!(winfo->stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
503 ns->link_qual = winfo->stats.qual.qual;
504 ns->link_qual_max = winfo->range.max_qual.qual;
509 if (winfo->has_ap_addr) {
510 iw_sawap_ntop(&winfo->ap_addr, ns->ap);
514 if (winfo->b.has_essid) {
515 if (winfo->b.essid_on) {
516 snprintf(ns->essid, 32, "%s", winfo->b.essid);
518 snprintf(ns->essid, 32, "off/any");
522 snprintf(ns->mode, 16, "%s", iw_operation_mode[winfo->b.mode]);
524 iw_sockets_close(skfd);
531 info.mask |= (1 << INFO_NET);
536 void update_total_processes(void)
540 struct sysinfo s_info;
543 info.procs = s_info.procs;
550 if (!(fp = open_file("/proc/loadavg", &rep))) {
554 fscanf(fp, "%*f %*f %*f %*d/%hu", &info.procs);
557 info.mask |= (1 << INFO_PROCS);
560 #define CPU_SAMPLE_COUNT 15
562 unsigned long long cpu_user;
563 unsigned long long cpu_system;
564 unsigned long long cpu_nice;
565 unsigned long long cpu_idle;
566 unsigned long long cpu_iowait;
567 unsigned long long cpu_irq;
568 unsigned long long cpu_softirq;
569 unsigned long long cpu_steal;
570 unsigned long long cpu_total;
571 unsigned long long cpu_active_total;
572 unsigned long long cpu_last_total;
573 unsigned long long cpu_last_active_total;
574 double cpu_val[CPU_SAMPLE_COUNT];
576 static short cpu_setup = 0;
578 /* Determine if this kernel gives us "extended" statistics information in
580 * Kernels around 2.5 and earlier only reported user, system, nice, and
581 * idle values in proc stat.
582 * Kernels around 2.6 and greater report these PLUS iowait, irq, softirq,
584 void determine_longstat(char *buf)
586 unsigned long long iowait = 0;
588 KFLAG_SETOFF(KFLAG_IS_LONGSTAT);
589 /* scanf will either return -1 or 1 because there is only 1 assignment */
590 if (sscanf(buf, "%*s %*d %*d %*d %*d %llu", &iowait) > 0) {
591 KFLAG_SETON(KFLAG_IS_LONGSTAT);
595 void get_cpu_count(void)
601 if (info.cpu_usage) {
605 if (!(stat_fp = open_file("/proc/stat", &rep))) {
611 while (!feof(stat_fp)) {
612 if (fgets(buf, 255, stat_fp) == NULL) {
616 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
617 if (info.cpu_count == 0) {
618 determine_longstat(buf);
623 info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
628 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
629 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
631 inline static void update_stat(void)
635 static struct cpu_info *cpu = NULL;
640 const char *stat_template = NULL;
641 unsigned int malloc_cpu_size = 0;
643 /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
644 if (!cpu_setup || !info.cpu_usage) {
649 if (!stat_template) {
651 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
655 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
656 cpu = malloc(malloc_cpu_size);
657 memset(cpu, 0, malloc_cpu_size);
660 if (!(stat_fp = open_file("/proc/stat", &rep))) {
662 if (info.cpu_usage) {
663 memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
669 while (!feof(stat_fp)) {
670 if (fgets(buf, 255, stat_fp) == NULL) {
674 if (strncmp(buf, "procs_running ", 14) == 0) {
675 sscanf(buf, "%*s %hu", &info.run_procs);
676 info.mask |= (1 << INFO_RUN_PROCS);
677 } else if (strncmp(buf, "cpu", 3) == 0) {
679 if (isdigit(buf[3])) {
680 idx = atoi(&buf[3]) + 1;
684 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
685 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
686 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
687 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
688 &(cpu[idx].cpu_steal));
690 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
691 cpu[idx].cpu_system + cpu[idx].cpu_idle +
692 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
693 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
695 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
696 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
697 info.mask |= (1 << INFO_CPU);
699 delta = current_update_time - last_update_time;
701 if (delta <= 0.001) {
705 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
706 cpu[idx].cpu_last_active_total) /
707 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
709 for (i = 0; i < info.cpu_avg_samples; i++) {
710 curtmp += cpu[idx].cpu_val[i];
712 /* TESTING -- I've removed this, because I don't think it is right.
713 * You shouldn't divide by the cpu count here ...
714 * removing for testing */
716 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
719 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
721 /* TESTING -- this line replaces the prev. "suspect" if/else */
722 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
724 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
725 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
726 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
727 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
734 void update_running_processes(void)
739 void update_cpu_usage(void)
744 void update_load_average(void)
746 #ifdef HAVE_GETLOADAVG
751 info.loadavg[0] = (float) v[0];
752 info.loadavg[1] = (float) v[1];
753 info.loadavg[2] = (float) v[2];
760 if (!(fp = open_file("/proc/loadavg", &rep))) {
761 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
764 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
768 info.mask |= (1 << INFO_LOADAVG);
771 #define PROC_I8K "/proc/i8k"
772 #define I8K_DELIM " "
773 static char *i8k_procbuf = NULL;
774 void update_i8k(void)
779 i8k_procbuf = (char *) malloc(128 * sizeof(char));
781 if ((fp = fopen(PROC_I8K, "r")) == NULL) {
782 CRIT_ERR("/proc/i8k doesn't exist! use insmod to make sure the kernel "
783 "driver is loaded...");
786 memset(&i8k_procbuf[0], 0, 128);
787 if (fread(&i8k_procbuf[0], sizeof(char), 128, fp) == 0) {
788 ERR("something wrong with /proc/i8k...");
793 i8k.version = strtok(&i8k_procbuf[0], I8K_DELIM);
794 i8k.bios = strtok(NULL, I8K_DELIM);
795 i8k.serial = strtok(NULL, I8K_DELIM);
796 i8k.cpu_temp = strtok(NULL, I8K_DELIM);
797 i8k.left_fan_status = strtok(NULL, I8K_DELIM);
798 i8k.right_fan_status = strtok(NULL, I8K_DELIM);
799 i8k.left_fan_rpm = strtok(NULL, I8K_DELIM);
800 i8k.right_fan_rpm = strtok(NULL, I8K_DELIM);
801 i8k.ac_status = strtok(NULL, I8K_DELIM);
802 i8k.buttons_status = strtok(NULL, I8K_DELIM);
805 /***********************************************************/
806 /***********************************************************/
807 /***********************************************************/
809 static int no_dots(const struct dirent *d)
811 if (d->d_name[0] == '.') {
817 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
819 struct dirent **namelist;
822 n = scandir(dir, &namelist, no_dots, alphasort);
825 ERR("scandir for %s: %s", dir, strerror(errno));
836 strncpy(s, namelist[0]->d_name, 255);
839 for (i = 0; i < n; i++) {
848 int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
849 int *divisor, char *devtype)
856 memset(buf, 0, sizeof(buf));
858 /* if device is NULL or *, get first */
859 if (dev == NULL || strcmp(dev, "*") == 0) {
862 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
868 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
870 /* buf holds result from get_first_file_in_a_directory() above,
871 * e.g. "hwmon0" -- append "/device" */
872 strcat(buf, "/device");
874 /* dev holds device number N as a string,
875 * e.g. "0", -- convert to "hwmon0/device" */
876 sprintf(buf, "hwmon%s/device", dev);
881 /* change vol to in */
882 if (strcmp(type, "vol") == 0) {
886 if (strcmp(type, "tempf") == 0) {
887 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, "temp", n);
889 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
891 strncpy(devtype, path, 255);
894 fd = open(path, O_RDONLY);
896 CRIT_ERR("can't open '%s': %s\nplease check your device or remove this "
897 "var from "PACKAGE_NAME, path, strerror(errno));
900 if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
901 || strcmp(type, "tempf") == 0) {
906 /* fan does not use *_div as a read divisor */
907 if (strcmp("fan", type) == 0) {
911 /* test if *_div file exist, open it and use it as divisor */
912 if (strcmp(type, "tempf") == 0) {
913 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
915 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
918 divfd = open(path, O_RDONLY);
924 divn = read(divfd, divbuf, 63);
925 /* should read until n == 0 but I doubt that kernel will give these
926 * in multiple pieces. :) */
928 ERR("open_sysfs_sensor(): can't read from sysfs");
931 *divisor = atoi(divbuf);
940 double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
948 lseek(*fd, 0, SEEK_SET);
954 n = read(*fd, buf, 63);
955 /* should read until n == 0 but I doubt that kernel will give these
956 * in multiple pieces. :) */
958 ERR("get_sysfs_info(): read from %s failed\n", devtype);
967 *fd = open(devtype, O_RDONLY);
969 ERR("can't open '%s': %s", devtype, strerror(errno));
972 /* My dirty hack for computing CPU value
973 * Filedil, from forums.gentoo.org */
974 /* if (strstr(devtype, "temp1_input") != NULL) {
975 return -15.096 + 1.4893 * (val / 1000.0);
978 /* divide voltage and temperature by 1000 */
979 /* or if any other divisor is given, use that */
980 if (strcmp(type, "tempf") == 0) {
982 return ((val / divisor + 40) * 9.0 / 5) - 40;
983 } else if (divisor) {
984 return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
986 return ((val + 40) * 9.0 / 5) - 40;
990 return val / divisor;
991 } else if (divisor) {
999 /* Prior to kernel version 2.6.12, the CPU fan speed was available in
1000 * ADT746X_FAN_OLD, whereas later kernel versions provide this information in
1002 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
1003 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
1005 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
1008 char adt746x_fan_state[64];
1011 if (!p_client_buffer || client_buffer_size <= 0) {
1015 if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
1016 && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL) {
1017 sprintf(adt746x_fan_state, "adt746x not found");
1019 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
1020 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
1024 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_fan_state);
1027 /* Prior to kernel version 2.6.12, the CPU temperature was found in
1028 * ADT746X_CPU_OLD, whereas later kernel versions provide this information in
1030 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
1031 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
1033 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
1036 char adt746x_cpu_state[64];
1039 if (!p_client_buffer || client_buffer_size <= 0) {
1043 if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
1044 && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL) {
1045 sprintf(adt746x_cpu_state, "adt746x not found");
1047 fscanf(fp, "%2s", adt746x_cpu_state);
1051 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state);
1054 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1055 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1057 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1058 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1059 const char *p_format, int divisor, unsigned int cpu)
1067 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1073 char current_freq_file[128];
1075 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1077 f = fopen(current_freq_file, "r");
1079 /* if there's a cpufreq /sys node, read the current frequency from
1080 * this node and divide by 1000 to get Mhz. */
1081 if (fgets(s, sizeof(s), f)) {
1082 s[strlen(s) - 1] = '\0';
1083 freq = strtod(s, NULL);
1086 snprintf(p_client_buffer, client_buffer_size, p_format,
1087 (freq / 1000) / divisor);
1092 // open the CPU information file
1093 f = open_file("/proc/cpuinfo", &rep);
1095 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1100 while (fgets(s, sizeof(s), f) != NULL) {
1102 #if defined(__i386) || defined(__x86_64)
1103 // and search for the cpu mhz
1104 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1106 #if defined(__alpha)
1107 // different on alpha
1108 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1110 // this is different on ppc for some reason
1111 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1112 #endif // defined(__alpha)
1113 #endif // defined(__i386) || defined(__x86_64)
1115 // copy just the number
1116 strcpy(frequency, strchr(s, ':') + 2);
1117 #if defined(__alpha)
1119 frequency[strlen(frequency) - 6] = '\0';
1120 // kernel reports in Hz
1121 freq = strtod(frequency, NULL) / 1000000;
1124 frequency[strlen(frequency) - 1] = '\0';
1125 freq = strtod(frequency, NULL);
1129 if (strncmp(s, "processor", 9) == 0) {
1136 snprintf(p_client_buffer, client_buffer_size, p_format,
1137 (float) freq / divisor);
1141 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1143 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1153 * Peter Tarjan (ptarjan@citromail.hu) */
1155 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1156 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1157 const char *p_format, int divisor, unsigned int cpu)
1163 char current_freq_file[128];
1166 /* build the voltage file name */
1168 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1171 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1176 /* read the current cpu frequency from the /sys node */
1177 f = fopen(current_freq_file, "r");
1179 if (fgets(s, sizeof(s), f)) {
1180 s[strlen(s) - 1] = '\0';
1181 freq = strtod(s, NULL);
1185 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1186 perror("get_voltage()");
1193 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1196 /* use the current cpu frequency to find the corresponding voltage */
1197 f = fopen(current_freq_file, "r");
1203 if (fgets(line, 255, f) == NULL) {
1206 sscanf(line, "%d %d", &freq_comp, &voltage);
1207 if (freq_comp == freq) {
1213 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1214 perror("get_voltage()");
1220 snprintf(p_client_buffer, client_buffer_size, p_format,
1221 (float) voltage / divisor);
1225 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1227 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1234 if (!p_client_buffer || client_buffer_size <= 0) {
1238 /* yeah, slow... :/ */
1239 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1240 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1244 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1246 fp = open_file(buf2, &rep);
1248 snprintf(p_client_buffer, client_buffer_size,
1249 "can't open fan's state file");
1252 memset(buf, 0, sizeof(buf));
1253 fscanf(fp, "%*s %99s", buf);
1256 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1259 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1260 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1261 /* Linux 2.6.25 onwards ac adapter info is in
1262 /sys/class/power_supply/AC/
1263 On my system I get the following.
1264 /sys/class/power_supply/AC/uevent:
1265 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1268 POWER_SUPPLY_NAME=AC
1269 POWER_SUPPLY_TYPE=Mains
1270 POWER_SUPPLY_ONLINE=1
1273 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1281 if (!p_client_buffer || client_buffer_size <= 0) {
1285 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1286 fp = open_file(buf2, &rep);
1288 /* sysfs processing */
1290 if (fgets(buf, sizeof(buf), fp) == NULL)
1293 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1295 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1296 snprintf(p_client_buffer, client_buffer_size,
1297 "%s-line", (online ? "on" : "off"));
1303 /* yeah, slow... :/ */
1304 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1305 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1309 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1311 fp = open_file(buf2, &rep);
1313 snprintf(p_client_buffer, client_buffer_size,
1314 "No ac adapter found.... where is it?");
1317 memset(buf, 0, sizeof(buf));
1318 fscanf(fp, "%*s %99s", buf);
1321 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1326 /proc/acpi/thermal_zone/THRM/cooling_mode
1327 cooling mode: active
1328 /proc/acpi/thermal_zone/THRM/polling_frequency
1330 /proc/acpi/thermal_zone/THRM/state
1332 /proc/acpi/thermal_zone/THRM/temperature
1334 /proc/acpi/thermal_zone/THRM/trip_points
1336 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1339 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1340 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1342 int open_acpi_temperature(const char *name)
1348 if (name == NULL || strcmp(name, "*") == 0) {
1351 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1357 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1359 fd = open(path, O_RDONLY);
1361 ERR("can't open '%s': %s", path, strerror(errno));
1367 static double last_acpi_temp;
1368 static double last_acpi_temp_time;
1370 double get_acpi_temperature(int fd)
1376 /* don't update acpi temperature too often */
1377 if (current_update_time - last_acpi_temp_time < 11.32) {
1378 return last_acpi_temp;
1380 last_acpi_temp_time = current_update_time;
1382 /* seek to beginning */
1383 lseek(fd, 0, SEEK_SET);
1390 n = read(fd, buf, 255);
1392 ERR("can't read fd %d: %s", fd, strerror(errno));
1395 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1399 return last_acpi_temp;
1403 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1405 design capacity: 4400 mAh
1406 last full capacity: 4064 mAh
1407 battery technology: rechargeable
1408 design voltage: 14800 mV
1409 design capacity warning: 300 mAh
1410 design capacity low: 200 mAh
1411 capacity granularity 1: 32 mAh
1412 capacity granularity 2: 32 mAh
1414 serial number: 16922
1420 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1423 charging state: unknown
1425 remaining capacity: 4064 mAh
1426 present voltage: 16608 mV
1430 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1431 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1432 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1433 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1434 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1436 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1437 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1439 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1440 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1443 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1444 Linux 2.6.24 onwards battery info is in
1445 /sys/class/power_supply/BAT0/
1446 On my system I get the following.
1447 /sys/class/power_supply/BAT0/uevent:
1448 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1450 PHYSDEVDRIVER=battery
1451 POWER_SUPPLY_NAME=BAT0
1452 POWER_SUPPLY_TYPE=Battery
1453 POWER_SUPPLY_STATUS=Discharging
1454 POWER_SUPPLY_PRESENT=1
1455 POWER_SUPPLY_TECHNOLOGY=Li-ion
1456 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1457 POWER_SUPPLY_VOLTAGE_NOW=10780000
1458 POWER_SUPPLY_CURRENT_NOW=13970000
1459 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1460 POWER_SUPPLY_ENERGY_FULL=27370000
1461 POWER_SUPPLY_ENERGY_NOW=11810000
1462 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1463 POWER_SUPPLY_MANUFACTURER=Panasonic
1464 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1467 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1468 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1469 #define APM_PATH "/proc/apm"
1470 #define MAX_BATTERY_COUNT 4
1472 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1473 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1474 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1476 static int batteries_initialized = 0;
1477 static char batteries[MAX_BATTERY_COUNT][32];
1479 static int acpi_last_full[MAX_BATTERY_COUNT];
1480 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1482 /* e.g. "charging 75%" */
1483 static char last_battery_str[MAX_BATTERY_COUNT][64];
1485 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1487 static double last_battery_time[MAX_BATTERY_COUNT];
1489 static int last_battery_perct[MAX_BATTERY_COUNT];
1490 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1492 void init_batteries(void)
1496 if (batteries_initialized) {
1499 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1500 batteries[idx][0] = '\0';
1502 batteries_initialized = 1;
1505 int get_battery_idx(const char *bat)
1509 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1510 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1515 /* if not found, enter a new entry */
1516 if (!strlen(batteries[idx])) {
1517 snprintf(batteries[idx], 31, "%s", bat);
1523 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1525 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1527 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1528 char acpi_path[128];
1529 char sysfs_path[128];
1531 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1532 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1536 idx = get_battery_idx(bat);
1538 /* don't update battery too often */
1539 if (current_update_time - last_battery_time[idx] < 29.5) {
1540 set_return_value(buffer, n, item, idx);
1544 last_battery_time[idx] = current_update_time;
1546 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1547 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1549 /* first try SYSFS if that fails try ACPI */
1551 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1552 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1555 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1556 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1559 if (sysfs_bat_fp[idx] != NULL) {
1561 int present_rate = -1;
1562 int remaining_capacity = -1;
1563 char charging_state[64];
1566 strcpy(charging_state, "unknown");
1568 while (!feof(sysfs_bat_fp[idx])) {
1570 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1573 /* let's just hope units are ok */
1574 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1575 strcpy(present, "yes");
1576 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1577 strcpy(present, "no");
1578 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1579 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1580 /* present_rate is not the same as the
1581 current flowing now but it is the same value
1582 which was used in the past. so we continue
1584 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1585 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1586 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1587 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1588 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1589 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1590 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1591 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1592 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1593 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1596 fclose(sysfs_bat_fp[idx]);
1597 sysfs_bat_fp[idx] = NULL;
1599 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1600 if (remaining_capacity > acpi_last_full[idx])
1601 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1604 if (strcmp(present, "No") == 0) {
1605 strncpy(last_battery_str[idx], "not present", 64);
1608 else if (strcmp(charging_state, "Charging") == 0) {
1609 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1610 /* e.g. charging 75% */
1611 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1612 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1614 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1615 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1616 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1617 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1618 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1619 snprintf(last_battery_time_str[idx],
1620 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1622 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1623 snprintf(last_battery_time_str[idx],
1624 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1628 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1629 if (present_rate > 0) {
1630 /* e.g. discharging 35% */
1631 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1632 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1634 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1635 (long) (((float) remaining_capacity / present_rate) * 3600));
1636 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1637 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1638 snprintf(last_battery_time_str[idx],
1639 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1641 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1643 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1644 snprintf(last_battery_time_str[idx],
1645 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1649 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1650 else if (strncmp(charging_state, "Charged", 64) == 0) {
1651 /* Below happens with the second battery on my X40,
1652 * when the second one is empty and the first one
1654 if (remaining_capacity == 0)
1655 strcpy(last_battery_str[idx], "empty");
1657 strcpy(last_battery_str[idx], "charged");
1659 /* unknown, probably full / AC */
1661 if (acpi_last_full[idx] != 0
1662 && remaining_capacity != acpi_last_full[idx])
1663 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1664 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1666 strncpy(last_battery_str[idx], "AC", 64);
1668 } else if (acpi_bat_fp[idx] != NULL) {
1670 int present_rate = -1;
1671 int remaining_capacity = -1;
1672 char charging_state[64];
1675 /* read last full capacity if it's zero */
1676 if (acpi_last_full[idx] == 0) {
1677 static int rep3 = 0;
1681 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1682 fp = open_file(path, &rep3);
1687 if (fgets(b, 256, fp) == NULL) {
1690 if (sscanf(b, "last full capacity: %d",
1691 &acpi_last_full[idx]) != 0) {
1700 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1702 strcpy(charging_state, "unknown");
1704 while (!feof(acpi_bat_fp[idx])) {
1707 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1711 /* let's just hope units are ok */
1712 if (strncmp(buf, "present:", 8) == 0) {
1713 sscanf(buf, "present: %4s", present);
1714 } else if (strncmp(buf, "charging state:", 15) == 0) {
1715 sscanf(buf, "charging state: %63s", charging_state);
1716 } else if (strncmp(buf, "present rate:", 13) == 0) {
1717 sscanf(buf, "present rate: %d", &present_rate);
1718 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1719 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1722 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1723 if (remaining_capacity > acpi_last_full[idx]) {
1724 /* normalize to 100% */
1725 acpi_last_full[idx] = remaining_capacity;
1729 if (strcmp(present, "no") == 0) {
1730 strncpy(last_battery_str[idx], "not present", 64);
1732 } else if (strcmp(charging_state, "charging") == 0) {
1733 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1734 /* e.g. charging 75% */
1735 snprintf(last_battery_str[idx],
1736 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1737 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1739 format_seconds(last_battery_time_str[idx],
1740 sizeof(last_battery_time_str[idx]) - 1,
1741 (long) (((acpi_last_full[idx] - remaining_capacity) *
1742 3600) / present_rate));
1743 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1744 snprintf(last_battery_str[idx],
1745 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1746 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1747 snprintf(last_battery_time_str[idx],
1748 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1750 strncpy(last_battery_str[idx], "charging",
1751 sizeof(last_battery_str[idx]) - 1);
1752 snprintf(last_battery_time_str[idx],
1753 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1756 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1757 if (present_rate > 0) {
1758 /* e.g. discharging 35% */
1759 snprintf(last_battery_str[idx],
1760 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1761 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1763 format_seconds(last_battery_time_str[idx],
1764 sizeof(last_battery_time_str[idx]) - 1,
1765 (long) ((remaining_capacity * 3600) / present_rate));
1766 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1767 snprintf(last_battery_str[idx],
1768 sizeof(last_battery_str[idx]) - 1, "full");
1769 snprintf(last_battery_time_str[idx],
1770 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1772 snprintf(last_battery_str[idx],
1773 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1774 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1775 snprintf(last_battery_time_str[idx],
1776 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1779 } else if (strncmp(charging_state, "charged", 64) == 0) {
1780 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1781 /* Below happens with the second battery on my X40,
1782 * when the second one is empty and the first one being charged. */
1783 if (remaining_capacity == 0) {
1784 strcpy(last_battery_str[idx], "empty");
1786 strcpy(last_battery_str[idx], "charged");
1788 /* unknown, probably full / AC */
1790 if (acpi_last_full[idx] != 0
1791 && remaining_capacity != acpi_last_full[idx]) {
1792 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1793 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1795 strncpy(last_battery_str[idx], "AC", 64);
1800 if (apm_bat_fp[idx] == NULL) {
1801 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1804 if (apm_bat_fp[idx] != NULL) {
1805 unsigned int ac, status, flag;
1808 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1809 &ac, &status, &flag, &life);
1812 /* could check now that there is ac */
1813 snprintf(last_battery_str[idx], 64, "AC");
1815 /* could check that status == 3 here? */
1816 } else if (ac && life != 100) {
1817 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1819 snprintf(last_battery_str[idx], 64, "%d%%", life);
1822 /* it seemed to buffer it so file must be closed (or could use
1823 * syscalls directly but I don't feel like coding it now) */
1824 fclose(apm_bat_fp[idx]);
1825 apm_bat_fp[idx] = NULL;
1828 set_return_value(buffer, n, item, idx);
1831 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1834 case BATTERY_STATUS:
1835 snprintf(buffer, n, "%s", last_battery_str[idx]);
1838 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1845 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1847 get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1848 if (0 == strncmp("charging", buffer, 8)) {
1850 memmove(buffer + 1, buffer + 8, n - 8);
1851 } else if (0 == strncmp("discharging", buffer, 11)) {
1853 memmove(buffer + 1, buffer + 11, n - 11);
1857 int get_battery_perct(const char *bat)
1861 char acpi_path[128];
1862 char sysfs_path[128];
1863 int remaining_capacity = -1;
1865 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1866 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1870 idx = get_battery_idx(bat);
1872 /* don't update battery too often */
1873 if (current_update_time - last_battery_perct_time[idx] < 30) {
1874 return last_battery_perct[idx];
1876 last_battery_perct_time[idx] = current_update_time;
1878 /* Only check for SYSFS or ACPI */
1880 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1881 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1885 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1886 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1889 if (sysfs_bat_fp[idx] != NULL) {
1891 while (!feof(sysfs_bat_fp[idx])) {
1893 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1896 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1897 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1898 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1899 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1900 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1901 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1902 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1903 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1907 fclose(sysfs_bat_fp[idx]);
1908 sysfs_bat_fp[idx] = NULL;
1910 } else if (acpi_bat_fp[idx] != NULL) {
1912 /* read last full capacity if it's zero */
1913 if (acpi_design_capacity[idx] == 0) {
1918 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1919 fp = open_file(path, &rep2);
1924 if (fgets(b, 256, fp) == NULL) {
1927 if (sscanf(b, "last full capacity: %d",
1928 &acpi_design_capacity[idx]) != 0) {
1936 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1938 while (!feof(acpi_bat_fp[idx])) {
1941 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1945 if (buf[0] == 'r') {
1946 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1950 if (remaining_capacity < 0) {
1953 /* compute the battery percentage */
1954 last_battery_perct[idx] =
1955 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
1956 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
1957 return last_battery_perct[idx];
1960 int get_battery_perct_bar(const char *bar)
1964 get_battery_perct(bar);
1965 idx = get_battery_idx(bar);
1966 return (int) (last_battery_perct[idx] * 2.56 - 1);
1969 /* On Apple powerbook and ibook:
1970 $ cat /proc/pmu/battery_0
1977 $ cat /proc/pmu/info
1978 PMU driver version : 2
1979 PMU firmware version : 0c
1984 /* defines as in <linux/pmu.h> */
1985 #define PMU_BATT_PRESENT 0x00000001
1986 #define PMU_BATT_CHARGING 0x00000002
1988 static FILE *pmu_battery_fp;
1989 static FILE *pmu_info_fp;
1990 static char pb_battery_info[3][32];
1991 static double pb_battery_info_update;
1993 #define PMU_PATH "/proc/pmu"
1994 void get_powerbook_batt_info(char *buffer, size_t n, int i)
1997 const char *batt_path = PMU_PATH "/battery_0";
1998 const char *info_path = PMU_PATH "/info";
2000 int charge, max_charge, ac = -1;
2003 /* don't update battery too often */
2004 if (current_update_time - pb_battery_info_update < 29.5) {
2005 snprintf(buffer, n, "%s", pb_battery_info[i]);
2008 pb_battery_info_update = current_update_time;
2010 if (pmu_battery_fp == NULL) {
2011 pmu_battery_fp = open_file(batt_path, &rep);
2012 if (pmu_battery_fp == NULL) {
2017 if (pmu_battery_fp != NULL) {
2018 rewind(pmu_battery_fp);
2019 while (!feof(pmu_battery_fp)) {
2022 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2026 if (buf[0] == 'f') {
2027 sscanf(buf, "flags : %8x", &flags);
2028 } else if (buf[0] == 'c' && buf[1] == 'h') {
2029 sscanf(buf, "charge : %d", &charge);
2030 } else if (buf[0] == 'm') {
2031 sscanf(buf, "max_charge : %d", &max_charge);
2032 } else if (buf[0] == 't') {
2033 sscanf(buf, "time rem. : %ld", &timeval);
2037 if (pmu_info_fp == NULL) {
2038 pmu_info_fp = open_file(info_path, &rep);
2039 if (pmu_info_fp == NULL) {
2044 if (pmu_info_fp != NULL) {
2045 rewind(pmu_info_fp);
2046 while (!feof(pmu_info_fp)) {
2049 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2052 if (buf[0] == 'A') {
2053 sscanf(buf, "AC Power : %d", &ac);
2057 /* update status string */
2058 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2059 strncpy(pb_battery_info[PB_BATT_STATUS], "AC", sizeof(pb_battery_info[PB_BATT_STATUS]));
2060 } else if (ac && (flags & PMU_BATT_PRESENT)
2061 && !(flags & PMU_BATT_CHARGING)) {
2062 strncpy(pb_battery_info[PB_BATT_STATUS], "charged", sizeof(pb_battery_info[PB_BATT_STATUS]));
2063 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2064 strncpy(pb_battery_info[PB_BATT_STATUS], "charging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2066 strncpy(pb_battery_info[PB_BATT_STATUS], "discharging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2069 /* update percentage string */
2070 if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT)
2071 && !(flags & PMU_BATT_CHARGING)) {
2072 snprintf(pb_battery_info[PB_BATT_PERCENT],
2073 sizeof(pb_battery_info[PB_BATT_PERCENT]), "100%%");
2074 } else if (timeval == 0) {
2075 snprintf(pb_battery_info[PB_BATT_PERCENT],
2076 sizeof(pb_battery_info[PB_BATT_PERCENT]), "unknown");
2078 snprintf(pb_battery_info[PB_BATT_PERCENT],
2079 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2080 (charge * 100) / max_charge);
2083 /* update time string */
2084 if (timeval == 0) { /* fully charged or battery not present */
2085 snprintf(pb_battery_info[PB_BATT_TIME],
2086 sizeof(pb_battery_info[PB_BATT_TIME]), "unknown");
2087 } else if (timeval < 60 * 60) { /* don't show secs */
2088 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2089 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2091 format_seconds(pb_battery_info[PB_BATT_TIME],
2092 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2095 snprintf(buffer, n, "%s", pb_battery_info[i]);
2098 void update_top(void)
2100 show_nice_processes = 1;
2101 process_find_top(info.cpu, info.memu, info.time);
2102 info.first_process = get_first_process();
2105 /* Here come the IBM ACPI-specific things. For reference, see
2106 * http://ibm-acpi.sourceforge.net/README
2107 * If IBM ACPI is installed, /proc/acpi/ibm contains the following files:
2123 * The content of these files is described in detail in the aforementioned
2124 * README - some of them also in the following functions accessing them.
2125 * Peter Tarjan (ptarjan@citromail.hu) */
2127 #define IBM_ACPI_DIR "/proc/acpi/ibm"
2129 /* get fan speed on IBM/Lenovo laptops running the ibm acpi.
2130 * /proc/acpi/ibm/fan looks like this (3 lines):
2133 commands: enable, disable
2134 * Peter Tarjan (ptarjan@citromail.hu) */
2136 void get_ibm_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
2139 unsigned int speed = 0;
2142 if (!p_client_buffer || client_buffer_size <= 0) {
2146 snprintf(fan, 127, "%s/fan", IBM_ACPI_DIR);
2148 fp = fopen(fan, "r");
2153 if (fgets(line, 255, fp) == NULL) {
2156 if (sscanf(line, "speed: %u", &speed)) {
2161 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2162 "ibm* from your "PACKAGE_NAME" config file.", fan, strerror(errno));
2166 snprintf(p_client_buffer, client_buffer_size, "%d", speed);
2169 /* get the measured temperatures from the temperature sensors
2170 * on IBM/Lenovo laptops running the ibm acpi.
2171 * There are 8 values in /proc/acpi/ibm/thermal, and according to
2172 * http://ibm-acpi.sourceforge.net/README
2173 * these mean the following (at least on an IBM R51...)
2174 * 0: CPU (also on the T series laptops)
2175 * 1: Mini PCI Module (?)
2177 * 3: GPU (also on the T series laptops)
2182 * I'm not too sure about those with the question mark, but the values I'm
2183 * reading from *my* thermal file (on a T42p) look realistic for the
2184 * hdd and the battery.
2185 * #5 and #7 are always -128.
2186 * /proc/acpi/ibm/thermal looks like this (1 line):
2187 temperatures: 41 43 31 46 33 -128 29 -128
2188 * Peter Tarjan (ptarjan@citromail.hu) */
2190 static double last_ibm_acpi_temp_time;
2191 void get_ibm_acpi_temps(void)
2197 /* don't update too often */
2198 if (current_update_time - last_ibm_acpi_temp_time < 10.00) {
2201 last_ibm_acpi_temp_time = current_update_time;
2203 /* if (!p_client_buffer || client_buffer_size <= 0) {
2207 snprintf(thermal, 127, "%s/thermal", IBM_ACPI_DIR);
2208 fp = fopen(thermal, "r");
2214 if (fgets(line, 255, fp) == NULL) {
2217 if (sscanf(line, "temperatures: %d %d %d %d %d %d %d %d",
2218 &ibm_acpi.temps[0], &ibm_acpi.temps[1], &ibm_acpi.temps[2],
2219 &ibm_acpi.temps[3], &ibm_acpi.temps[4], &ibm_acpi.temps[5],
2220 &ibm_acpi.temps[6], &ibm_acpi.temps[7])) {
2225 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2226 "ibm* from your "PACKAGE_NAME" config file.", thermal, strerror(errno));
2232 /* get volume (0-14) on IBM/Lenovo laptops running the ibm acpi.
2233 * "Volume" here is none of the mixer volumes, but a "master of masters"
2234 * volume adjusted by the IBM volume keys.
2235 * /proc/acpi/ibm/fan looks like this (4 lines):
2238 commands: up, down, mute
2239 commands: level <level> (<level> is 0-15)
2240 * Peter Tarjan (ptarjan@citromail.hu) */
2242 void get_ibm_acpi_volume(char *p_client_buffer, size_t client_buffer_size)
2246 unsigned int vol = -1;
2249 if (!p_client_buffer || client_buffer_size <= 0) {
2253 snprintf(volume, 127, "%s/volume", IBM_ACPI_DIR);
2255 fp = fopen(volume, "r");
2259 unsigned int read_vol = -1;
2261 if (fgets(line, 255, fp) == NULL) {
2264 if (sscanf(line, "level: %u", &read_vol)) {
2268 if (sscanf(line, "mute: %s", mute)) {
2273 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2274 "ibm* from your "PACKAGE_NAME" config file.", volume, strerror(errno));
2279 if (strcmp(mute, "on") == 0) {
2280 snprintf(p_client_buffer, client_buffer_size, "%s", "mute");
2283 snprintf(p_client_buffer, client_buffer_size, "%d", vol);
2288 /* static FILE *fp = NULL; */
2290 /* get LCD brightness on IBM/Lenovo laptops running the ibm acpi.
2291 * /proc/acpi/ibm/brightness looks like this (3 lines):
2294 commands: level <level> (<level> is 0-7)
2295 * Peter Tarjan (ptarjan@citromail.hu) */
2297 void get_ibm_acpi_brightness(char *p_client_buffer, size_t client_buffer_size)
2300 unsigned int brightness = 0;
2303 if (!p_client_buffer || client_buffer_size <= 0) {
2307 snprintf(filename, 127, "%s/brightness", IBM_ACPI_DIR);
2309 fp = fopen(filename, "r");
2314 if (fgets(line, 255, fp) == NULL) {
2317 if (sscanf(line, "level: %u", &brightness)) {
2322 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2323 "ibm* from your "PACKAGE_NAME" config file.", filename, strerror(errno));
2328 snprintf(p_client_buffer, client_buffer_size, "%d", brightness);
2331 void update_entropy(void)
2334 const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2335 const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2338 info.entropy.entropy_avail = 0;
2339 info.entropy.poolsize = 0;
2341 if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2345 if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2350 fscanf(fp1, "%u", &info.entropy.entropy_avail);
2351 fscanf(fp2, "%u", &info.entropy.poolsize);
2356 info.mask |= (1 << INFO_ENTROPY);
2359 const char *get_disk_protect_queue(const char *disk)
2365 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2366 if (access(path, F_OK)) {
2367 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2369 if ((fp = fopen(path, "r")) == NULL)
2371 if (fscanf(fp, "%d\n", &state) != 1) {
2376 return (state > 0) ? "frozen" : "free ";