1 /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
2 * vim: ts=4 sw=4 noet ai cindent syntax=c
4 * Conky, a system monitor, based on torsmo
6 * Any original torsmo code is licensed under the BSD license
8 * All code written since the fork of torsmo is licensed under the GPL
10 * Please see COPYING for details
12 * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
13 * Copyright (c) 2007 Toni Spets
14 * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
16 * All rights reserved.
18 * This program is free software: you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation, either version 3 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
38 #include "temphelper.h"
43 #include <sys/types.h>
44 #include <sys/sysinfo.h>
46 #ifndef HAVE_CLOCK_GETTIME
51 // #include <assert.h>
55 #include <sys/ioctl.h>
56 #include <sys/socket.h>
57 #include <netinet/in.h>
58 #include <linux/sockios.h>
60 #include <arpa/inet.h>
64 #include <linux/route.h>
67 /* The following ifdefs were adapted from gkrellm */
68 #include <linux/major.h>
70 #if !defined(MD_MAJOR)
74 #if !defined(LVM_BLK_MAJOR)
75 #define LVM_BLK_MAJOR 58
78 #if !defined(NBD_MAJOR)
94 #define SHORTSTAT_TEMPL "%*s %llu %llu %llu"
95 #define LONGSTAT_TEMPL "%*s %llu %llu %llu "
97 /* This flag tells the linux routines to use the /proc system where possible,
98 * even if other api's are available, e.g. sysinfo() or getloadavg().
99 * the reason for this is to allow for /proc-based distributed monitoring.
100 * using a flag in this manner creates less confusing code. */
101 static int prefer_proc = 0;
103 void prepare_update(void)
107 void update_uptime(void)
111 struct sysinfo s_info;
114 info.uptime = (double) s_info.uptime;
121 if (!(fp = open_file("/proc/uptime", &rep))) {
125 fscanf(fp, "%lf", &info.uptime);
130 int check_mount(char *s)
133 FILE *mtab = fopen("/etc/mtab", "r");
136 char buf1[256], buf2[128];
138 while (fgets(buf1, 256, mtab)) {
139 sscanf(buf1, "%*s %128s", buf2);
140 if (!strcmp(s, buf2)) {
147 NORM_ERR("Could not open mtab");
152 /* these things are also in sysinfo except Buffers:
153 * (that's why I'm reading them from proc) */
155 void update_meminfo(void)
160 /* unsigned int a; */
163 info.mem = info.memmax = info.swap = info.swapfree = info.swapmax = info.bufmem =
164 info.buffers = info.cached = info.memfree = info.memeasyfree = 0;
166 if (!(meminfo_fp = open_file("/proc/meminfo", &rep))) {
170 while (!feof(meminfo_fp)) {
171 if (fgets(buf, 255, meminfo_fp) == NULL) {
175 if (strncmp(buf, "MemTotal:", 9) == 0) {
176 sscanf(buf, "%*s %llu", &info.memmax);
177 } else if (strncmp(buf, "MemFree:", 8) == 0) {
178 sscanf(buf, "%*s %llu", &info.memfree);
179 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
180 sscanf(buf, "%*s %llu", &info.swapmax);
181 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
182 sscanf(buf, "%*s %llu", &info.swapfree);
183 } else if (strncmp(buf, "Buffers:", 8) == 0) {
184 sscanf(buf, "%*s %llu", &info.buffers);
185 } else if (strncmp(buf, "Cached:", 7) == 0) {
186 sscanf(buf, "%*s %llu", &info.cached);
190 info.mem = info.memmax - info.memfree;
191 info.memeasyfree = info.memfree;
192 info.swap = info.swapmax - info.swapfree;
194 info.bufmem = info.cached + info.buffers;
199 int get_laptop_mode(void)
204 if ((fp = fopen("/proc/sys/vm/laptop_mode", "r")) != NULL)
205 fscanf(fp, "%d\n", &val);
211 * # cat /sys/block/sda/queue/scheduler
212 * noop [anticipatory] cfq
214 char *get_ioscheduler(char *disk)
220 return strndup("n/a", text_buffer_size);
222 snprintf(buf, 127, "/sys/block/%s/queue/scheduler", disk);
223 if ((fp = fopen(buf, "r")) == NULL) {
224 return strndup("n/a", text_buffer_size);
227 fscanf(fp, "%127s", buf);
229 buf[strlen(buf) - 1] = '\0';
231 return strndup(buf + 1, text_buffer_size);
235 return strndup("n/a", text_buffer_size);
244 #define COND_FREE(x) if(x) free(x); x = 0
245 #define SAVE_SET_STRING(x, y) \
246 if (x && strcmp((char *)x, (char *)y)) { \
248 x = strndup("multiple", text_buffer_size); \
250 x = strndup(y, text_buffer_size); \
253 void update_gateway_info_failure(const char *reason)
258 //2 pointers to 1 location causes a crash when we try to free them both
259 gw_info.iface = strndup("failed", text_buffer_size);
260 gw_info.ip = strndup("failed", text_buffer_size);
264 /* Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT */
265 #define RT_ENTRY_FORMAT "%63s %lx %lx %x %*d %*d %*d %lx %*d %*d %*d\n"
267 void update_gateway_info(void)
272 unsigned long dest, gate, mask;
275 COND_FREE(gw_info.iface);
276 COND_FREE(gw_info.ip);
279 if ((fp = fopen("/proc/net/route", "r")) == NULL) {
280 update_gateway_info_failure("fopen()");
284 /* skip over the table header line, which is always present */
285 fscanf(fp, "%*[^\n]\n");
288 if(fscanf(fp, RT_ENTRY_FORMAT,
289 iface, &dest, &gate, &flags, &mask) != 5) {
290 update_gateway_info_failure("fscanf()");
293 if (!(dest || mask) && ((flags & RTF_GATEWAY) || !gate) ) {
295 SAVE_SET_STRING(gw_info.iface, iface)
297 SAVE_SET_STRING(gw_info.ip, inet_ntoa(ina))
304 void free_gateway_info(void)
310 memset(&gw_info, 0, sizeof(gw_info));
313 int gateway_exists(void)
315 return !!gw_info.count;
318 void print_gateway_iface(char *p, int p_max_size)
320 snprintf(p, p_max_size, "%s", gw_info.iface);
323 void print_gateway_ip(char *p, int p_max_size)
325 snprintf(p, p_max_size, "%s", gw_info.ip);
328 void update_net_stats(void)
332 static char first = 1;
334 // FIXME: arbitrary size chosen to keep code simple.
336 unsigned int curtmp1, curtmp2;
343 // wireless info variables
344 int skfd, has_bitrate = 0;
345 struct wireless_info *winfo;
350 delta = current_update_time - last_update_time;
351 if (delta <= 0.0001) {
355 /* open file and ignore first two lines */
356 if (!(net_dev_fp = open_file("/proc/net/dev", &rep))) {
361 fgets(buf, 255, net_dev_fp); /* garbage */
362 fgets(buf, 255, net_dev_fp); /* garbage (field names) */
364 /* read each interface */
365 for (i2 = 0; i2 < MAX_NET_INTERFACES; i2++) {
369 long long r, t, last_recv, last_trans;
371 if (fgets(buf, 255, net_dev_fp) == NULL) {
375 while (isspace((int) *p)) {
381 while (*p && *p != ':') {
390 ns = get_net_stat(s, NULL, NULL);
392 memset(&(ns->addr.sa_data), 0, 14);
394 memset(ns->addrs, 0, 17 * MAX_NET_INTERFACES + 1); /* Up to 17 chars per ip, max MAX_NET_INTERFACES interfaces. Nasty memory usage... */
396 last_recv = ns->recv;
397 last_trans = ns->trans;
399 /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
400 sscanf(p, "%lld %*d %*d %*d %*d %*d %*d %*d %lld",
403 /* if recv or trans is less than last time, an overflow happened */
404 if (r < ns->last_read_recv) {
407 ns->recv += (r - ns->last_read_recv);
409 ns->last_read_recv = r;
411 if (t < ns->last_read_trans) {
414 ns->trans += (t - ns->last_read_trans);
416 ns->last_read_trans = t;
418 /*** ip addr patch ***/
419 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
421 conf.ifc_buf = malloc(sizeof(struct ifreq) * MAX_NET_INTERFACES);
422 conf.ifc_len = sizeof(struct ifreq) * MAX_NET_INTERFACES;
423 memset(conf.ifc_buf, 0, conf.ifc_len);
425 ioctl((long) i, SIOCGIFCONF, &conf);
427 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
428 struct net_stat *ns2;
430 if (!(((struct ifreq *) conf.ifc_buf) + k))
434 ((struct ifreq *) conf.ifc_buf)[k].ifr_ifrn.ifrn_name, NULL, NULL);
435 ns2->addr = ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.ifru_addr;
436 sprintf(temp_addr, "%u.%u.%u.%u, ",
437 ns2->addr.sa_data[2] & 255,
438 ns2->addr.sa_data[3] & 255,
439 ns2->addr.sa_data[4] & 255,
440 ns2->addr.sa_data[5] & 255);
441 if(NULL == strstr(ns2->addrs, temp_addr))
442 strncpy(ns2->addrs + strlen(ns2->addrs), temp_addr, 17);
449 /*** end ip addr patch ***/
452 /* calculate speeds */
453 ns->net_rec[0] = (ns->recv - last_recv) / delta;
454 ns->net_trans[0] = (ns->trans - last_trans) / delta;
461 #pragma omp parallel for reduction(+:curtmp1, curtmp2) schedule(dynamic,10)
462 #endif /* HAVE_OPENMP */
463 for (i = 0; i < info.net_avg_samples; i++) {
464 curtmp1 = curtmp1 + ns->net_rec[i];
465 curtmp2 = curtmp2 + ns->net_trans[i];
467 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
468 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
469 if (info.net_avg_samples > 1) {
471 #pragma omp parallel for schedule(dynamic,10)
472 #endif /* HAVE_OPENMP */
473 for (i = info.net_avg_samples; i > 1; i--) {
474 ns->net_rec[i - 1] = ns->net_rec[i - 2];
475 ns->net_trans[i - 1] = ns->net_trans[i - 2];
480 /* update wireless info */
481 winfo = malloc(sizeof(struct wireless_info));
482 memset(winfo, 0, sizeof(struct wireless_info));
484 skfd = iw_sockets_open();
485 if (iw_get_basic_config(skfd, s, &(winfo->b)) > -1) {
487 // set present winfo variables
488 if (iw_get_stats(skfd, s, &(winfo->stats),
489 &winfo->range, winfo->has_range) >= 0) {
490 winfo->has_stats = 1;
492 if (iw_get_range_info(skfd, s, &(winfo->range)) >= 0) {
493 winfo->has_range = 1;
495 if (iw_get_ext(skfd, s, SIOCGIWAP, &wrq) >= 0) {
496 winfo->has_ap_addr = 1;
497 memcpy(&(winfo->ap_addr), &(wrq.u.ap_addr), sizeof(sockaddr));
501 if (iw_get_ext(skfd, s, SIOCGIWRATE, &wrq) >= 0) {
502 memcpy(&(winfo->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
503 iw_print_bitrate(ns->bitrate, 16, winfo->bitrate.value);
508 if (winfo->has_range && winfo->has_stats
509 && ((winfo->stats.qual.level != 0)
510 || (winfo->stats.qual.updated & IW_QUAL_DBM))) {
511 if (!(winfo->stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
512 ns->link_qual = winfo->stats.qual.qual;
513 ns->link_qual_max = winfo->range.max_qual.qual;
518 if (winfo->has_ap_addr) {
519 iw_sawap_ntop(&winfo->ap_addr, ns->ap);
523 if (winfo->b.has_essid) {
524 if (winfo->b.essid_on) {
525 snprintf(ns->essid, 32, "%s", winfo->b.essid);
527 snprintf(ns->essid, 32, "off/any");
531 snprintf(ns->mode, 16, "%s", iw_operation_mode[winfo->b.mode]);
533 iw_sockets_close(skfd);
544 void update_total_processes(void)
547 struct dirent *entry;
552 if (!(dir = opendir("/proc"))) {
555 while ((entry = readdir(dir))) {
557 /* Problem reading list of processes */
562 if (sscanf(entry->d_name, "%d%c", &ignore1, &ignore2) == 1) {
569 void update_threads(void)
573 struct sysinfo s_info;
576 info.threads = s_info.procs;
583 if (!(fp = open_file("/proc/loadavg", &rep))) {
587 fscanf(fp, "%*f %*f %*f %*d/%hu", &info.threads);
592 #define CPU_SAMPLE_COUNT 15
594 unsigned long long cpu_user;
595 unsigned long long cpu_system;
596 unsigned long long cpu_nice;
597 unsigned long long cpu_idle;
598 unsigned long long cpu_iowait;
599 unsigned long long cpu_irq;
600 unsigned long long cpu_softirq;
601 unsigned long long cpu_steal;
602 unsigned long long cpu_total;
603 unsigned long long cpu_active_total;
604 unsigned long long cpu_last_total;
605 unsigned long long cpu_last_active_total;
606 double cpu_val[CPU_SAMPLE_COUNT];
608 static short cpu_setup = 0;
610 /* Determine if this kernel gives us "extended" statistics information in
612 * Kernels around 2.5 and earlier only reported user, system, nice, and
613 * idle values in proc stat.
614 * Kernels around 2.6 and greater report these PLUS iowait, irq, softirq,
616 void determine_longstat(char *buf)
618 unsigned long long iowait = 0;
620 KFLAG_SETOFF(KFLAG_IS_LONGSTAT);
621 /* scanf will either return -1 or 1 because there is only 1 assignment */
622 if (sscanf(buf, "%*s %*d %*d %*d %*d %llu", &iowait) > 0) {
623 KFLAG_SETON(KFLAG_IS_LONGSTAT);
627 void get_cpu_count(void)
633 if (info.cpu_usage) {
637 if (!(stat_fp = open_file("/proc/stat", &rep))) {
643 while (!feof(stat_fp)) {
644 if (fgets(buf, 255, stat_fp) == NULL) {
648 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
649 if (info.cpu_count == 0) {
650 determine_longstat(buf);
655 info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
660 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
661 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
663 void update_stat(void)
667 static struct cpu_info *cpu = NULL;
672 const char *stat_template = NULL;
673 unsigned int malloc_cpu_size = 0;
674 extern void* global_cpu;
675 static double last_stat_update = 0.0;
677 /* since we use wrappers for this function, the update machinery
678 * can't eliminate double invocations of this function. Check for
679 * them here, otherwise cpu_usage counters are freaking out. */
680 if (last_stat_update == current_update_time)
682 last_stat_update = current_update_time;
684 /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
685 if (!cpu_setup || !info.cpu_usage) {
690 if (!stat_template) {
692 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
696 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
697 cpu = malloc(malloc_cpu_size);
698 memset(cpu, 0, malloc_cpu_size);
702 if (!(stat_fp = open_file("/proc/stat", &rep))) {
703 info.run_threads = 0;
704 if (info.cpu_usage) {
705 memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
711 while (!feof(stat_fp)) {
712 if (fgets(buf, 255, stat_fp) == NULL) {
716 if (strncmp(buf, "procs_running ", 14) == 0) {
717 sscanf(buf, "%*s %hu", &info.run_threads);
718 } else if (strncmp(buf, "cpu", 3) == 0) {
720 if (isdigit(buf[3])) {
721 idx = atoi(&buf[3]) + 1;
725 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
726 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
727 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
728 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
729 &(cpu[idx].cpu_steal));
731 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
732 cpu[idx].cpu_system + cpu[idx].cpu_idle +
733 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
734 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
736 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
737 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
739 delta = current_update_time - last_update_time;
741 if (delta <= 0.001) {
745 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
746 cpu[idx].cpu_last_active_total) /
747 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
750 #pragma omp parallel for reduction(+:curtmp) schedule(dynamic,10)
751 #endif /* HAVE_OPENMP */
752 for (i = 0; i < info.cpu_avg_samples; i++) {
753 curtmp = curtmp + cpu[idx].cpu_val[i];
755 /* TESTING -- I've removed this, because I don't think it is right.
756 * You shouldn't divide by the cpu count here ...
757 * removing for testing */
759 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
762 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
764 /* TESTING -- this line replaces the prev. "suspect" if/else */
765 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
767 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
768 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
770 #pragma omp parallel for schedule(dynamic,10)
771 #endif /* HAVE_OPENMP */
772 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
773 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
780 void update_running_processes(void)
785 void update_cpu_usage(void)
790 void update_load_average(void)
792 #ifdef HAVE_GETLOADAVG
797 info.loadavg[0] = (float) v[0];
798 info.loadavg[1] = (float) v[1];
799 info.loadavg[2] = (float) v[2];
806 if (!(fp = open_file("/proc/loadavg", &rep))) {
807 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
810 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
816 /***********************************************************/
817 /***********************************************************/
818 /***********************************************************/
820 static int no_dots(const struct dirent *d)
822 if (d->d_name[0] == '.') {
828 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
830 struct dirent **namelist;
833 n = scandir(dir, &namelist, no_dots, alphasort);
836 NORM_ERR("scandir for %s: %s", dir, strerror(errno));
847 strncpy(s, namelist[0]->d_name, 255);
851 #pragma omp parallel for schedule(dynamic,10)
852 #endif /* HAVE_OPENMP */
853 for (i = 0; i < n; i++) {
862 static int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
863 int *divisor, char *devtype)
870 memset(buf, 0, sizeof(buf));
872 /* if device is NULL or *, get first */
873 if (dev == NULL || strcmp(dev, "*") == 0) {
876 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
882 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
884 /* buf holds result from get_first_file_in_a_directory() above,
885 * e.g. "hwmon0" -- append "/device" */
886 strcat(buf, "/device");
888 /* dev holds device number N as a string,
889 * e.g. "0", -- convert to "hwmon0/device" */
890 sprintf(buf, "hwmon%s/device", dev);
895 /* change vol to in, tempf to temp */
896 if (strcmp(type, "vol") == 0) {
898 } else if (strcmp(type, "tempf") == 0) {
903 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
905 /* first, attempt to open file in /device */
906 fd = open(path, O_RDONLY);
909 /* if it fails, strip the /device from dev and attempt again */
910 buf[strlen(buf) - 7] = 0;
911 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
912 fd = open(path, O_RDONLY);
914 CRIT_ERR(NULL, NULL, "can't open '%s': %s\nplease check your device or remove this "
915 "var from "PACKAGE_NAME, path, strerror(errno));
919 strncpy(devtype, path, 255);
921 if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
922 || strcmp(type, "tempf") == 0) {
927 /* fan does not use *_div as a read divisor */
928 if (strcmp("fan", type) == 0) {
932 /* test if *_div file exist, open it and use it as divisor */
933 if (strcmp(type, "tempf") == 0) {
934 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
936 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
939 divfd = open(path, O_RDONLY);
945 divn = read(divfd, divbuf, 63);
946 /* should read until n == 0 but I doubt that kernel will give these
947 * in multiple pieces. :) */
949 NORM_ERR("open_sysfs_sensor(): can't read from sysfs");
952 *divisor = atoi(divbuf);
960 static double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
968 lseek(*fd, 0, SEEK_SET);
974 n = read(*fd, buf, 63);
975 /* should read until n == 0 but I doubt that kernel will give these
976 * in multiple pieces. :) */
978 NORM_ERR("get_sysfs_info(): read from %s failed\n", devtype);
987 *fd = open(devtype, O_RDONLY);
989 NORM_ERR("can't open '%s': %s", devtype, strerror(errno));
992 /* My dirty hack for computing CPU value
993 * Filedil, from forums.gentoo.org */
994 /* if (strstr(devtype, "temp1_input") != NULL) {
995 return -15.096 + 1.4893 * (val / 1000.0);
998 /* divide voltage and temperature by 1000 */
999 /* or if any other divisor is given, use that */
1000 if (strcmp(type, "tempf") == 0) {
1002 return ((val / divisor + 40) * 9.0 / 5) - 40;
1003 } else if (divisor) {
1004 return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
1006 return ((val + 40) * 9.0 / 5) - 40;
1010 return val / divisor;
1011 } else if (divisor) {
1012 return val / 1000.0;
1019 #define HWMON_RESET() {\
1024 static void parse_sysfs_sensor(struct text_object *obj, const char *arg, const char *path, const char *type)
1026 char buf1[64], buf2[64];
1027 float factor, offset;
1031 if (sscanf(arg, "%63s %d %f %f", buf2, &n, &factor, &offset) == 4) found = 1; else HWMON_RESET();
1032 if (!found && sscanf(arg, "%63s %63s %d %f %f", buf1, buf2, &n, &factor, &offset) == 5) found = 1; else if (!found) HWMON_RESET();
1033 if (!found && sscanf(arg, "%63s %63s %d", buf1, buf2, &n) == 3) found = 1; else if (!found) HWMON_RESET();
1034 if (!found && sscanf(arg, "%63s %d", buf2, &n) == 2) found = 1; else if (!found) HWMON_RESET();
1037 NORM_ERR("i2c failed to parse arguments");
1038 obj->type = OBJ_text;
1041 DBGP("parsed %s args: '%s' '%s' %d %f %f\n", type, buf1, buf2, n, factor, offset);
1042 sf = malloc(sizeof(struct sysfs));
1043 memset(sf, 0, sizeof(struct sysfs));
1044 sf->fd = open_sysfs_sensor(path, (*buf1) ? buf1 : 0, buf2, n,
1045 &sf->arg, sf->devtype);
1046 strncpy(sf->type, buf2, 63);
1047 sf->factor = factor;
1048 sf->offset = offset;
1049 obj->data.opaque = sf;
1052 #define PARSER_GENERATOR(name, path) \
1053 void parse_##name##_sensor(struct text_object *obj, const char *arg) \
1055 parse_sysfs_sensor(obj, arg, path, #name); \
1058 PARSER_GENERATOR(i2c, "/sys/bus/i2c/devices/")
1059 PARSER_GENERATOR(hwmon, "/sys/class/hwmon/")
1060 PARSER_GENERATOR(platform, "/sys/bus/platform/devices/")
1062 void print_sysfs_sensor(struct text_object *obj, char *p, int p_max_size)
1065 struct sysfs *sf = obj->data.opaque;
1070 r = get_sysfs_info(&sf->fd, sf->arg,
1071 sf->devtype, sf->type);
1073 r = r * sf->factor + sf->offset;
1075 if (!strncmp(sf->type, "temp", 4)) {
1076 temp_print(p, p_max_size, r, TEMP_CELSIUS);
1077 } else if (r >= 100.0 || r == 0) {
1078 snprintf(p, p_max_size, "%d", (int) r);
1080 snprintf(p, p_max_size, "%.1f", r);
1084 void free_sysfs_sensor(struct text_object *obj)
1086 struct sysfs *sf = obj->data.opaque;
1092 free(obj->data.opaque);
1093 obj->data.opaque = NULL;
1096 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1097 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1099 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1100 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1101 const char *p_format, int divisor, unsigned int cpu)
1109 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1115 char current_freq_file[128];
1117 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1119 f = fopen(current_freq_file, "r");
1121 /* if there's a cpufreq /sys node, read the current frequency from
1122 * this node and divide by 1000 to get Mhz. */
1123 if (fgets(s, sizeof(s), f)) {
1124 s[strlen(s) - 1] = '\0';
1125 freq = strtod(s, NULL);
1128 snprintf(p_client_buffer, client_buffer_size, p_format,
1129 (freq / 1000) / divisor);
1134 // open the CPU information file
1135 f = open_file("/proc/cpuinfo", &rep);
1137 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1142 while (fgets(s, sizeof(s), f) != NULL) {
1144 #if defined(__i386) || defined(__x86_64)
1145 // and search for the cpu mhz
1146 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1148 #if defined(__alpha)
1149 // different on alpha
1150 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1152 // this is different on ppc for some reason
1153 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1154 #endif // defined(__alpha)
1155 #endif // defined(__i386) || defined(__x86_64)
1157 // copy just the number
1158 strcpy(frequency, strchr(s, ':') + 2);
1159 #if defined(__alpha)
1161 frequency[strlen(frequency) - 6] = '\0';
1162 // kernel reports in Hz
1163 freq = strtod(frequency, NULL) / 1000000;
1166 frequency[strlen(frequency) - 1] = '\0';
1167 freq = strtod(frequency, NULL);
1171 if (strncmp(s, "processor", 9) == 0) {
1178 snprintf(p_client_buffer, client_buffer_size, p_format,
1179 (float) freq / divisor);
1183 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1185 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1195 * Peter Tarjan (ptarjan@citromail.hu) */
1197 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1198 static char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1199 const char *p_format, int divisor, unsigned int cpu)
1205 char current_freq_file[128];
1208 /* build the voltage file name */
1210 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1213 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1218 /* read the current cpu frequency from the /sys node */
1219 f = fopen(current_freq_file, "r");
1221 if (fgets(s, sizeof(s), f)) {
1222 s[strlen(s) - 1] = '\0';
1223 freq = strtod(s, NULL);
1227 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1228 perror("get_voltage()");
1235 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1238 /* use the current cpu frequency to find the corresponding voltage */
1239 f = fopen(current_freq_file, "r");
1245 if (fgets(line, 255, f) == NULL) {
1248 sscanf(line, "%d %d", &freq_comp, &voltage);
1249 if (freq_comp == freq) {
1255 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1256 perror("get_voltage()");
1262 snprintf(p_client_buffer, client_buffer_size, p_format,
1263 (float) voltage / divisor);
1267 void print_voltage_mv(struct text_object *obj, char *p, int p_max_size)
1271 ok = get_voltage(p, p_max_size, "%.0f", 1, obj->data.i);
1275 void print_voltage_v(struct text_object *obj, char *p, int p_max_size)
1279 ok = get_voltage(p, p_max_size, "%'.3f", 1000, obj->data.i);
1283 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1285 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1292 if (!p_client_buffer || client_buffer_size <= 0) {
1296 /* yeah, slow... :/ */
1297 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1298 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1302 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1304 fp = open_file(buf2, &rep);
1306 snprintf(p_client_buffer, client_buffer_size,
1307 "can't open fan's state file");
1310 memset(buf, 0, sizeof(buf));
1311 fscanf(fp, "%*s %99s", buf);
1314 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1317 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1318 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1319 /* Linux 2.6.25 onwards ac adapter info is in
1320 /sys/class/power_supply/AC/
1321 On my system I get the following.
1322 /sys/class/power_supply/AC/uevent:
1323 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1326 POWER_SUPPLY_NAME=AC
1327 POWER_SUPPLY_TYPE=Mains
1328 POWER_SUPPLY_ONLINE=1
1331 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1339 if (!p_client_buffer || client_buffer_size <= 0) {
1343 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1344 fp = open_file(buf2, &rep);
1346 /* sysfs processing */
1348 if (fgets(buf, sizeof(buf), fp) == NULL)
1351 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1353 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1354 snprintf(p_client_buffer, client_buffer_size,
1355 "%s-line", (online ? "on" : "off"));
1361 /* yeah, slow... :/ */
1362 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1363 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1367 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1369 fp = open_file(buf2, &rep);
1371 snprintf(p_client_buffer, client_buffer_size,
1372 "No ac adapter found.... where is it?");
1375 memset(buf, 0, sizeof(buf));
1376 fscanf(fp, "%*s %99s", buf);
1379 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1384 /proc/acpi/thermal_zone/THRM/cooling_mode
1385 cooling mode: active
1386 /proc/acpi/thermal_zone/THRM/polling_frequency
1388 /proc/acpi/thermal_zone/THRM/state
1390 /proc/acpi/thermal_zone/THRM/temperature
1392 /proc/acpi/thermal_zone/THRM/trip_points
1394 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1397 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1398 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1400 int open_acpi_temperature(const char *name)
1406 if (name == NULL || strcmp(name, "*") == 0) {
1409 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1415 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1417 fd = open(path, O_RDONLY);
1419 NORM_ERR("can't open '%s': %s", path, strerror(errno));
1425 static double last_acpi_temp;
1426 static double last_acpi_temp_time;
1428 double get_acpi_temperature(int fd)
1434 /* don't update acpi temperature too often */
1435 if (current_update_time - last_acpi_temp_time < 11.32) {
1436 return last_acpi_temp;
1438 last_acpi_temp_time = current_update_time;
1440 /* seek to beginning */
1441 lseek(fd, 0, SEEK_SET);
1448 n = read(fd, buf, 255);
1450 NORM_ERR("can't read fd %d: %s", fd, strerror(errno));
1453 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1457 return last_acpi_temp;
1461 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1463 design capacity: 4400 mAh
1464 last full capacity: 4064 mAh
1465 battery technology: rechargeable
1466 design voltage: 14800 mV
1467 design capacity warning: 300 mAh
1468 design capacity low: 200 mAh
1469 capacity granularity 1: 32 mAh
1470 capacity granularity 2: 32 mAh
1472 serial number: 16922
1478 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1481 charging state: unknown
1483 remaining capacity: 4064 mAh
1484 present voltage: 16608 mV
1488 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1489 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1490 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1491 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1492 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1494 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1495 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1497 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1498 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1501 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1502 Linux 2.6.24 onwards battery info is in
1503 /sys/class/power_supply/BAT0/
1504 On my system I get the following.
1505 /sys/class/power_supply/BAT0/uevent:
1506 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1508 PHYSDEVDRIVER=battery
1509 POWER_SUPPLY_NAME=BAT0
1510 POWER_SUPPLY_TYPE=Battery
1511 POWER_SUPPLY_STATUS=Discharging
1512 POWER_SUPPLY_PRESENT=1
1513 POWER_SUPPLY_TECHNOLOGY=Li-ion
1514 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1515 POWER_SUPPLY_VOLTAGE_NOW=10780000
1516 POWER_SUPPLY_CURRENT_NOW=13970000
1517 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1518 POWER_SUPPLY_ENERGY_FULL=27370000
1519 POWER_SUPPLY_ENERGY_NOW=11810000
1520 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1521 POWER_SUPPLY_MANUFACTURER=Panasonic
1522 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1525 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1526 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1527 #define APM_PATH "/proc/apm"
1528 #define MAX_BATTERY_COUNT 4
1530 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1531 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1532 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1534 static int batteries_initialized = 0;
1535 static char batteries[MAX_BATTERY_COUNT][32];
1537 static int acpi_last_full[MAX_BATTERY_COUNT];
1538 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1540 /* e.g. "charging 75%" */
1541 static char last_battery_str[MAX_BATTERY_COUNT][64];
1543 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1545 static double last_battery_time[MAX_BATTERY_COUNT];
1547 static int last_battery_perct[MAX_BATTERY_COUNT];
1548 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1550 void init_batteries(void)
1554 if (batteries_initialized) {
1558 #pragma omp parallel for schedule(dynamic,10)
1559 #endif /* HAVE_OPENMP */
1560 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1561 batteries[idx][0] = '\0';
1563 batteries_initialized = 1;
1566 int get_battery_idx(const char *bat)
1570 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1571 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1576 /* if not found, enter a new entry */
1577 if (!strlen(batteries[idx])) {
1578 snprintf(batteries[idx], 31, "%s", bat);
1584 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1586 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1588 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1589 char acpi_path[128];
1590 char sysfs_path[128];
1592 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1593 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1597 idx = get_battery_idx(bat);
1599 /* don't update battery too often */
1600 if (current_update_time - last_battery_time[idx] < 29.5) {
1601 set_return_value(buffer, n, item, idx);
1605 last_battery_time[idx] = current_update_time;
1607 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1608 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1610 /* first try SYSFS if that fails try ACPI */
1612 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1613 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1616 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1617 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1620 if (sysfs_bat_fp[idx] != NULL) {
1622 int present_rate = -1;
1623 int remaining_capacity = -1;
1624 char charging_state[64];
1627 strcpy(charging_state, "unknown");
1629 while (!feof(sysfs_bat_fp[idx])) {
1631 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1634 /* let's just hope units are ok */
1635 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1636 strcpy(present, "yes");
1637 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1638 strcpy(present, "no");
1639 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1640 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1641 /* present_rate is not the same as the
1642 current flowing now but it is the same value
1643 which was used in the past. so we continue
1645 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1646 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1647 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1648 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1649 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1650 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1651 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1652 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1653 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1654 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1657 fclose(sysfs_bat_fp[idx]);
1658 sysfs_bat_fp[idx] = NULL;
1660 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1661 if (remaining_capacity > acpi_last_full[idx])
1662 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1665 if (strcmp(present, "No") == 0) {
1666 strncpy(last_battery_str[idx], "not present", 64);
1669 else if (strcmp(charging_state, "Charging") == 0) {
1670 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1671 /* e.g. charging 75% */
1672 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1673 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1675 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1676 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1677 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1678 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1679 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1680 snprintf(last_battery_time_str[idx],
1681 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1683 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1684 snprintf(last_battery_time_str[idx],
1685 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1689 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1690 if (present_rate > 0) {
1691 /* e.g. discharging 35% */
1692 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1693 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1695 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1696 (long) (((float) remaining_capacity / present_rate) * 3600));
1697 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1698 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1699 snprintf(last_battery_time_str[idx],
1700 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1702 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1704 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1705 snprintf(last_battery_time_str[idx],
1706 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1710 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1711 else if (strncmp(charging_state, "Charged", 64) == 0 || strncmp(charging_state, "Full", 64) == 0) {
1712 /* Below happens with the second battery on my X40,
1713 * when the second one is empty and the first one
1715 if (remaining_capacity == 0)
1716 strcpy(last_battery_str[idx], "empty");
1718 strcpy(last_battery_str[idx], "charged");
1720 /* unknown, probably full / AC */
1722 if (acpi_last_full[idx] != 0
1723 && remaining_capacity != acpi_last_full[idx])
1724 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1725 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1727 strncpy(last_battery_str[idx], "AC", 64);
1729 } else if (acpi_bat_fp[idx] != NULL) {
1731 int present_rate = -1;
1732 int remaining_capacity = -1;
1733 char charging_state[64];
1736 /* read last full capacity if it's zero */
1737 if (acpi_last_full[idx] == 0) {
1738 static int rep3 = 0;
1742 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1743 fp = open_file(path, &rep3);
1748 if (fgets(b, 256, fp) == NULL) {
1751 if (sscanf(b, "last full capacity: %d",
1752 &acpi_last_full[idx]) != 0) {
1761 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1763 strcpy(charging_state, "unknown");
1765 while (!feof(acpi_bat_fp[idx])) {
1768 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1772 /* let's just hope units are ok */
1773 if (strncmp(buf, "present:", 8) == 0) {
1774 sscanf(buf, "present: %4s", present);
1775 } else if (strncmp(buf, "charging state:", 15) == 0) {
1776 sscanf(buf, "charging state: %63s", charging_state);
1777 } else if (strncmp(buf, "present rate:", 13) == 0) {
1778 sscanf(buf, "present rate: %d", &present_rate);
1779 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1780 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1783 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1784 if (remaining_capacity > acpi_last_full[idx]) {
1785 /* normalize to 100% */
1786 acpi_last_full[idx] = remaining_capacity;
1790 if (strcmp(present, "no") == 0) {
1791 strncpy(last_battery_str[idx], "not present", 64);
1793 } else if (strcmp(charging_state, "charging") == 0) {
1794 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1795 /* e.g. charging 75% */
1796 snprintf(last_battery_str[idx],
1797 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1798 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1800 format_seconds(last_battery_time_str[idx],
1801 sizeof(last_battery_time_str[idx]) - 1,
1802 (long) (((acpi_last_full[idx] - remaining_capacity) *
1803 3600) / present_rate));
1804 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1805 snprintf(last_battery_str[idx],
1806 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1807 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1808 snprintf(last_battery_time_str[idx],
1809 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1811 strncpy(last_battery_str[idx], "charging",
1812 sizeof(last_battery_str[idx]) - 1);
1813 snprintf(last_battery_time_str[idx],
1814 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1817 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1818 if (present_rate > 0) {
1819 /* e.g. discharging 35% */
1820 snprintf(last_battery_str[idx],
1821 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1822 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1824 format_seconds(last_battery_time_str[idx],
1825 sizeof(last_battery_time_str[idx]) - 1,
1826 (long) ((remaining_capacity * 3600) / present_rate));
1827 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1828 snprintf(last_battery_str[idx],
1829 sizeof(last_battery_str[idx]) - 1, "full");
1830 snprintf(last_battery_time_str[idx],
1831 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1833 snprintf(last_battery_str[idx],
1834 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1835 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1836 snprintf(last_battery_time_str[idx],
1837 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1840 } else if (strncmp(charging_state, "charged", 64) == 0) {
1841 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1842 /* Below happens with the second battery on my X40,
1843 * when the second one is empty and the first one being charged. */
1844 if (remaining_capacity == 0) {
1845 strcpy(last_battery_str[idx], "empty");
1847 strcpy(last_battery_str[idx], "charged");
1849 /* unknown, probably full / AC */
1851 if (strncmp(charging_state, "Full", 64) == 0) {
1852 strncpy(last_battery_str[idx], "full", 64);
1853 } else if (acpi_last_full[idx] != 0
1854 && remaining_capacity != acpi_last_full[idx]) {
1855 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1856 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1858 strncpy(last_battery_str[idx], "AC", 64);
1861 fclose(acpi_bat_fp[idx]);
1862 acpi_bat_fp[idx] = NULL;
1865 if (apm_bat_fp[idx] == NULL) {
1866 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1869 if (apm_bat_fp[idx] != NULL) {
1870 unsigned int ac, status, flag;
1873 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1874 &ac, &status, &flag, &life);
1877 /* could check now that there is ac */
1878 snprintf(last_battery_str[idx], 64, "AC");
1880 /* could check that status == 3 here? */
1881 } else if (ac && life != 100) {
1882 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1884 snprintf(last_battery_str[idx], 64, "%d%%", life);
1887 /* it seemed to buffer it so file must be closed (or could use
1888 * syscalls directly but I don't feel like coding it now) */
1889 fclose(apm_bat_fp[idx]);
1890 apm_bat_fp[idx] = NULL;
1893 set_return_value(buffer, n, item, idx);
1896 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1899 case BATTERY_STATUS:
1900 snprintf(buffer, n, "%s", last_battery_str[idx]);
1903 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1910 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1912 get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1913 if (0 == strncmp("charging", buffer, 8)) {
1915 memmove(buffer + 1, buffer + 8, n - 8);
1916 } else if (0 == strncmp("discharging", buffer, 11)) {
1918 memmove(buffer + 1, buffer + 11, n - 11);
1919 } else if (0 == strncmp("charged", buffer, 7)) {
1921 memmove(buffer + 1, buffer + 7, n - 7);
1922 } else if (0 == strncmp("not present", buffer, 11)) {
1924 memmove(buffer + 1, buffer + 11, n - 11);
1925 } else if (0 == strncmp("empty", buffer, 5)) {
1927 memmove(buffer + 1, buffer + 5, n - 5);
1928 } else if (0 != strncmp("AC", buffer, 2)) {
1930 memmove(buffer + 1, buffer + 11, n - 11);
1934 int get_battery_perct(const char *bat)
1938 char acpi_path[128];
1939 char sysfs_path[128];
1940 int remaining_capacity = -1;
1942 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1943 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1947 idx = get_battery_idx(bat);
1949 /* don't update battery too often */
1950 if (current_update_time - last_battery_perct_time[idx] < 30) {
1951 return last_battery_perct[idx];
1953 last_battery_perct_time[idx] = current_update_time;
1955 /* Only check for SYSFS or ACPI */
1957 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1958 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1962 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1963 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1966 if (sysfs_bat_fp[idx] != NULL) {
1968 while (!feof(sysfs_bat_fp[idx])) {
1970 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1973 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1974 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1975 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1976 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1977 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1978 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1979 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1980 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1984 fclose(sysfs_bat_fp[idx]);
1985 sysfs_bat_fp[idx] = NULL;
1987 } else if (acpi_bat_fp[idx] != NULL) {
1989 /* read last full capacity if it's zero */
1990 if (acpi_design_capacity[idx] == 0) {
1995 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1996 fp = open_file(path, &rep2);
2001 if (fgets(b, 256, fp) == NULL) {
2004 if (sscanf(b, "last full capacity: %d",
2005 &acpi_design_capacity[idx]) != 0) {
2013 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
2015 while (!feof(acpi_bat_fp[idx])) {
2018 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
2022 if (buf[0] == 'r') {
2023 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
2027 if (remaining_capacity < 0) {
2030 /* compute the battery percentage */
2031 last_battery_perct[idx] =
2032 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
2033 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
2034 return last_battery_perct[idx];
2037 int get_battery_perct_bar(const char *bar)
2041 get_battery_perct(bar);
2042 idx = get_battery_idx(bar);
2043 return (int) (last_battery_perct[idx] * 2.56 - 1);
2046 /* On Apple powerbook and ibook:
2047 $ cat /proc/pmu/battery_0
2054 $ cat /proc/pmu/info
2055 PMU driver version : 2
2056 PMU firmware version : 0c
2061 /* defines as in <linux/pmu.h> */
2062 #define PMU_BATT_PRESENT 0x00000001
2063 #define PMU_BATT_CHARGING 0x00000002
2065 static FILE *pmu_battery_fp;
2066 static FILE *pmu_info_fp;
2067 static char pb_battery_info[3][32];
2068 static double pb_battery_info_update;
2070 #define PMU_PATH "/proc/pmu"
2071 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2074 const char *batt_path = PMU_PATH "/battery_0";
2075 const char *info_path = PMU_PATH "/info";
2077 int charge, max_charge, ac = -1;
2080 /* don't update battery too often */
2081 if (current_update_time - pb_battery_info_update < 29.5) {
2082 snprintf(buffer, n, "%s", pb_battery_info[i]);
2085 pb_battery_info_update = current_update_time;
2087 if (pmu_battery_fp == NULL) {
2088 pmu_battery_fp = open_file(batt_path, &rep);
2089 if (pmu_battery_fp == NULL) {
2094 if (pmu_battery_fp != NULL) {
2095 rewind(pmu_battery_fp);
2096 while (!feof(pmu_battery_fp)) {
2099 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2103 if (buf[0] == 'f') {
2104 sscanf(buf, "flags : %8x", &flags);
2105 } else if (buf[0] == 'c' && buf[1] == 'h') {
2106 sscanf(buf, "charge : %d", &charge);
2107 } else if (buf[0] == 'm') {
2108 sscanf(buf, "max_charge : %d", &max_charge);
2109 } else if (buf[0] == 't') {
2110 sscanf(buf, "time rem. : %ld", &timeval);
2114 if (pmu_info_fp == NULL) {
2115 pmu_info_fp = open_file(info_path, &rep);
2116 if (pmu_info_fp == NULL) {
2121 if (pmu_info_fp != NULL) {
2122 rewind(pmu_info_fp);
2123 while (!feof(pmu_info_fp)) {
2126 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2129 if (buf[0] == 'A') {
2130 sscanf(buf, "AC Power : %d", &ac);
2134 /* update status string */
2135 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2136 strncpy(pb_battery_info[PB_BATT_STATUS], "AC", sizeof(pb_battery_info[PB_BATT_STATUS]));
2137 } else if (ac && (flags & PMU_BATT_PRESENT)
2138 && !(flags & PMU_BATT_CHARGING)) {
2139 strncpy(pb_battery_info[PB_BATT_STATUS], "charged", sizeof(pb_battery_info[PB_BATT_STATUS]));
2140 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2141 strncpy(pb_battery_info[PB_BATT_STATUS], "charging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2143 strncpy(pb_battery_info[PB_BATT_STATUS], "discharging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2146 /* update percentage string */
2147 if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT)
2148 && !(flags & PMU_BATT_CHARGING)) {
2149 snprintf(pb_battery_info[PB_BATT_PERCENT],
2150 sizeof(pb_battery_info[PB_BATT_PERCENT]), "100%%");
2151 } else if (timeval == 0) {
2152 snprintf(pb_battery_info[PB_BATT_PERCENT],
2153 sizeof(pb_battery_info[PB_BATT_PERCENT]), "unknown");
2155 snprintf(pb_battery_info[PB_BATT_PERCENT],
2156 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2157 (charge * 100) / max_charge);
2160 /* update time string */
2161 if (timeval == 0) { /* fully charged or battery not present */
2162 snprintf(pb_battery_info[PB_BATT_TIME],
2163 sizeof(pb_battery_info[PB_BATT_TIME]), "unknown");
2164 } else if (timeval < 60 * 60) { /* don't show secs */
2165 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2166 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2168 format_seconds(pb_battery_info[PB_BATT_TIME],
2169 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2172 snprintf(buffer, n, "%s", pb_battery_info[i]);
2175 void update_top(void)
2177 process_find_top(info.cpu, info.memu, info.time
2182 info.first_process = get_first_process();
2185 #define ENTROPY_AVAIL_PATH "/proc/sys/kernel/random/entropy_avail"
2187 int get_entropy_avail(unsigned int *val)
2192 if (!(fp = open_file(ENTROPY_AVAIL_PATH, &rep)))
2195 if (fscanf(fp, "%u", val) != 1)
2202 #define ENTROPY_POOLSIZE_PATH "/proc/sys/kernel/random/poolsize"
2204 int get_entropy_poolsize(unsigned int *val)
2209 if (!(fp = open_file(ENTROPY_POOLSIZE_PATH, &rep)))
2212 if (fscanf(fp, "%u", val) != 1)
2219 const char *get_disk_protect_queue(const char *disk)
2225 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2226 if (access(path, F_OK)) {
2227 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2229 if ((fp = fopen(path, "r")) == NULL)
2231 if (fscanf(fp, "%d\n", &state) != 1) {
2236 return (state > 0) ? "frozen" : "free ";
2239 void update_diskio(void)
2243 char buf[512], devbuf[64];
2244 unsigned int major, minor;
2246 struct diskio_stat *cur;
2247 unsigned int reads, writes;
2248 unsigned int total_reads = 0, total_writes = 0;
2251 stats.current_read = 0;
2252 stats.current_write = 0;
2254 if (!(fp = open_file("/proc/diskstats", &rep))) {
2258 /* read reads and writes from all disks (minor = 0), including cd-roms
2259 * and floppies, and sum them up */
2260 while (fgets(buf, 512, fp)) {
2261 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
2262 &minor, devbuf, &reads, &writes);
2263 /* ignore subdevices (they have only 3 matching entries in their line)
2264 * and virtual devices (LVM, network block devices, RAM disks, Loopback)
2266 * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
2267 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
2268 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
2269 total_reads += reads;
2270 total_writes += writes;
2272 col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
2273 &major, &minor, devbuf, &reads, &writes);
2274 if (col_count != 5) {
2279 while (cur && strcmp(devbuf, cur->dev))
2283 update_diskio_values(cur, reads, writes);
2285 update_diskio_values(&stats, total_reads, total_writes);