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/>.
38 #include <sys/types.h>
39 #include <sys/sysinfo.h>
41 #ifndef HAVE_CLOCK_GETTIME
46 // #include <assert.h>
50 #include <sys/ioctl.h>
51 #include <sys/socket.h>
52 #include <netinet/in.h>
53 #include <linux/sockios.h>
55 #include <arpa/inet.h>
59 #include <linux/route.h>
62 /* The following ifdefs were adapted from gkrellm */
63 #include <linux/major.h>
65 #if !defined(MD_MAJOR)
69 #if !defined(LVM_BLK_MAJOR)
70 #define LVM_BLK_MAJOR 58
73 #if !defined(NBD_MAJOR)
81 #define SHORTSTAT_TEMPL "%*s %llu %llu %llu"
82 #define LONGSTAT_TEMPL "%*s %llu %llu %llu "
84 /* This flag tells the linux routines to use the /proc system where possible,
85 * even if other api's are available, e.g. sysinfo() or getloadavg().
86 * the reason for this is to allow for /proc-based distributed monitoring.
87 * using a flag in this manner creates less confusing code. */
88 static int prefer_proc = 0;
90 void prepare_update(void)
94 void update_uptime(void)
98 struct sysinfo s_info;
101 info.uptime = (double) s_info.uptime;
108 if (!(fp = open_file("/proc/uptime", &rep))) {
112 fscanf(fp, "%lf", &info.uptime);
115 info.mask |= (1 << INFO_UPTIME);
118 int check_mount(char *s)
121 FILE *mtab = fopen("/etc/mtab", "r");
124 char buf1[256], buf2[128];
126 while (fgets(buf1, 256, mtab)) {
127 sscanf(buf1, "%*s %128s", buf2);
128 if (!strcmp(s, buf2)) {
135 ERR("Could not open mtab");
140 /* these things are also in sysinfo except Buffers:
141 * (that's why I'm reading them from proc) */
143 void update_meminfo(void)
148 /* unsigned int a; */
151 info.mem = info.memmax = info.swap = info.swapmax = info.bufmem =
152 info.buffers = info.cached = info.memfree = info.memeasyfree = 0;
154 if (!(meminfo_fp = open_file("/proc/meminfo", &rep))) {
158 while (!feof(meminfo_fp)) {
159 if (fgets(buf, 255, meminfo_fp) == NULL) {
163 if (strncmp(buf, "MemTotal:", 9) == 0) {
164 sscanf(buf, "%*s %llu", &info.memmax);
165 } else if (strncmp(buf, "MemFree:", 8) == 0) {
166 sscanf(buf, "%*s %llu", &info.memfree);
167 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
168 sscanf(buf, "%*s %llu", &info.swapmax);
169 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
170 sscanf(buf, "%*s %llu", &info.swap);
171 } else if (strncmp(buf, "Buffers:", 8) == 0) {
172 sscanf(buf, "%*s %llu", &info.buffers);
173 } else if (strncmp(buf, "Cached:", 7) == 0) {
174 sscanf(buf, "%*s %llu", &info.cached);
178 info.mem = info.memmax - info.memfree;
179 info.memeasyfree = info.memfree;
180 info.swap = info.swapmax - info.swap;
182 info.bufmem = info.cached + info.buffers;
184 info.mask |= (1 << INFO_MEM) | (1 << INFO_BUFFERS);
189 int get_laptop_mode(void)
194 if ((fp = fopen("/proc/sys/vm/laptop_mode", "r")) != NULL)
195 fscanf(fp, "%d\n", &val);
201 * # cat /sys/block/sda/queue/scheduler
202 * noop [anticipatory] cfq
204 char *get_ioscheduler(char *disk)
210 return strndup("n/a", text_buffer_size);
212 snprintf(buf, 127, "/sys/block/%s/queue/scheduler", disk);
213 if ((fp = fopen(buf, "r")) == NULL) {
214 return strndup("n/a", text_buffer_size);
217 fscanf(fp, "%127s", buf);
219 buf[strlen(buf) - 1] = '\0';
221 return strndup(buf + 1, text_buffer_size);
225 return strndup("n/a", text_buffer_size);
228 #define COND_FREE(x) if(x) free(x); x = 0
229 #define SAVE_SET_STRING(x, y) \
230 if (x && strcmp((char *)x, (char *)y)) { \
232 x = strndup("multiple", text_buffer_size); \
234 x = strndup(y, text_buffer_size); \
237 void update_gateway_info_failure(const char *reason)
242 //2 pointers to 1 location causes a crash when we try to free them both
243 info.gw_info.iface = strndup("failed", text_buffer_size);
244 info.gw_info.ip = strndup("failed", text_buffer_size);
248 /* Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT */
249 #define RT_ENTRY_FORMAT "%63s %lx %lx %x %*d %*d %*d %lx %*d %*d %*d\n"
251 void update_gateway_info(void)
256 unsigned long dest, gate, mask;
259 struct gateway_info *gw_info = &info.gw_info;
261 COND_FREE(gw_info->iface);
262 COND_FREE(gw_info->ip);
265 if ((fp = fopen("/proc/net/route", "r")) == NULL) {
266 update_gateway_info_failure("fopen()");
270 /* skip over the table header line, which is always present */
271 fscanf(fp, "%*[^\n]\n");
274 if(fscanf(fp, RT_ENTRY_FORMAT,
275 iface, &dest, &gate, &flags, &mask) != 5) {
276 update_gateway_info_failure("fscanf()");
279 if (!(dest || mask) && ((flags & RTF_GATEWAY) || !gate) ) {
281 SAVE_SET_STRING(gw_info->iface, iface)
283 SAVE_SET_STRING(gw_info->ip, inet_ntoa(ina))
290 void update_net_stats(void)
294 static char first = 1;
296 // FIXME: arbitrary size chosen to keep code simple.
298 unsigned int curtmp1, curtmp2;
305 // wireless info variables
306 int skfd, has_bitrate = 0;
307 struct wireless_info *winfo;
312 delta = current_update_time - last_update_time;
313 if (delta <= 0.0001) {
317 /* open file and ignore first two lines */
318 if (!(net_dev_fp = open_file("/proc/net/dev", &rep))) {
323 fgets(buf, 255, net_dev_fp); /* garbage */
324 fgets(buf, 255, net_dev_fp); /* garbage (field names) */
326 /* read each interface */
327 for (i2 = 0; i2 < 16; i2++) {
331 long long r, t, last_recv, last_trans;
333 if (fgets(buf, 255, net_dev_fp) == NULL) {
337 while (isspace((int) *p)) {
343 while (*p && *p != ':') {
352 ns = get_net_stat(s);
354 memset(&(ns->addr.sa_data), 0, 14);
356 memset(ns->addrs, 0, 17 * 16 + 1); /* Up to 17 chars per ip, max 16 interfaces. Nasty memory usage... */
358 last_recv = ns->recv;
359 last_trans = ns->trans;
361 /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
362 sscanf(p, "%lld %*d %*d %*d %*d %*d %*d %*d %lld",
365 /* if recv or trans is less than last time, an overflow happened */
366 if (r < ns->last_read_recv) {
369 ns->recv += (r - ns->last_read_recv);
371 ns->last_read_recv = r;
373 if (t < ns->last_read_trans) {
376 ns->trans += (t - ns->last_read_trans);
378 ns->last_read_trans = t;
380 /*** ip addr patch ***/
381 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
383 conf.ifc_buf = malloc(sizeof(struct ifreq) * 16);
384 conf.ifc_len = sizeof(struct ifreq) * 16;
385 memset(conf.ifc_buf, 0, conf.ifc_len);
387 ioctl((long) i, SIOCGIFCONF, &conf);
389 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
390 struct net_stat *ns2;
392 if (!(((struct ifreq *) conf.ifc_buf) + k))
396 ((struct ifreq *) conf.ifc_buf)[k].ifr_ifrn.ifrn_name);
397 ns2->addr = ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.ifru_addr;
398 sprintf(temp_addr, "%u.%u.%u.%u, ",
399 ns2->addr.sa_data[2] & 255,
400 ns2->addr.sa_data[3] & 255,
401 ns2->addr.sa_data[4] & 255,
402 ns2->addr.sa_data[5] & 255);
403 if(NULL == strstr(ns2->addrs, temp_addr))
404 strncpy(ns2->addrs + strlen(ns2->addrs), temp_addr, 17);
411 /*** end ip addr patch ***/
414 /* calculate speeds */
415 ns->net_rec[0] = (ns->recv - last_recv) / delta;
416 ns->net_trans[0] = (ns->trans - last_trans) / delta;
423 #pragma omp parallel for reduction(+:curtmp1, curtmp2)
424 #endif /* HAVE_OPENMP */
425 for (i = 0; i < info.net_avg_samples; i++) {
426 curtmp1 = curtmp1 + ns->net_rec[i];
427 curtmp2 = curtmp2 + ns->net_trans[i];
435 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
436 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
437 if (info.net_avg_samples > 1) {
439 #pragma omp parallel for
440 #endif /* HAVE_OPENMP */
441 for (i = info.net_avg_samples; i > 1; i--) {
442 ns->net_rec[i - 1] = ns->net_rec[i - 2];
443 ns->net_trans[i - 1] = ns->net_trans[i - 2];
448 /* update wireless info */
449 winfo = malloc(sizeof(struct wireless_info));
450 memset(winfo, 0, sizeof(struct wireless_info));
452 skfd = iw_sockets_open();
453 if (iw_get_basic_config(skfd, s, &(winfo->b)) > -1) {
455 // set present winfo variables
456 if (iw_get_stats(skfd, s, &(winfo->stats),
457 &winfo->range, winfo->has_range) >= 0) {
458 winfo->has_stats = 1;
460 if (iw_get_range_info(skfd, s, &(winfo->range)) >= 0) {
461 winfo->has_range = 1;
463 if (iw_get_ext(skfd, s, SIOCGIWAP, &wrq) >= 0) {
464 winfo->has_ap_addr = 1;
465 memcpy(&(winfo->ap_addr), &(wrq.u.ap_addr), sizeof(sockaddr));
469 if (iw_get_ext(skfd, s, SIOCGIWRATE, &wrq) >= 0) {
470 memcpy(&(winfo->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
471 iw_print_bitrate(ns->bitrate, 16, winfo->bitrate.value);
476 if (winfo->has_range && winfo->has_stats
477 && ((winfo->stats.qual.level != 0)
478 || (winfo->stats.qual.updated & IW_QUAL_DBM))) {
479 if (!(winfo->stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
480 ns->link_qual = winfo->stats.qual.qual;
481 ns->link_qual_max = winfo->range.max_qual.qual;
486 if (winfo->has_ap_addr) {
487 iw_sawap_ntop(&winfo->ap_addr, ns->ap);
491 if (winfo->b.has_essid) {
492 if (winfo->b.essid_on) {
493 snprintf(ns->essid, 32, "%s", winfo->b.essid);
495 snprintf(ns->essid, 32, "off/any");
499 snprintf(ns->mode, 16, "%s", iw_operation_mode[winfo->b.mode]);
501 iw_sockets_close(skfd);
509 info.mask |= (1 << INFO_NET);
514 void update_total_processes(void)
518 struct sysinfo s_info;
521 info.procs = s_info.procs;
528 if (!(fp = open_file("/proc/loadavg", &rep))) {
532 fscanf(fp, "%*f %*f %*f %*d/%hu", &info.procs);
535 info.mask |= (1 << INFO_PROCS);
538 #define CPU_SAMPLE_COUNT 15
540 unsigned long long cpu_user;
541 unsigned long long cpu_system;
542 unsigned long long cpu_nice;
543 unsigned long long cpu_idle;
544 unsigned long long cpu_iowait;
545 unsigned long long cpu_irq;
546 unsigned long long cpu_softirq;
547 unsigned long long cpu_steal;
548 unsigned long long cpu_total;
549 unsigned long long cpu_active_total;
550 unsigned long long cpu_last_total;
551 unsigned long long cpu_last_active_total;
552 double cpu_val[CPU_SAMPLE_COUNT];
554 static short cpu_setup = 0;
556 /* Determine if this kernel gives us "extended" statistics information in
558 * Kernels around 2.5 and earlier only reported user, system, nice, and
559 * idle values in proc stat.
560 * Kernels around 2.6 and greater report these PLUS iowait, irq, softirq,
562 void determine_longstat(char *buf)
564 unsigned long long iowait = 0;
566 KFLAG_SETOFF(KFLAG_IS_LONGSTAT);
567 /* scanf will either return -1 or 1 because there is only 1 assignment */
568 if (sscanf(buf, "%*s %*d %*d %*d %*d %llu", &iowait) > 0) {
569 KFLAG_SETON(KFLAG_IS_LONGSTAT);
573 void get_cpu_count(void)
579 if (info.cpu_usage) {
583 if (!(stat_fp = open_file("/proc/stat", &rep))) {
589 while (!feof(stat_fp)) {
590 if (fgets(buf, 255, stat_fp) == NULL) {
594 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
595 if (info.cpu_count == 0) {
596 determine_longstat(buf);
601 info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
606 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
607 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
609 inline static void update_stat(void)
613 static struct cpu_info *cpu = NULL;
618 const char *stat_template = NULL;
619 unsigned int malloc_cpu_size = 0;
621 /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
622 if (!cpu_setup || !info.cpu_usage) {
627 if (!stat_template) {
629 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
633 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
634 cpu = malloc(malloc_cpu_size);
635 memset(cpu, 0, malloc_cpu_size);
638 if (!(stat_fp = open_file("/proc/stat", &rep))) {
640 if (info.cpu_usage) {
641 memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
647 while (!feof(stat_fp)) {
648 if (fgets(buf, 255, stat_fp) == NULL) {
652 if (strncmp(buf, "procs_running ", 14) == 0) {
653 sscanf(buf, "%*s %hu", &info.run_procs);
654 info.mask |= (1 << INFO_RUN_PROCS);
655 } else if (strncmp(buf, "cpu", 3) == 0) {
657 if (isdigit(buf[3])) {
658 idx = atoi(&buf[3]) + 1;
662 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
663 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
664 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
665 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
666 &(cpu[idx].cpu_steal));
668 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
669 cpu[idx].cpu_system + cpu[idx].cpu_idle +
670 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
671 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
673 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
674 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
675 info.mask |= (1 << INFO_CPU);
677 delta = current_update_time - last_update_time;
679 if (delta <= 0.001) {
683 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
684 cpu[idx].cpu_last_active_total) /
685 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
688 #pragma omp parallel for reduction(+:curtmp)
689 #endif /* HAVE_OPENMP */
690 for (i = 0; i < info.cpu_avg_samples; i++) {
691 curtmp = curtmp + cpu[idx].cpu_val[i];
693 /* TESTING -- I've removed this, because I don't think it is right.
694 * You shouldn't divide by the cpu count here ...
695 * removing for testing */
697 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
700 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
702 /* TESTING -- this line replaces the prev. "suspect" if/else */
703 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
705 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
706 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
708 #pragma omp parallel for
709 #endif /* HAVE_OPENMP */
710 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
711 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
718 void update_running_processes(void)
723 void update_cpu_usage(void)
728 void update_load_average(void)
730 #ifdef HAVE_GETLOADAVG
735 info.loadavg[0] = (float) v[0];
736 info.loadavg[1] = (float) v[1];
737 info.loadavg[2] = (float) v[2];
744 if (!(fp = open_file("/proc/loadavg", &rep))) {
745 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
748 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
752 info.mask |= (1 << INFO_LOADAVG);
755 #define PROC_I8K "/proc/i8k"
756 #define I8K_DELIM " "
757 static char *i8k_procbuf = NULL;
758 void update_i8k(void)
763 i8k_procbuf = (char *) malloc(128 * sizeof(char));
765 if ((fp = fopen(PROC_I8K, "r")) == NULL) {
766 CRIT_ERR("/proc/i8k doesn't exist! use insmod to make sure the kernel "
767 "driver is loaded...");
770 memset(&i8k_procbuf[0], 0, 128);
771 if (fread(&i8k_procbuf[0], sizeof(char), 128, fp) == 0) {
772 ERR("something wrong with /proc/i8k...");
777 i8k.version = strtok(&i8k_procbuf[0], I8K_DELIM);
778 i8k.bios = strtok(NULL, I8K_DELIM);
779 i8k.serial = strtok(NULL, I8K_DELIM);
780 i8k.cpu_temp = strtok(NULL, I8K_DELIM);
781 i8k.left_fan_status = strtok(NULL, I8K_DELIM);
782 i8k.right_fan_status = strtok(NULL, I8K_DELIM);
783 i8k.left_fan_rpm = strtok(NULL, I8K_DELIM);
784 i8k.right_fan_rpm = strtok(NULL, I8K_DELIM);
785 i8k.ac_status = strtok(NULL, I8K_DELIM);
786 i8k.buttons_status = strtok(NULL, I8K_DELIM);
789 /***********************************************************/
790 /***********************************************************/
791 /***********************************************************/
793 static int no_dots(const struct dirent *d)
795 if (d->d_name[0] == '.') {
801 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
803 struct dirent **namelist;
806 n = scandir(dir, &namelist, no_dots, alphasort);
809 ERR("scandir for %s: %s", dir, strerror(errno));
820 strncpy(s, namelist[0]->d_name, 255);
824 #pragma omp parallel for
825 #endif /* HAVE_OPENMP */
826 for (i = 0; i < n; i++) {
835 int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
836 int *divisor, char *devtype)
844 memset(buf, 0, sizeof(buf));
846 /* if device is NULL or *, get first */
847 if (dev == NULL || strcmp(dev, "*") == 0) {
850 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
856 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
858 /* buf holds result from get_first_file_in_a_directory() above,
859 * e.g. "hwmon0" -- append "/device" */
860 strcat(buf, "/device");
862 /* dev holds device number N as a string,
863 * e.g. "0", -- convert to "hwmon0/device" */
864 sprintf(buf, "hwmon%s/device", dev);
869 /* At least the acpitz hwmon doesn't have a 'device' subdir,
870 * so check it's existence and strip it from buf otherwise. */
871 snprintf(path, 255, "%s%s", dir, dev);
872 if (stat(path, &st)) {
873 buf[strlen(buf) - 7] = 0;
876 /* change vol to in, tempf to temp */
877 if (strcmp(type, "vol") == 0) {
879 } else if (strcmp(type, "tempf") == 0) {
883 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
884 strncpy(devtype, path, 255);
887 fd = open(path, O_RDONLY);
889 CRIT_ERR("can't open '%s': %s\nplease check your device or remove this "
890 "var from "PACKAGE_NAME, path, strerror(errno));
893 if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
894 || strcmp(type, "tempf") == 0) {
899 /* fan does not use *_div as a read divisor */
900 if (strcmp("fan", type) == 0) {
904 /* test if *_div file exist, open it and use it as divisor */
905 if (strcmp(type, "tempf") == 0) {
906 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
908 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
911 divfd = open(path, O_RDONLY);
917 divn = read(divfd, divbuf, 63);
918 /* should read until n == 0 but I doubt that kernel will give these
919 * in multiple pieces. :) */
921 ERR("open_sysfs_sensor(): can't read from sysfs");
924 *divisor = atoi(divbuf);
933 double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
941 lseek(*fd, 0, SEEK_SET);
947 n = read(*fd, buf, 63);
948 /* should read until n == 0 but I doubt that kernel will give these
949 * in multiple pieces. :) */
951 ERR("get_sysfs_info(): read from %s failed\n", devtype);
960 *fd = open(devtype, O_RDONLY);
962 ERR("can't open '%s': %s", devtype, strerror(errno));
965 /* My dirty hack for computing CPU value
966 * Filedil, from forums.gentoo.org */
967 /* if (strstr(devtype, "temp1_input") != NULL) {
968 return -15.096 + 1.4893 * (val / 1000.0);
971 /* divide voltage and temperature by 1000 */
972 /* or if any other divisor is given, use that */
973 if (strcmp(type, "tempf") == 0) {
975 return ((val / divisor + 40) * 9.0 / 5) - 40;
976 } else if (divisor) {
977 return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
979 return ((val + 40) * 9.0 / 5) - 40;
983 return val / divisor;
984 } else if (divisor) {
992 /* Prior to kernel version 2.6.12, the CPU fan speed was available in
993 * ADT746X_FAN_OLD, whereas later kernel versions provide this information in
995 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
996 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
998 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
1001 char adt746x_fan_state[64];
1004 if (!p_client_buffer || client_buffer_size <= 0) {
1008 if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
1009 && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL) {
1010 sprintf(adt746x_fan_state, "adt746x not found");
1012 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
1013 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
1017 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_fan_state);
1020 /* Prior to kernel version 2.6.12, the CPU temperature was found in
1021 * ADT746X_CPU_OLD, whereas later kernel versions provide this information in
1023 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
1024 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
1026 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
1029 char adt746x_cpu_state[64];
1032 if (!p_client_buffer || client_buffer_size <= 0) {
1036 if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
1037 && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL) {
1038 sprintf(adt746x_cpu_state, "adt746x not found");
1040 fscanf(fp, "%2s", adt746x_cpu_state);
1044 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state);
1047 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1048 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1050 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1051 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1052 const char *p_format, int divisor, unsigned int cpu)
1060 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1066 char current_freq_file[128];
1068 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1070 f = fopen(current_freq_file, "r");
1072 /* if there's a cpufreq /sys node, read the current frequency from
1073 * this node and divide by 1000 to get Mhz. */
1074 if (fgets(s, sizeof(s), f)) {
1075 s[strlen(s) - 1] = '\0';
1076 freq = strtod(s, NULL);
1079 snprintf(p_client_buffer, client_buffer_size, p_format,
1080 (freq / 1000) / divisor);
1085 // open the CPU information file
1086 f = open_file("/proc/cpuinfo", &rep);
1088 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1093 while (fgets(s, sizeof(s), f) != NULL) {
1095 #if defined(__i386) || defined(__x86_64)
1096 // and search for the cpu mhz
1097 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1099 #if defined(__alpha)
1100 // different on alpha
1101 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1103 // this is different on ppc for some reason
1104 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1105 #endif // defined(__alpha)
1106 #endif // defined(__i386) || defined(__x86_64)
1108 // copy just the number
1109 strcpy(frequency, strchr(s, ':') + 2);
1110 #if defined(__alpha)
1112 frequency[strlen(frequency) - 6] = '\0';
1113 // kernel reports in Hz
1114 freq = strtod(frequency, NULL) / 1000000;
1117 frequency[strlen(frequency) - 1] = '\0';
1118 freq = strtod(frequency, NULL);
1122 if (strncmp(s, "processor", 9) == 0) {
1129 snprintf(p_client_buffer, client_buffer_size, p_format,
1130 (float) freq / divisor);
1134 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1136 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1146 * Peter Tarjan (ptarjan@citromail.hu) */
1148 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1149 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1150 const char *p_format, int divisor, unsigned int cpu)
1156 char current_freq_file[128];
1159 /* build the voltage file name */
1161 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1164 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1169 /* read the current cpu frequency from the /sys node */
1170 f = fopen(current_freq_file, "r");
1172 if (fgets(s, sizeof(s), f)) {
1173 s[strlen(s) - 1] = '\0';
1174 freq = strtod(s, NULL);
1178 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1179 perror("get_voltage()");
1186 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1189 /* use the current cpu frequency to find the corresponding voltage */
1190 f = fopen(current_freq_file, "r");
1196 if (fgets(line, 255, f) == NULL) {
1199 sscanf(line, "%d %d", &freq_comp, &voltage);
1200 if (freq_comp == freq) {
1206 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1207 perror("get_voltage()");
1213 snprintf(p_client_buffer, client_buffer_size, p_format,
1214 (float) voltage / divisor);
1218 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1220 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1227 if (!p_client_buffer || client_buffer_size <= 0) {
1231 /* yeah, slow... :/ */
1232 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1233 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1237 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1239 fp = open_file(buf2, &rep);
1241 snprintf(p_client_buffer, client_buffer_size,
1242 "can't open fan's state file");
1245 memset(buf, 0, sizeof(buf));
1246 fscanf(fp, "%*s %99s", buf);
1249 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1252 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1253 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1254 /* Linux 2.6.25 onwards ac adapter info is in
1255 /sys/class/power_supply/AC/
1256 On my system I get the following.
1257 /sys/class/power_supply/AC/uevent:
1258 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1261 POWER_SUPPLY_NAME=AC
1262 POWER_SUPPLY_TYPE=Mains
1263 POWER_SUPPLY_ONLINE=1
1266 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1274 if (!p_client_buffer || client_buffer_size <= 0) {
1278 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1279 fp = open_file(buf2, &rep);
1281 /* sysfs processing */
1283 if (fgets(buf, sizeof(buf), fp) == NULL)
1286 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1288 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1289 snprintf(p_client_buffer, client_buffer_size,
1290 "%s-line", (online ? "on" : "off"));
1296 /* yeah, slow... :/ */
1297 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1298 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1302 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1304 fp = open_file(buf2, &rep);
1306 snprintf(p_client_buffer, client_buffer_size,
1307 "No ac adapter found.... where is it?");
1310 memset(buf, 0, sizeof(buf));
1311 fscanf(fp, "%*s %99s", buf);
1314 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1319 /proc/acpi/thermal_zone/THRM/cooling_mode
1320 cooling mode: active
1321 /proc/acpi/thermal_zone/THRM/polling_frequency
1323 /proc/acpi/thermal_zone/THRM/state
1325 /proc/acpi/thermal_zone/THRM/temperature
1327 /proc/acpi/thermal_zone/THRM/trip_points
1329 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1332 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1333 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1335 int open_acpi_temperature(const char *name)
1341 if (name == NULL || strcmp(name, "*") == 0) {
1344 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1350 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1352 fd = open(path, O_RDONLY);
1354 ERR("can't open '%s': %s", path, strerror(errno));
1360 static double last_acpi_temp;
1361 static double last_acpi_temp_time;
1363 double get_acpi_temperature(int fd)
1369 /* don't update acpi temperature too often */
1370 if (current_update_time - last_acpi_temp_time < 11.32) {
1371 return last_acpi_temp;
1373 last_acpi_temp_time = current_update_time;
1375 /* seek to beginning */
1376 lseek(fd, 0, SEEK_SET);
1383 n = read(fd, buf, 255);
1385 ERR("can't read fd %d: %s", fd, strerror(errno));
1388 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1392 return last_acpi_temp;
1396 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1398 design capacity: 4400 mAh
1399 last full capacity: 4064 mAh
1400 battery technology: rechargeable
1401 design voltage: 14800 mV
1402 design capacity warning: 300 mAh
1403 design capacity low: 200 mAh
1404 capacity granularity 1: 32 mAh
1405 capacity granularity 2: 32 mAh
1407 serial number: 16922
1413 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1416 charging state: unknown
1418 remaining capacity: 4064 mAh
1419 present voltage: 16608 mV
1423 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1424 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1425 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1426 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1427 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1429 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1430 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1432 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1433 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1436 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1437 Linux 2.6.24 onwards battery info is in
1438 /sys/class/power_supply/BAT0/
1439 On my system I get the following.
1440 /sys/class/power_supply/BAT0/uevent:
1441 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1443 PHYSDEVDRIVER=battery
1444 POWER_SUPPLY_NAME=BAT0
1445 POWER_SUPPLY_TYPE=Battery
1446 POWER_SUPPLY_STATUS=Discharging
1447 POWER_SUPPLY_PRESENT=1
1448 POWER_SUPPLY_TECHNOLOGY=Li-ion
1449 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1450 POWER_SUPPLY_VOLTAGE_NOW=10780000
1451 POWER_SUPPLY_CURRENT_NOW=13970000
1452 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1453 POWER_SUPPLY_ENERGY_FULL=27370000
1454 POWER_SUPPLY_ENERGY_NOW=11810000
1455 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1456 POWER_SUPPLY_MANUFACTURER=Panasonic
1457 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1460 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1461 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1462 #define APM_PATH "/proc/apm"
1463 #define MAX_BATTERY_COUNT 4
1465 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1466 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1467 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1469 static int batteries_initialized = 0;
1470 static char batteries[MAX_BATTERY_COUNT][32];
1472 static int acpi_last_full[MAX_BATTERY_COUNT];
1473 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1475 /* e.g. "charging 75%" */
1476 static char last_battery_str[MAX_BATTERY_COUNT][64];
1478 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1480 static double last_battery_time[MAX_BATTERY_COUNT];
1482 static int last_battery_perct[MAX_BATTERY_COUNT];
1483 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1485 void init_batteries(void)
1489 if (batteries_initialized) {
1493 #pragma omp parallel for
1494 #endif /* HAVE_OPENMP */
1495 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1496 batteries[idx][0] = '\0';
1498 batteries_initialized = 1;
1501 int get_battery_idx(const char *bat)
1505 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1506 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1511 /* if not found, enter a new entry */
1512 if (!strlen(batteries[idx])) {
1513 snprintf(batteries[idx], 31, "%s", bat);
1519 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1521 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1523 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1524 char acpi_path[128];
1525 char sysfs_path[128];
1527 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1528 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1532 idx = get_battery_idx(bat);
1534 /* don't update battery too often */
1535 if (current_update_time - last_battery_time[idx] < 29.5) {
1536 set_return_value(buffer, n, item, idx);
1540 last_battery_time[idx] = current_update_time;
1542 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1543 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1545 /* first try SYSFS if that fails try ACPI */
1547 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1548 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1551 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1552 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1555 if (sysfs_bat_fp[idx] != NULL) {
1557 int present_rate = -1;
1558 int remaining_capacity = -1;
1559 char charging_state[64];
1562 strcpy(charging_state, "unknown");
1564 while (!feof(sysfs_bat_fp[idx])) {
1566 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1569 /* let's just hope units are ok */
1570 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1571 strcpy(present, "yes");
1572 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1573 strcpy(present, "no");
1574 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1575 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1576 /* present_rate is not the same as the
1577 current flowing now but it is the same value
1578 which was used in the past. so we continue
1580 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1581 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1582 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1583 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1584 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1585 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1586 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1587 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1588 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1589 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1592 fclose(sysfs_bat_fp[idx]);
1593 sysfs_bat_fp[idx] = NULL;
1595 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1596 if (remaining_capacity > acpi_last_full[idx])
1597 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1600 if (strcmp(present, "No") == 0) {
1601 strncpy(last_battery_str[idx], "not present", 64);
1604 else if (strcmp(charging_state, "Charging") == 0) {
1605 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1606 /* e.g. charging 75% */
1607 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1608 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1610 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1611 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1612 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1613 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1614 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1615 snprintf(last_battery_time_str[idx],
1616 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1618 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1619 snprintf(last_battery_time_str[idx],
1620 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1624 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1625 if (present_rate > 0) {
1626 /* e.g. discharging 35% */
1627 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1628 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1630 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1631 (long) (((float) remaining_capacity / present_rate) * 3600));
1632 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1633 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1634 snprintf(last_battery_time_str[idx],
1635 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1637 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1639 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1640 snprintf(last_battery_time_str[idx],
1641 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1645 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1646 else if (strncmp(charging_state, "Charged", 64) == 0) {
1647 /* Below happens with the second battery on my X40,
1648 * when the second one is empty and the first one
1650 if (remaining_capacity == 0)
1651 strcpy(last_battery_str[idx], "empty");
1653 strcpy(last_battery_str[idx], "charged");
1655 /* unknown, probably full / AC */
1657 if (acpi_last_full[idx] != 0
1658 && remaining_capacity != acpi_last_full[idx])
1659 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1660 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1662 strncpy(last_battery_str[idx], "AC", 64);
1664 } else if (acpi_bat_fp[idx] != NULL) {
1666 int present_rate = -1;
1667 int remaining_capacity = -1;
1668 char charging_state[64];
1671 /* read last full capacity if it's zero */
1672 if (acpi_last_full[idx] == 0) {
1673 static int rep3 = 0;
1677 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1678 fp = open_file(path, &rep3);
1683 if (fgets(b, 256, fp) == NULL) {
1686 if (sscanf(b, "last full capacity: %d",
1687 &acpi_last_full[idx]) != 0) {
1696 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1698 strcpy(charging_state, "unknown");
1700 while (!feof(acpi_bat_fp[idx])) {
1703 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1707 /* let's just hope units are ok */
1708 if (strncmp(buf, "present:", 8) == 0) {
1709 sscanf(buf, "present: %4s", present);
1710 } else if (strncmp(buf, "charging state:", 15) == 0) {
1711 sscanf(buf, "charging state: %63s", charging_state);
1712 } else if (strncmp(buf, "present rate:", 13) == 0) {
1713 sscanf(buf, "present rate: %d", &present_rate);
1714 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1715 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1718 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1719 if (remaining_capacity > acpi_last_full[idx]) {
1720 /* normalize to 100% */
1721 acpi_last_full[idx] = remaining_capacity;
1725 if (strcmp(present, "no") == 0) {
1726 strncpy(last_battery_str[idx], "not present", 64);
1728 } else if (strcmp(charging_state, "charging") == 0) {
1729 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1730 /* e.g. charging 75% */
1731 snprintf(last_battery_str[idx],
1732 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1733 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1735 format_seconds(last_battery_time_str[idx],
1736 sizeof(last_battery_time_str[idx]) - 1,
1737 (long) (((acpi_last_full[idx] - remaining_capacity) *
1738 3600) / present_rate));
1739 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1740 snprintf(last_battery_str[idx],
1741 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1742 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1743 snprintf(last_battery_time_str[idx],
1744 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1746 strncpy(last_battery_str[idx], "charging",
1747 sizeof(last_battery_str[idx]) - 1);
1748 snprintf(last_battery_time_str[idx],
1749 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1752 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1753 if (present_rate > 0) {
1754 /* e.g. discharging 35% */
1755 snprintf(last_battery_str[idx],
1756 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1757 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1759 format_seconds(last_battery_time_str[idx],
1760 sizeof(last_battery_time_str[idx]) - 1,
1761 (long) ((remaining_capacity * 3600) / present_rate));
1762 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1763 snprintf(last_battery_str[idx],
1764 sizeof(last_battery_str[idx]) - 1, "full");
1765 snprintf(last_battery_time_str[idx],
1766 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1768 snprintf(last_battery_str[idx],
1769 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1770 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1771 snprintf(last_battery_time_str[idx],
1772 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1775 } else if (strncmp(charging_state, "charged", 64) == 0) {
1776 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1777 /* Below happens with the second battery on my X40,
1778 * when the second one is empty and the first one being charged. */
1779 if (remaining_capacity == 0) {
1780 strcpy(last_battery_str[idx], "empty");
1782 strcpy(last_battery_str[idx], "charged");
1784 /* unknown, probably full / AC */
1786 if (strncmp(charging_state, "Full", 64) == 0) {
1787 strncpy(last_battery_str[idx], "full", 64);
1788 } else if (acpi_last_full[idx] != 0
1789 && remaining_capacity != acpi_last_full[idx]) {
1790 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1791 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1793 strncpy(last_battery_str[idx], "AC", 64);
1798 if (apm_bat_fp[idx] == NULL) {
1799 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1802 if (apm_bat_fp[idx] != NULL) {
1803 unsigned int ac, status, flag;
1806 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1807 &ac, &status, &flag, &life);
1810 /* could check now that there is ac */
1811 snprintf(last_battery_str[idx], 64, "AC");
1813 /* could check that status == 3 here? */
1814 } else if (ac && life != 100) {
1815 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1817 snprintf(last_battery_str[idx], 64, "%d%%", life);
1820 /* it seemed to buffer it so file must be closed (or could use
1821 * syscalls directly but I don't feel like coding it now) */
1822 fclose(apm_bat_fp[idx]);
1823 apm_bat_fp[idx] = NULL;
1826 set_return_value(buffer, n, item, idx);
1829 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1832 case BATTERY_STATUS:
1833 snprintf(buffer, n, "%s", last_battery_str[idx]);
1836 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1843 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1845 get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1846 if (0 == strncmp("charging", buffer, 8)) {
1848 memmove(buffer + 1, buffer + 8, n - 8);
1849 } else if (0 == strncmp("discharging", buffer, 11)) {
1851 memmove(buffer + 1, buffer + 11, n - 11);
1855 int get_battery_perct(const char *bat)
1859 char acpi_path[128];
1860 char sysfs_path[128];
1861 int remaining_capacity = -1;
1863 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1864 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1868 idx = get_battery_idx(bat);
1870 /* don't update battery too often */
1871 if (current_update_time - last_battery_perct_time[idx] < 30) {
1872 return last_battery_perct[idx];
1874 last_battery_perct_time[idx] = current_update_time;
1876 /* Only check for SYSFS or ACPI */
1878 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1879 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1883 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1884 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1887 if (sysfs_bat_fp[idx] != NULL) {
1889 while (!feof(sysfs_bat_fp[idx])) {
1891 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1894 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1895 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1896 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1897 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1898 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1899 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1900 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1901 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1905 fclose(sysfs_bat_fp[idx]);
1906 sysfs_bat_fp[idx] = NULL;
1908 } else if (acpi_bat_fp[idx] != NULL) {
1910 /* read last full capacity if it's zero */
1911 if (acpi_design_capacity[idx] == 0) {
1916 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1917 fp = open_file(path, &rep2);
1922 if (fgets(b, 256, fp) == NULL) {
1925 if (sscanf(b, "last full capacity: %d",
1926 &acpi_design_capacity[idx]) != 0) {
1934 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1936 while (!feof(acpi_bat_fp[idx])) {
1939 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1943 if (buf[0] == 'r') {
1944 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1948 if (remaining_capacity < 0) {
1951 /* compute the battery percentage */
1952 last_battery_perct[idx] =
1953 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
1954 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
1955 return last_battery_perct[idx];
1958 int get_battery_perct_bar(const char *bar)
1962 get_battery_perct(bar);
1963 idx = get_battery_idx(bar);
1964 return (int) (last_battery_perct[idx] * 2.56 - 1);
1967 /* On Apple powerbook and ibook:
1968 $ cat /proc/pmu/battery_0
1975 $ cat /proc/pmu/info
1976 PMU driver version : 2
1977 PMU firmware version : 0c
1982 /* defines as in <linux/pmu.h> */
1983 #define PMU_BATT_PRESENT 0x00000001
1984 #define PMU_BATT_CHARGING 0x00000002
1986 static FILE *pmu_battery_fp;
1987 static FILE *pmu_info_fp;
1988 static char pb_battery_info[3][32];
1989 static double pb_battery_info_update;
1991 #define PMU_PATH "/proc/pmu"
1992 void get_powerbook_batt_info(char *buffer, size_t n, int i)
1995 const char *batt_path = PMU_PATH "/battery_0";
1996 const char *info_path = PMU_PATH "/info";
1998 int charge, max_charge, ac = -1;
2001 /* don't update battery too often */
2002 if (current_update_time - pb_battery_info_update < 29.5) {
2003 snprintf(buffer, n, "%s", pb_battery_info[i]);
2006 pb_battery_info_update = current_update_time;
2008 if (pmu_battery_fp == NULL) {
2009 pmu_battery_fp = open_file(batt_path, &rep);
2010 if (pmu_battery_fp == NULL) {
2015 if (pmu_battery_fp != NULL) {
2016 rewind(pmu_battery_fp);
2017 while (!feof(pmu_battery_fp)) {
2020 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2024 if (buf[0] == 'f') {
2025 sscanf(buf, "flags : %8x", &flags);
2026 } else if (buf[0] == 'c' && buf[1] == 'h') {
2027 sscanf(buf, "charge : %d", &charge);
2028 } else if (buf[0] == 'm') {
2029 sscanf(buf, "max_charge : %d", &max_charge);
2030 } else if (buf[0] == 't') {
2031 sscanf(buf, "time rem. : %ld", &timeval);
2035 if (pmu_info_fp == NULL) {
2036 pmu_info_fp = open_file(info_path, &rep);
2037 if (pmu_info_fp == NULL) {
2042 if (pmu_info_fp != NULL) {
2043 rewind(pmu_info_fp);
2044 while (!feof(pmu_info_fp)) {
2047 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2050 if (buf[0] == 'A') {
2051 sscanf(buf, "AC Power : %d", &ac);
2055 /* update status string */
2056 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2057 strncpy(pb_battery_info[PB_BATT_STATUS], "AC", sizeof(pb_battery_info[PB_BATT_STATUS]));
2058 } else if (ac && (flags & PMU_BATT_PRESENT)
2059 && !(flags & PMU_BATT_CHARGING)) {
2060 strncpy(pb_battery_info[PB_BATT_STATUS], "charged", sizeof(pb_battery_info[PB_BATT_STATUS]));
2061 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2062 strncpy(pb_battery_info[PB_BATT_STATUS], "charging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2064 strncpy(pb_battery_info[PB_BATT_STATUS], "discharging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2067 /* update percentage string */
2068 if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT)
2069 && !(flags & PMU_BATT_CHARGING)) {
2070 snprintf(pb_battery_info[PB_BATT_PERCENT],
2071 sizeof(pb_battery_info[PB_BATT_PERCENT]), "100%%");
2072 } else if (timeval == 0) {
2073 snprintf(pb_battery_info[PB_BATT_PERCENT],
2074 sizeof(pb_battery_info[PB_BATT_PERCENT]), "unknown");
2076 snprintf(pb_battery_info[PB_BATT_PERCENT],
2077 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2078 (charge * 100) / max_charge);
2081 /* update time string */
2082 if (timeval == 0) { /* fully charged or battery not present */
2083 snprintf(pb_battery_info[PB_BATT_TIME],
2084 sizeof(pb_battery_info[PB_BATT_TIME]), "unknown");
2085 } else if (timeval < 60 * 60) { /* don't show secs */
2086 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2087 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2089 format_seconds(pb_battery_info[PB_BATT_TIME],
2090 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2093 snprintf(buffer, n, "%s", pb_battery_info[i]);
2096 void update_top(void)
2098 process_find_top(info.cpu, info.memu, info.time);
2099 info.first_process = get_first_process();
2102 void update_entropy(void)
2105 const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2106 const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2109 info.entropy.entropy_avail = 0;
2110 info.entropy.poolsize = 0;
2112 if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2116 if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2121 fscanf(fp1, "%u", &info.entropy.entropy_avail);
2122 fscanf(fp2, "%u", &info.entropy.poolsize);
2127 info.mask |= (1 << INFO_ENTROPY);
2130 const char *get_disk_protect_queue(const char *disk)
2136 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2137 if (access(path, F_OK)) {
2138 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2140 if ((fp = fopen(path, "r")) == NULL)
2142 if (fscanf(fp, "%d\n", &state) != 1) {
2147 return (state > 0) ? "frozen" : "free ";
2150 void update_diskio(void)
2154 char buf[512], devbuf[64];
2155 unsigned int major, minor;
2157 struct diskio_stat *cur;
2158 unsigned int reads, writes;
2159 unsigned int total_reads = 0, total_writes = 0;
2162 stats.current_read = 0;
2163 stats.current_write = 0;
2165 if (!(fp = open_file("/proc/diskstats", &rep))) {
2169 /* read reads and writes from all disks (minor = 0), including cd-roms
2170 * and floppies, and sum them up */
2171 while (fgets(buf, 512, fp)) {
2172 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
2173 &minor, devbuf, &reads, &writes);
2174 /* ignore subdevices (they have only 3 matching entries in their line)
2175 * and virtual devices (LVM, network block devices, RAM disks, Loopback)
2177 * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
2178 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
2179 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
2180 total_reads += reads;
2181 total_writes += writes;
2183 col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
2184 &major, &minor, devbuf, &reads, &writes);
2185 if (col_count != 5) {
2190 while (cur && strcmp(devbuf, cur->dev))
2194 update_diskio_values(cur, reads, writes);
2196 update_diskio_values(&stats, total_reads, total_writes);