1 /* Conky, a system monitor, based on torsmo
3 * Any original torsmo code is licensed under the BSD license
5 * All code written since the fork of torsmo is licensed under the GPL
7 * Please see COPYING for details
9 * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
10 * Copyright (c) 2007 Toni Spets
11 * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
13 * All rights reserved.
15 * This program is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation, either version 3 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program. If not, see <http://www.gnu.org/licenses/>.
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 /* This flag tells the linux routines to use the /proc system where possible,
69 * even if other api's are available, e.g. sysinfo() or getloadavg().
70 * the reason for this is to allow for /proc-based distributed monitoring.
71 * using a flag in this manner creates less confusing code. */
72 static int prefer_proc = 0;
74 void prepare_update(void)
78 void update_uptime(void)
82 struct sysinfo s_info;
85 info.uptime = (double) s_info.uptime;
92 if (!(fp = open_file("/proc/uptime", &rep))) {
96 fscanf(fp, "%lf", &info.uptime);
99 info.mask |= (1 << INFO_UPTIME);
102 int check_mount(char *s)
105 FILE *mtab = fopen("/etc/mtab", "r");
108 char buf1[256], buf2[128];
110 while (fgets(buf1, 256, mtab)) {
111 sscanf(buf1, "%*s %128s", buf2);
112 if (!strcmp(s, buf2)) {
119 ERR("Could not open mtab");
124 /* these things are also in sysinfo except Buffers:
125 * (that's why I'm reading them from proc) */
127 void update_meminfo(void)
132 /* unsigned int a; */
135 info.mem = info.memmax = info.swap = info.swapmax = info.bufmem =
136 info.buffers = info.cached = info.memfree = info.memeasyfree = 0;
138 if (!(meminfo_fp = open_file("/proc/meminfo", &rep))) {
142 while (!feof(meminfo_fp)) {
143 if (fgets(buf, 255, meminfo_fp) == NULL) {
147 if (strncmp(buf, "MemTotal:", 9) == 0) {
148 sscanf(buf, "%*s %llu", &info.memmax);
149 } else if (strncmp(buf, "MemFree:", 8) == 0) {
150 sscanf(buf, "%*s %llu", &info.memfree);
151 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
152 sscanf(buf, "%*s %llu", &info.swapmax);
153 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
154 sscanf(buf, "%*s %llu", &info.swap);
155 } else if (strncmp(buf, "Buffers:", 8) == 0) {
156 sscanf(buf, "%*s %llu", &info.buffers);
157 } else if (strncmp(buf, "Cached:", 7) == 0) {
158 sscanf(buf, "%*s %llu", &info.cached);
162 info.mem = info.memmax - info.memfree;
163 info.memeasyfree = info.memfree;
164 info.swap = info.swapmax - info.swap;
166 info.bufmem = info.cached + info.buffers;
168 info.mask |= (1 << INFO_MEM) | (1 << INFO_BUFFERS);
173 int get_laptop_mode(void)
178 if ((fp = fopen("/proc/sys/vm/laptop_mode", "r")) != NULL)
179 fscanf(fp, "%d\n", &val);
185 * # cat /sys/block/sda/queue/scheduler
186 * noop [anticipatory] cfq
188 char *get_ioscheduler(char *disk)
194 return strndup("n/a", text_buffer_size);
196 snprintf(buf, 127, "/sys/block/%s/queue/scheduler", disk);
197 if ((fp = fopen(buf, "r")) == NULL) {
198 return strndup("n/a", text_buffer_size);
201 fscanf(fp, "%127s", buf);
203 buf[strlen(buf) - 1] = '\0';
205 return strndup(buf + 1, text_buffer_size);
209 return strndup("n/a", text_buffer_size);
212 #define COND_FREE(x) if(x) free(x); x = 0
213 #define SAVE_SET_STRING(x, y) \
214 if (x && strcmp((char *)x, (char *)y)) { \
216 x = strndup("multiple", text_buffer_size); \
218 x = strndup(y, text_buffer_size); \
221 void update_gateway_info_failure(const char *reason)
226 //2 pointers to 1 location causes a crash when we try to free them both
227 info.gw_info.iface = strndup("failed", text_buffer_size);
228 info.gw_info.ip = strndup("failed", text_buffer_size);
232 /* Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT */
233 #define RT_ENTRY_FORMAT "%63s %lx %lx %x %*d %*d %*d %lx %*d %*d %*d\n"
235 void update_gateway_info(void)
240 unsigned long dest, gate, mask;
243 struct gateway_info *gw_info = &info.gw_info;
245 COND_FREE(gw_info->iface);
246 COND_FREE(gw_info->ip);
249 if ((fp = fopen("/proc/net/route", "r")) == NULL) {
250 update_gateway_info_failure("fopen()");
254 /* skip over the table header line, which is always present */
255 fscanf(fp, "%*[^\n]\n");
258 if(fscanf(fp, RT_ENTRY_FORMAT,
259 iface, &dest, &gate, &flags, &mask) != 5) {
260 update_gateway_info_failure("fscanf()");
263 if (!(dest || mask) && ((flags & RTF_GATEWAY) || !gate) ) {
265 SAVE_SET_STRING(gw_info->iface, iface)
267 SAVE_SET_STRING(gw_info->ip, inet_ntoa(ina))
274 void update_net_stats(void)
278 static char first = 1;
280 // FIXME: arbitrary size chosen to keep code simple.
282 unsigned int curtmp1, curtmp2;
289 // wireless info variables
290 int skfd, has_bitrate = 0;
291 struct wireless_info *winfo;
296 delta = current_update_time - last_update_time;
297 if (delta <= 0.0001) {
301 /* open file and ignore first two lines */
302 if (!(net_dev_fp = open_file("/proc/net/dev", &rep))) {
307 fgets(buf, 255, net_dev_fp); /* garbage */
308 fgets(buf, 255, net_dev_fp); /* garbage (field names) */
310 /* read each interface */
311 for (i2 = 0; i2 < 16; i2++) {
315 long long r, t, last_recv, last_trans;
317 if (fgets(buf, 255, net_dev_fp) == NULL) {
321 while (isspace((int) *p)) {
327 while (*p && *p != ':') {
336 ns = get_net_stat(s);
338 memset(&(ns->addr.sa_data), 0, 14);
340 memset(ns->addrs, 0, 17 * 16 + 1); /* Up to 17 chars per ip, max 16 interfaces. Nasty memory usage... */
342 last_recv = ns->recv;
343 last_trans = ns->trans;
345 /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
346 sscanf(p, "%lld %*d %*d %*d %*d %*d %*d %*d %lld",
349 /* if recv or trans is less than last time, an overflow happened */
350 if (r < ns->last_read_recv) {
353 ns->recv += (r - ns->last_read_recv);
355 ns->last_read_recv = r;
357 if (t < ns->last_read_trans) {
360 ns->trans += (t - ns->last_read_trans);
362 ns->last_read_trans = t;
364 /*** ip addr patch ***/
365 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
367 conf.ifc_buf = malloc(sizeof(struct ifreq) * 16);
368 conf.ifc_len = sizeof(struct ifreq) * 16;
369 memset(conf.ifc_buf, 0, conf.ifc_len);
371 ioctl((long) i, SIOCGIFCONF, &conf);
373 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
374 struct net_stat *ns2;
376 if (!(((struct ifreq *) conf.ifc_buf) + k))
380 ((struct ifreq *) conf.ifc_buf)[k].ifr_ifrn.ifrn_name);
381 ns2->addr = ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.ifru_addr;
382 sprintf(temp_addr, "%u.%u.%u.%u, ",
383 ns2->addr.sa_data[2] & 255,
384 ns2->addr.sa_data[3] & 255,
385 ns2->addr.sa_data[4] & 255,
386 ns2->addr.sa_data[5] & 255);
387 if(NULL == strstr(ns2->addrs, temp_addr))
388 strncpy(ns2->addrs + strlen(ns2->addrs), temp_addr, 17);
395 /*** end ip addr patch ***/
398 /* calculate speeds */
399 ns->net_rec[0] = (ns->recv - last_recv) / delta;
400 ns->net_trans[0] = (ns->trans - last_trans) / delta;
407 #pragma omp parallel for reduction(+:curtmp1, curtmp2)
408 #endif /* HAVE_OPENMP */
409 for (i = 0; i < info.net_avg_samples; i++) {
410 curtmp1 = curtmp1 + ns->net_rec[i];
411 curtmp2 = curtmp2 + ns->net_trans[i];
419 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
420 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
421 if (info.net_avg_samples > 1) {
423 #pragma omp parallel for
424 #endif /* HAVE_OPENMP */
425 for (i = info.net_avg_samples; i > 1; i--) {
426 ns->net_rec[i - 1] = ns->net_rec[i - 2];
427 ns->net_trans[i - 1] = ns->net_trans[i - 2];
432 /* update wireless info */
433 winfo = malloc(sizeof(struct wireless_info));
434 memset(winfo, 0, sizeof(struct wireless_info));
436 skfd = iw_sockets_open();
437 if (iw_get_basic_config(skfd, s, &(winfo->b)) > -1) {
439 // set present winfo variables
440 if (iw_get_stats(skfd, s, &(winfo->stats),
441 &winfo->range, winfo->has_range) >= 0) {
442 winfo->has_stats = 1;
444 if (iw_get_range_info(skfd, s, &(winfo->range)) >= 0) {
445 winfo->has_range = 1;
447 if (iw_get_ext(skfd, s, SIOCGIWAP, &wrq) >= 0) {
448 winfo->has_ap_addr = 1;
449 memcpy(&(winfo->ap_addr), &(wrq.u.ap_addr), sizeof(sockaddr));
453 if (iw_get_ext(skfd, s, SIOCGIWRATE, &wrq) >= 0) {
454 memcpy(&(winfo->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
455 iw_print_bitrate(ns->bitrate, 16, winfo->bitrate.value);
460 if (winfo->has_range && winfo->has_stats
461 && ((winfo->stats.qual.level != 0)
462 || (winfo->stats.qual.updated & IW_QUAL_DBM))) {
463 if (!(winfo->stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
464 ns->link_qual = winfo->stats.qual.qual;
465 ns->link_qual_max = winfo->range.max_qual.qual;
470 if (winfo->has_ap_addr) {
471 iw_sawap_ntop(&winfo->ap_addr, ns->ap);
475 if (winfo->b.has_essid) {
476 if (winfo->b.essid_on) {
477 snprintf(ns->essid, 32, "%s", winfo->b.essid);
479 snprintf(ns->essid, 32, "off/any");
483 snprintf(ns->mode, 16, "%s", iw_operation_mode[winfo->b.mode]);
485 iw_sockets_close(skfd);
493 info.mask |= (1 << INFO_NET);
498 void update_total_processes(void)
502 struct sysinfo s_info;
505 info.procs = s_info.procs;
512 if (!(fp = open_file("/proc/loadavg", &rep))) {
516 fscanf(fp, "%*f %*f %*f %*d/%hu", &info.procs);
519 info.mask |= (1 << INFO_PROCS);
522 #define CPU_SAMPLE_COUNT 15
524 unsigned long long cpu_user;
525 unsigned long long cpu_system;
526 unsigned long long cpu_nice;
527 unsigned long long cpu_idle;
528 unsigned long long cpu_iowait;
529 unsigned long long cpu_irq;
530 unsigned long long cpu_softirq;
531 unsigned long long cpu_steal;
532 unsigned long long cpu_total;
533 unsigned long long cpu_active_total;
534 unsigned long long cpu_last_total;
535 unsigned long long cpu_last_active_total;
536 double cpu_val[CPU_SAMPLE_COUNT];
538 static short cpu_setup = 0;
540 /* Determine if this kernel gives us "extended" statistics information in
542 * Kernels around 2.5 and earlier only reported user, system, nice, and
543 * idle values in proc stat.
544 * Kernels around 2.6 and greater report these PLUS iowait, irq, softirq,
546 void determine_longstat(char *buf)
548 unsigned long long iowait = 0;
550 KFLAG_SETOFF(KFLAG_IS_LONGSTAT);
551 /* scanf will either return -1 or 1 because there is only 1 assignment */
552 if (sscanf(buf, "%*s %*d %*d %*d %*d %llu", &iowait) > 0) {
553 KFLAG_SETON(KFLAG_IS_LONGSTAT);
557 void get_cpu_count(void)
563 if (info.cpu_usage) {
567 if (!(stat_fp = open_file("/proc/stat", &rep))) {
573 while (!feof(stat_fp)) {
574 if (fgets(buf, 255, stat_fp) == NULL) {
578 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
579 if (info.cpu_count == 0) {
580 determine_longstat(buf);
585 info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
590 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
591 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
593 inline static void update_stat(void)
597 static struct cpu_info *cpu = NULL;
602 const char *stat_template = NULL;
603 unsigned int malloc_cpu_size = 0;
605 /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
606 if (!cpu_setup || !info.cpu_usage) {
611 if (!stat_template) {
613 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
617 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
618 cpu = malloc(malloc_cpu_size);
619 memset(cpu, 0, malloc_cpu_size);
622 if (!(stat_fp = open_file("/proc/stat", &rep))) {
624 if (info.cpu_usage) {
625 memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
631 while (!feof(stat_fp)) {
632 if (fgets(buf, 255, stat_fp) == NULL) {
636 if (strncmp(buf, "procs_running ", 14) == 0) {
637 sscanf(buf, "%*s %hu", &info.run_procs);
638 info.mask |= (1 << INFO_RUN_PROCS);
639 } else if (strncmp(buf, "cpu", 3) == 0) {
641 if (isdigit(buf[3])) {
642 idx = atoi(&buf[3]) + 1;
646 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
647 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
648 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
649 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
650 &(cpu[idx].cpu_steal));
652 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
653 cpu[idx].cpu_system + cpu[idx].cpu_idle +
654 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
655 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
657 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
658 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
659 info.mask |= (1 << INFO_CPU);
661 delta = current_update_time - last_update_time;
663 if (delta <= 0.001) {
667 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
668 cpu[idx].cpu_last_active_total) /
669 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
672 #pragma omp parallel for reduction(+:curtmp)
673 #endif /* HAVE_OPENMP */
674 for (i = 0; i < info.cpu_avg_samples; i++) {
675 curtmp = curtmp + cpu[idx].cpu_val[i];
677 /* TESTING -- I've removed this, because I don't think it is right.
678 * You shouldn't divide by the cpu count here ...
679 * removing for testing */
681 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
684 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
686 /* TESTING -- this line replaces the prev. "suspect" if/else */
687 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
689 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
690 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
692 #pragma omp parallel for
693 #endif /* HAVE_OPENMP */
694 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
695 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
702 void update_running_processes(void)
707 void update_cpu_usage(void)
712 void update_load_average(void)
714 #ifdef HAVE_GETLOADAVG
719 info.loadavg[0] = (float) v[0];
720 info.loadavg[1] = (float) v[1];
721 info.loadavg[2] = (float) v[2];
728 if (!(fp = open_file("/proc/loadavg", &rep))) {
729 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
732 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
736 info.mask |= (1 << INFO_LOADAVG);
739 #define PROC_I8K "/proc/i8k"
740 #define I8K_DELIM " "
741 static char *i8k_procbuf = NULL;
742 void update_i8k(void)
747 i8k_procbuf = (char *) malloc(128 * sizeof(char));
749 if ((fp = fopen(PROC_I8K, "r")) == NULL) {
750 CRIT_ERR("/proc/i8k doesn't exist! use insmod to make sure the kernel "
751 "driver is loaded...");
754 memset(&i8k_procbuf[0], 0, 128);
755 if (fread(&i8k_procbuf[0], sizeof(char), 128, fp) == 0) {
756 ERR("something wrong with /proc/i8k...");
761 i8k.version = strtok(&i8k_procbuf[0], I8K_DELIM);
762 i8k.bios = strtok(NULL, I8K_DELIM);
763 i8k.serial = strtok(NULL, I8K_DELIM);
764 i8k.cpu_temp = strtok(NULL, I8K_DELIM);
765 i8k.left_fan_status = strtok(NULL, I8K_DELIM);
766 i8k.right_fan_status = strtok(NULL, I8K_DELIM);
767 i8k.left_fan_rpm = strtok(NULL, I8K_DELIM);
768 i8k.right_fan_rpm = strtok(NULL, I8K_DELIM);
769 i8k.ac_status = strtok(NULL, I8K_DELIM);
770 i8k.buttons_status = strtok(NULL, I8K_DELIM);
773 /***********************************************************/
774 /***********************************************************/
775 /***********************************************************/
777 static int no_dots(const struct dirent *d)
779 if (d->d_name[0] == '.') {
785 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
787 struct dirent **namelist;
790 n = scandir(dir, &namelist, no_dots, alphasort);
793 ERR("scandir for %s: %s", dir, strerror(errno));
804 strncpy(s, namelist[0]->d_name, 255);
808 #pragma omp parallel for
809 #endif /* HAVE_OPENMP */
810 for (i = 0; i < n; i++) {
819 int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
820 int *divisor, char *devtype)
828 memset(buf, 0, sizeof(buf));
830 /* if device is NULL or *, get first */
831 if (dev == NULL || strcmp(dev, "*") == 0) {
834 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
840 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
842 /* buf holds result from get_first_file_in_a_directory() above,
843 * e.g. "hwmon0" -- append "/device" */
844 strcat(buf, "/device");
846 /* dev holds device number N as a string,
847 * e.g. "0", -- convert to "hwmon0/device" */
848 sprintf(buf, "hwmon%s/device", dev);
853 /* At least the acpitz hwmon doesn't have a 'device' subdir,
854 * so check it's existence and strip it from buf otherwise. */
855 snprintf(path, 255, "%s%s", dir, dev);
856 if (stat(path, &st)) {
857 buf[strlen(buf) - 7] = 0;
860 /* change vol to in, tempf to temp */
861 if (strcmp(type, "vol") == 0) {
863 } else if (strcmp(type, "tempf") == 0) {
867 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
868 strncpy(devtype, path, 255);
871 fd = open(path, O_RDONLY);
873 CRIT_ERR("can't open '%s': %s\nplease check your device or remove this "
874 "var from "PACKAGE_NAME, path, strerror(errno));
877 if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
878 || strcmp(type, "tempf") == 0) {
883 /* fan does not use *_div as a read divisor */
884 if (strcmp("fan", type) == 0) {
888 /* test if *_div file exist, open it and use it as divisor */
889 if (strcmp(type, "tempf") == 0) {
890 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
892 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
895 divfd = open(path, O_RDONLY);
901 divn = read(divfd, divbuf, 63);
902 /* should read until n == 0 but I doubt that kernel will give these
903 * in multiple pieces. :) */
905 ERR("open_sysfs_sensor(): can't read from sysfs");
908 *divisor = atoi(divbuf);
917 double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
925 lseek(*fd, 0, SEEK_SET);
931 n = read(*fd, buf, 63);
932 /* should read until n == 0 but I doubt that kernel will give these
933 * in multiple pieces. :) */
935 ERR("get_sysfs_info(): read from %s failed\n", devtype);
944 *fd = open(devtype, O_RDONLY);
946 ERR("can't open '%s': %s", devtype, strerror(errno));
949 /* My dirty hack for computing CPU value
950 * Filedil, from forums.gentoo.org */
951 /* if (strstr(devtype, "temp1_input") != NULL) {
952 return -15.096 + 1.4893 * (val / 1000.0);
955 /* divide voltage and temperature by 1000 */
956 /* or if any other divisor is given, use that */
957 if (strcmp(type, "tempf") == 0) {
959 return ((val / divisor + 40) * 9.0 / 5) - 40;
960 } else if (divisor) {
961 return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
963 return ((val + 40) * 9.0 / 5) - 40;
967 return val / divisor;
968 } else if (divisor) {
976 /* Prior to kernel version 2.6.12, the CPU fan speed was available in
977 * ADT746X_FAN_OLD, whereas later kernel versions provide this information in
979 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
980 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
982 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
985 char adt746x_fan_state[64];
988 if (!p_client_buffer || client_buffer_size <= 0) {
992 if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
993 && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL) {
994 sprintf(adt746x_fan_state, "adt746x not found");
996 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
997 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
1001 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_fan_state);
1004 /* Prior to kernel version 2.6.12, the CPU temperature was found in
1005 * ADT746X_CPU_OLD, whereas later kernel versions provide this information in
1007 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
1008 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
1010 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
1013 char adt746x_cpu_state[64];
1016 if (!p_client_buffer || client_buffer_size <= 0) {
1020 if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
1021 && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL) {
1022 sprintf(adt746x_cpu_state, "adt746x not found");
1024 fscanf(fp, "%2s", adt746x_cpu_state);
1028 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state);
1031 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1032 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1034 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1035 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1036 const char *p_format, int divisor, unsigned int cpu)
1044 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1050 char current_freq_file[128];
1052 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1054 f = fopen(current_freq_file, "r");
1056 /* if there's a cpufreq /sys node, read the current frequency from
1057 * this node and divide by 1000 to get Mhz. */
1058 if (fgets(s, sizeof(s), f)) {
1059 s[strlen(s) - 1] = '\0';
1060 freq = strtod(s, NULL);
1063 snprintf(p_client_buffer, client_buffer_size, p_format,
1064 (freq / 1000) / divisor);
1069 // open the CPU information file
1070 f = open_file("/proc/cpuinfo", &rep);
1072 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1077 while (fgets(s, sizeof(s), f) != NULL) {
1079 #if defined(__i386) || defined(__x86_64)
1080 // and search for the cpu mhz
1081 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1083 #if defined(__alpha)
1084 // different on alpha
1085 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1087 // this is different on ppc for some reason
1088 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1089 #endif // defined(__alpha)
1090 #endif // defined(__i386) || defined(__x86_64)
1092 // copy just the number
1093 strcpy(frequency, strchr(s, ':') + 2);
1094 #if defined(__alpha)
1096 frequency[strlen(frequency) - 6] = '\0';
1097 // kernel reports in Hz
1098 freq = strtod(frequency, NULL) / 1000000;
1101 frequency[strlen(frequency) - 1] = '\0';
1102 freq = strtod(frequency, NULL);
1106 if (strncmp(s, "processor", 9) == 0) {
1113 snprintf(p_client_buffer, client_buffer_size, p_format,
1114 (float) freq / divisor);
1118 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1120 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1130 * Peter Tarjan (ptarjan@citromail.hu) */
1132 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1133 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1134 const char *p_format, int divisor, unsigned int cpu)
1140 char current_freq_file[128];
1143 /* build the voltage file name */
1145 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1148 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1153 /* read the current cpu frequency from the /sys node */
1154 f = fopen(current_freq_file, "r");
1156 if (fgets(s, sizeof(s), f)) {
1157 s[strlen(s) - 1] = '\0';
1158 freq = strtod(s, NULL);
1162 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1163 perror("get_voltage()");
1170 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1173 /* use the current cpu frequency to find the corresponding voltage */
1174 f = fopen(current_freq_file, "r");
1180 if (fgets(line, 255, f) == NULL) {
1183 sscanf(line, "%d %d", &freq_comp, &voltage);
1184 if (freq_comp == freq) {
1190 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1191 perror("get_voltage()");
1197 snprintf(p_client_buffer, client_buffer_size, p_format,
1198 (float) voltage / divisor);
1202 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1204 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1211 if (!p_client_buffer || client_buffer_size <= 0) {
1215 /* yeah, slow... :/ */
1216 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1217 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1221 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1223 fp = open_file(buf2, &rep);
1225 snprintf(p_client_buffer, client_buffer_size,
1226 "can't open fan's state file");
1229 memset(buf, 0, sizeof(buf));
1230 fscanf(fp, "%*s %99s", buf);
1233 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1236 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1237 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1238 /* Linux 2.6.25 onwards ac adapter info is in
1239 /sys/class/power_supply/AC/
1240 On my system I get the following.
1241 /sys/class/power_supply/AC/uevent:
1242 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1245 POWER_SUPPLY_NAME=AC
1246 POWER_SUPPLY_TYPE=Mains
1247 POWER_SUPPLY_ONLINE=1
1250 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1258 if (!p_client_buffer || client_buffer_size <= 0) {
1262 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1263 fp = open_file(buf2, &rep);
1265 /* sysfs processing */
1267 if (fgets(buf, sizeof(buf), fp) == NULL)
1270 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1272 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1273 snprintf(p_client_buffer, client_buffer_size,
1274 "%s-line", (online ? "on" : "off"));
1280 /* yeah, slow... :/ */
1281 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1282 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1286 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1288 fp = open_file(buf2, &rep);
1290 snprintf(p_client_buffer, client_buffer_size,
1291 "No ac adapter found.... where is it?");
1294 memset(buf, 0, sizeof(buf));
1295 fscanf(fp, "%*s %99s", buf);
1298 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1303 /proc/acpi/thermal_zone/THRM/cooling_mode
1304 cooling mode: active
1305 /proc/acpi/thermal_zone/THRM/polling_frequency
1307 /proc/acpi/thermal_zone/THRM/state
1309 /proc/acpi/thermal_zone/THRM/temperature
1311 /proc/acpi/thermal_zone/THRM/trip_points
1313 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1316 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1317 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1319 int open_acpi_temperature(const char *name)
1325 if (name == NULL || strcmp(name, "*") == 0) {
1328 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1334 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1336 fd = open(path, O_RDONLY);
1338 ERR("can't open '%s': %s", path, strerror(errno));
1344 static double last_acpi_temp;
1345 static double last_acpi_temp_time;
1347 double get_acpi_temperature(int fd)
1353 /* don't update acpi temperature too often */
1354 if (current_update_time - last_acpi_temp_time < 11.32) {
1355 return last_acpi_temp;
1357 last_acpi_temp_time = current_update_time;
1359 /* seek to beginning */
1360 lseek(fd, 0, SEEK_SET);
1367 n = read(fd, buf, 255);
1369 ERR("can't read fd %d: %s", fd, strerror(errno));
1372 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1376 return last_acpi_temp;
1380 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1382 design capacity: 4400 mAh
1383 last full capacity: 4064 mAh
1384 battery technology: rechargeable
1385 design voltage: 14800 mV
1386 design capacity warning: 300 mAh
1387 design capacity low: 200 mAh
1388 capacity granularity 1: 32 mAh
1389 capacity granularity 2: 32 mAh
1391 serial number: 16922
1397 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1400 charging state: unknown
1402 remaining capacity: 4064 mAh
1403 present voltage: 16608 mV
1407 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1408 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1409 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1410 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1411 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1413 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1414 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1416 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1417 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1420 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1421 Linux 2.6.24 onwards battery info is in
1422 /sys/class/power_supply/BAT0/
1423 On my system I get the following.
1424 /sys/class/power_supply/BAT0/uevent:
1425 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1427 PHYSDEVDRIVER=battery
1428 POWER_SUPPLY_NAME=BAT0
1429 POWER_SUPPLY_TYPE=Battery
1430 POWER_SUPPLY_STATUS=Discharging
1431 POWER_SUPPLY_PRESENT=1
1432 POWER_SUPPLY_TECHNOLOGY=Li-ion
1433 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1434 POWER_SUPPLY_VOLTAGE_NOW=10780000
1435 POWER_SUPPLY_CURRENT_NOW=13970000
1436 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1437 POWER_SUPPLY_ENERGY_FULL=27370000
1438 POWER_SUPPLY_ENERGY_NOW=11810000
1439 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1440 POWER_SUPPLY_MANUFACTURER=Panasonic
1441 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1444 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1445 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1446 #define APM_PATH "/proc/apm"
1447 #define MAX_BATTERY_COUNT 4
1449 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1450 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1451 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1453 static int batteries_initialized = 0;
1454 static char batteries[MAX_BATTERY_COUNT][32];
1456 static int acpi_last_full[MAX_BATTERY_COUNT];
1457 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1459 /* e.g. "charging 75%" */
1460 static char last_battery_str[MAX_BATTERY_COUNT][64];
1462 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1464 static double last_battery_time[MAX_BATTERY_COUNT];
1466 static int last_battery_perct[MAX_BATTERY_COUNT];
1467 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1469 void init_batteries(void)
1473 if (batteries_initialized) {
1477 #pragma omp parallel for
1478 #endif /* HAVE_OPENMP */
1479 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1480 batteries[idx][0] = '\0';
1482 batteries_initialized = 1;
1485 int get_battery_idx(const char *bat)
1489 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1490 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1495 /* if not found, enter a new entry */
1496 if (!strlen(batteries[idx])) {
1497 snprintf(batteries[idx], 31, "%s", bat);
1503 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1505 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1507 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1508 char acpi_path[128];
1509 char sysfs_path[128];
1511 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1512 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1516 idx = get_battery_idx(bat);
1518 /* don't update battery too often */
1519 if (current_update_time - last_battery_time[idx] < 29.5) {
1520 set_return_value(buffer, n, item, idx);
1524 last_battery_time[idx] = current_update_time;
1526 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1527 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1529 /* first try SYSFS if that fails try ACPI */
1531 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1532 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1535 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1536 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1539 if (sysfs_bat_fp[idx] != NULL) {
1541 int present_rate = -1;
1542 int remaining_capacity = -1;
1543 char charging_state[64];
1546 strcpy(charging_state, "unknown");
1548 while (!feof(sysfs_bat_fp[idx])) {
1550 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1553 /* let's just hope units are ok */
1554 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1555 strcpy(present, "yes");
1556 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1557 strcpy(present, "no");
1558 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1559 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1560 /* present_rate is not the same as the
1561 current flowing now but it is the same value
1562 which was used in the past. so we continue
1564 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1565 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1566 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1567 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1568 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1569 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1570 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1571 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1572 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1573 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1576 fclose(sysfs_bat_fp[idx]);
1577 sysfs_bat_fp[idx] = NULL;
1579 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1580 if (remaining_capacity > acpi_last_full[idx])
1581 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1584 if (strcmp(present, "No") == 0) {
1585 strncpy(last_battery_str[idx], "not present", 64);
1588 else if (strcmp(charging_state, "Charging") == 0) {
1589 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1590 /* e.g. charging 75% */
1591 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1592 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1594 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1595 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1596 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1597 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1598 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1599 snprintf(last_battery_time_str[idx],
1600 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1602 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1603 snprintf(last_battery_time_str[idx],
1604 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1608 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1609 if (present_rate > 0) {
1610 /* e.g. discharging 35% */
1611 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %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) remaining_capacity / present_rate) * 3600));
1616 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1617 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1618 snprintf(last_battery_time_str[idx],
1619 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1621 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1623 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1624 snprintf(last_battery_time_str[idx],
1625 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1629 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1630 else if (strncmp(charging_state, "Charged", 64) == 0) {
1631 /* Below happens with the second battery on my X40,
1632 * when the second one is empty and the first one
1634 if (remaining_capacity == 0)
1635 strcpy(last_battery_str[idx], "empty");
1637 strcpy(last_battery_str[idx], "charged");
1639 /* unknown, probably full / AC */
1641 if (acpi_last_full[idx] != 0
1642 && remaining_capacity != acpi_last_full[idx])
1643 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1644 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1646 strncpy(last_battery_str[idx], "AC", 64);
1648 } else if (acpi_bat_fp[idx] != NULL) {
1650 int present_rate = -1;
1651 int remaining_capacity = -1;
1652 char charging_state[64];
1655 /* read last full capacity if it's zero */
1656 if (acpi_last_full[idx] == 0) {
1657 static int rep3 = 0;
1661 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1662 fp = open_file(path, &rep3);
1667 if (fgets(b, 256, fp) == NULL) {
1670 if (sscanf(b, "last full capacity: %d",
1671 &acpi_last_full[idx]) != 0) {
1680 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1682 strcpy(charging_state, "unknown");
1684 while (!feof(acpi_bat_fp[idx])) {
1687 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1691 /* let's just hope units are ok */
1692 if (strncmp(buf, "present:", 8) == 0) {
1693 sscanf(buf, "present: %4s", present);
1694 } else if (strncmp(buf, "charging state:", 15) == 0) {
1695 sscanf(buf, "charging state: %63s", charging_state);
1696 } else if (strncmp(buf, "present rate:", 13) == 0) {
1697 sscanf(buf, "present rate: %d", &present_rate);
1698 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1699 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1702 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1703 if (remaining_capacity > acpi_last_full[idx]) {
1704 /* normalize to 100% */
1705 acpi_last_full[idx] = remaining_capacity;
1709 if (strcmp(present, "no") == 0) {
1710 strncpy(last_battery_str[idx], "not present", 64);
1712 } else if (strcmp(charging_state, "charging") == 0) {
1713 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1714 /* e.g. charging 75% */
1715 snprintf(last_battery_str[idx],
1716 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1717 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1719 format_seconds(last_battery_time_str[idx],
1720 sizeof(last_battery_time_str[idx]) - 1,
1721 (long) (((acpi_last_full[idx] - remaining_capacity) *
1722 3600) / present_rate));
1723 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1724 snprintf(last_battery_str[idx],
1725 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1726 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1727 snprintf(last_battery_time_str[idx],
1728 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1730 strncpy(last_battery_str[idx], "charging",
1731 sizeof(last_battery_str[idx]) - 1);
1732 snprintf(last_battery_time_str[idx],
1733 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1736 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1737 if (present_rate > 0) {
1738 /* e.g. discharging 35% */
1739 snprintf(last_battery_str[idx],
1740 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1741 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1743 format_seconds(last_battery_time_str[idx],
1744 sizeof(last_battery_time_str[idx]) - 1,
1745 (long) ((remaining_capacity * 3600) / present_rate));
1746 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1747 snprintf(last_battery_str[idx],
1748 sizeof(last_battery_str[idx]) - 1, "full");
1749 snprintf(last_battery_time_str[idx],
1750 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1752 snprintf(last_battery_str[idx],
1753 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1754 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1755 snprintf(last_battery_time_str[idx],
1756 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1759 } else if (strncmp(charging_state, "charged", 64) == 0) {
1760 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1761 /* Below happens with the second battery on my X40,
1762 * when the second one is empty and the first one being charged. */
1763 if (remaining_capacity == 0) {
1764 strcpy(last_battery_str[idx], "empty");
1766 strcpy(last_battery_str[idx], "charged");
1768 /* unknown, probably full / AC */
1770 if (strncmp(charging_state, "Full", 64) == 0) {
1771 strncpy(last_battery_str[idx], "full", 64);
1772 } else if (acpi_last_full[idx] != 0
1773 && remaining_capacity != acpi_last_full[idx]) {
1774 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1775 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1777 strncpy(last_battery_str[idx], "AC", 64);
1782 if (apm_bat_fp[idx] == NULL) {
1783 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1786 if (apm_bat_fp[idx] != NULL) {
1787 unsigned int ac, status, flag;
1790 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1791 &ac, &status, &flag, &life);
1794 /* could check now that there is ac */
1795 snprintf(last_battery_str[idx], 64, "AC");
1797 /* could check that status == 3 here? */
1798 } else if (ac && life != 100) {
1799 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1801 snprintf(last_battery_str[idx], 64, "%d%%", life);
1804 /* it seemed to buffer it so file must be closed (or could use
1805 * syscalls directly but I don't feel like coding it now) */
1806 fclose(apm_bat_fp[idx]);
1807 apm_bat_fp[idx] = NULL;
1810 set_return_value(buffer, n, item, idx);
1813 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1816 case BATTERY_STATUS:
1817 snprintf(buffer, n, "%s", last_battery_str[idx]);
1820 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1827 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1829 get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1830 if (0 == strncmp("charging", buffer, 8)) {
1832 memmove(buffer + 1, buffer + 8, n - 8);
1833 } else if (0 == strncmp("discharging", buffer, 11)) {
1835 memmove(buffer + 1, buffer + 11, n - 11);
1839 int get_battery_perct(const char *bat)
1843 char acpi_path[128];
1844 char sysfs_path[128];
1845 int remaining_capacity = -1;
1847 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1848 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1852 idx = get_battery_idx(bat);
1854 /* don't update battery too often */
1855 if (current_update_time - last_battery_perct_time[idx] < 30) {
1856 return last_battery_perct[idx];
1858 last_battery_perct_time[idx] = current_update_time;
1860 /* Only check for SYSFS or ACPI */
1862 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1863 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1867 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1868 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1871 if (sysfs_bat_fp[idx] != NULL) {
1873 while (!feof(sysfs_bat_fp[idx])) {
1875 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1878 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1879 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1880 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1881 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1882 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1883 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1884 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1885 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1889 fclose(sysfs_bat_fp[idx]);
1890 sysfs_bat_fp[idx] = NULL;
1892 } else if (acpi_bat_fp[idx] != NULL) {
1894 /* read last full capacity if it's zero */
1895 if (acpi_design_capacity[idx] == 0) {
1900 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1901 fp = open_file(path, &rep2);
1906 if (fgets(b, 256, fp) == NULL) {
1909 if (sscanf(b, "last full capacity: %d",
1910 &acpi_design_capacity[idx]) != 0) {
1918 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1920 while (!feof(acpi_bat_fp[idx])) {
1923 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1927 if (buf[0] == 'r') {
1928 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1932 if (remaining_capacity < 0) {
1935 /* compute the battery percentage */
1936 last_battery_perct[idx] =
1937 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
1938 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
1939 return last_battery_perct[idx];
1942 int get_battery_perct_bar(const char *bar)
1946 get_battery_perct(bar);
1947 idx = get_battery_idx(bar);
1948 return (int) (last_battery_perct[idx] * 2.56 - 1);
1951 /* On Apple powerbook and ibook:
1952 $ cat /proc/pmu/battery_0
1959 $ cat /proc/pmu/info
1960 PMU driver version : 2
1961 PMU firmware version : 0c
1966 /* defines as in <linux/pmu.h> */
1967 #define PMU_BATT_PRESENT 0x00000001
1968 #define PMU_BATT_CHARGING 0x00000002
1970 static FILE *pmu_battery_fp;
1971 static FILE *pmu_info_fp;
1972 static char pb_battery_info[3][32];
1973 static double pb_battery_info_update;
1975 #define PMU_PATH "/proc/pmu"
1976 void get_powerbook_batt_info(char *buffer, size_t n, int i)
1979 const char *batt_path = PMU_PATH "/battery_0";
1980 const char *info_path = PMU_PATH "/info";
1982 int charge, max_charge, ac = -1;
1985 /* don't update battery too often */
1986 if (current_update_time - pb_battery_info_update < 29.5) {
1987 snprintf(buffer, n, "%s", pb_battery_info[i]);
1990 pb_battery_info_update = current_update_time;
1992 if (pmu_battery_fp == NULL) {
1993 pmu_battery_fp = open_file(batt_path, &rep);
1994 if (pmu_battery_fp == NULL) {
1999 if (pmu_battery_fp != NULL) {
2000 rewind(pmu_battery_fp);
2001 while (!feof(pmu_battery_fp)) {
2004 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2008 if (buf[0] == 'f') {
2009 sscanf(buf, "flags : %8x", &flags);
2010 } else if (buf[0] == 'c' && buf[1] == 'h') {
2011 sscanf(buf, "charge : %d", &charge);
2012 } else if (buf[0] == 'm') {
2013 sscanf(buf, "max_charge : %d", &max_charge);
2014 } else if (buf[0] == 't') {
2015 sscanf(buf, "time rem. : %ld", &timeval);
2019 if (pmu_info_fp == NULL) {
2020 pmu_info_fp = open_file(info_path, &rep);
2021 if (pmu_info_fp == NULL) {
2026 if (pmu_info_fp != NULL) {
2027 rewind(pmu_info_fp);
2028 while (!feof(pmu_info_fp)) {
2031 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2034 if (buf[0] == 'A') {
2035 sscanf(buf, "AC Power : %d", &ac);
2039 /* update status string */
2040 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2041 strncpy(pb_battery_info[PB_BATT_STATUS], "AC", sizeof(pb_battery_info[PB_BATT_STATUS]));
2042 } else if (ac && (flags & PMU_BATT_PRESENT)
2043 && !(flags & PMU_BATT_CHARGING)) {
2044 strncpy(pb_battery_info[PB_BATT_STATUS], "charged", sizeof(pb_battery_info[PB_BATT_STATUS]));
2045 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2046 strncpy(pb_battery_info[PB_BATT_STATUS], "charging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2048 strncpy(pb_battery_info[PB_BATT_STATUS], "discharging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2051 /* update percentage string */
2052 if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT)
2053 && !(flags & PMU_BATT_CHARGING)) {
2054 snprintf(pb_battery_info[PB_BATT_PERCENT],
2055 sizeof(pb_battery_info[PB_BATT_PERCENT]), "100%%");
2056 } else if (timeval == 0) {
2057 snprintf(pb_battery_info[PB_BATT_PERCENT],
2058 sizeof(pb_battery_info[PB_BATT_PERCENT]), "unknown");
2060 snprintf(pb_battery_info[PB_BATT_PERCENT],
2061 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2062 (charge * 100) / max_charge);
2065 /* update time string */
2066 if (timeval == 0) { /* fully charged or battery not present */
2067 snprintf(pb_battery_info[PB_BATT_TIME],
2068 sizeof(pb_battery_info[PB_BATT_TIME]), "unknown");
2069 } else if (timeval < 60 * 60) { /* don't show secs */
2070 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2071 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2073 format_seconds(pb_battery_info[PB_BATT_TIME],
2074 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2077 snprintf(buffer, n, "%s", pb_battery_info[i]);
2080 void update_top(void)
2082 process_find_top(info.cpu, info.memu, info.time);
2083 info.first_process = get_first_process();
2086 void update_entropy(void)
2089 const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2090 const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2093 info.entropy.entropy_avail = 0;
2094 info.entropy.poolsize = 0;
2096 if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2100 if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2105 fscanf(fp1, "%u", &info.entropy.entropy_avail);
2106 fscanf(fp2, "%u", &info.entropy.poolsize);
2111 info.mask |= (1 << INFO_ENTROPY);
2114 const char *get_disk_protect_queue(const char *disk)
2120 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2121 if (access(path, F_OK)) {
2122 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2124 if ((fp = fopen(path, "r")) == NULL)
2126 if (fscanf(fp, "%d\n", &state) != 1) {
2131 return (state > 0) ? "frozen" : "free ";