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.swapfree = 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.swapfree);
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.swapfree;
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;
620 extern void* global_cpu;
622 /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
623 if (!cpu_setup || !info.cpu_usage) {
628 if (!stat_template) {
630 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
634 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
635 cpu = malloc(malloc_cpu_size);
636 memset(cpu, 0, malloc_cpu_size);
640 if (!(stat_fp = open_file("/proc/stat", &rep))) {
642 if (info.cpu_usage) {
643 memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
649 while (!feof(stat_fp)) {
650 if (fgets(buf, 255, stat_fp) == NULL) {
654 if (strncmp(buf, "procs_running ", 14) == 0) {
655 sscanf(buf, "%*s %hu", &info.run_procs);
656 info.mask |= (1 << INFO_RUN_PROCS);
657 } else if (strncmp(buf, "cpu", 3) == 0) {
659 if (isdigit(buf[3])) {
660 idx = atoi(&buf[3]) + 1;
664 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
665 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
666 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
667 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
668 &(cpu[idx].cpu_steal));
670 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
671 cpu[idx].cpu_system + cpu[idx].cpu_idle +
672 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
673 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
675 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
676 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
677 info.mask |= (1 << INFO_CPU);
679 delta = current_update_time - last_update_time;
681 if (delta <= 0.001) {
685 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
686 cpu[idx].cpu_last_active_total) /
687 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
690 #pragma omp parallel for reduction(+:curtmp)
691 #endif /* HAVE_OPENMP */
692 for (i = 0; i < info.cpu_avg_samples; i++) {
693 curtmp = curtmp + cpu[idx].cpu_val[i];
695 /* TESTING -- I've removed this, because I don't think it is right.
696 * You shouldn't divide by the cpu count here ...
697 * removing for testing */
699 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
702 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
704 /* TESTING -- this line replaces the prev. "suspect" if/else */
705 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
707 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
708 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
710 #pragma omp parallel for
711 #endif /* HAVE_OPENMP */
712 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
713 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
720 void update_running_processes(void)
725 void update_cpu_usage(void)
730 void update_load_average(void)
732 #ifdef HAVE_GETLOADAVG
737 info.loadavg[0] = (float) v[0];
738 info.loadavg[1] = (float) v[1];
739 info.loadavg[2] = (float) v[2];
746 if (!(fp = open_file("/proc/loadavg", &rep))) {
747 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
750 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
754 info.mask |= (1 << INFO_LOADAVG);
757 #define PROC_I8K "/proc/i8k"
758 #define I8K_DELIM " "
759 static char *i8k_procbuf = NULL;
760 void update_i8k(void)
765 i8k_procbuf = (char *) malloc(128 * sizeof(char));
767 if ((fp = fopen(PROC_I8K, "r")) == NULL) {
768 CRIT_ERR("/proc/i8k doesn't exist! use insmod to make sure the kernel "
769 "driver is loaded...");
772 memset(&i8k_procbuf[0], 0, 128);
773 if (fread(&i8k_procbuf[0], sizeof(char), 128, fp) == 0) {
774 ERR("something wrong with /proc/i8k...");
779 i8k.version = strtok(&i8k_procbuf[0], I8K_DELIM);
780 i8k.bios = strtok(NULL, I8K_DELIM);
781 i8k.serial = strtok(NULL, I8K_DELIM);
782 i8k.cpu_temp = strtok(NULL, I8K_DELIM);
783 i8k.left_fan_status = strtok(NULL, I8K_DELIM);
784 i8k.right_fan_status = strtok(NULL, I8K_DELIM);
785 i8k.left_fan_rpm = strtok(NULL, I8K_DELIM);
786 i8k.right_fan_rpm = strtok(NULL, I8K_DELIM);
787 i8k.ac_status = strtok(NULL, I8K_DELIM);
788 i8k.buttons_status = strtok(NULL, I8K_DELIM);
791 /***********************************************************/
792 /***********************************************************/
793 /***********************************************************/
795 static int no_dots(const struct dirent *d)
797 if (d->d_name[0] == '.') {
803 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
805 struct dirent **namelist;
808 n = scandir(dir, &namelist, no_dots, alphasort);
811 ERR("scandir for %s: %s", dir, strerror(errno));
822 strncpy(s, namelist[0]->d_name, 255);
826 #pragma omp parallel for
827 #endif /* HAVE_OPENMP */
828 for (i = 0; i < n; i++) {
837 int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
838 int *divisor, char *devtype)
846 memset(buf, 0, sizeof(buf));
848 /* if device is NULL or *, get first */
849 if (dev == NULL || strcmp(dev, "*") == 0) {
852 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
858 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
860 /* buf holds result from get_first_file_in_a_directory() above,
861 * e.g. "hwmon0" -- append "/device" */
862 strcat(buf, "/device");
864 /* dev holds device number N as a string,
865 * e.g. "0", -- convert to "hwmon0/device" */
866 sprintf(buf, "hwmon%s/device", dev);
871 /* At least the acpitz hwmon doesn't have a 'device' subdir,
872 * so check it's existence and strip it from buf otherwise. */
873 snprintf(path, 255, "%s%s", dir, dev);
874 if (stat(path, &st)) {
875 buf[strlen(buf) - 7] = 0;
878 /* change vol to in, tempf to temp */
879 if (strcmp(type, "vol") == 0) {
881 } else if (strcmp(type, "tempf") == 0) {
885 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
886 strncpy(devtype, path, 255);
889 fd = open(path, O_RDONLY);
891 CRIT_ERR("can't open '%s': %s\nplease check your device or remove this "
892 "var from "PACKAGE_NAME, path, strerror(errno));
895 if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
896 || strcmp(type, "tempf") == 0) {
901 /* fan does not use *_div as a read divisor */
902 if (strcmp("fan", type) == 0) {
906 /* test if *_div file exist, open it and use it as divisor */
907 if (strcmp(type, "tempf") == 0) {
908 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
910 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
913 divfd = open(path, O_RDONLY);
919 divn = read(divfd, divbuf, 63);
920 /* should read until n == 0 but I doubt that kernel will give these
921 * in multiple pieces. :) */
923 ERR("open_sysfs_sensor(): can't read from sysfs");
926 *divisor = atoi(divbuf);
935 double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
943 lseek(*fd, 0, SEEK_SET);
949 n = read(*fd, buf, 63);
950 /* should read until n == 0 but I doubt that kernel will give these
951 * in multiple pieces. :) */
953 ERR("get_sysfs_info(): read from %s failed\n", devtype);
962 *fd = open(devtype, O_RDONLY);
964 ERR("can't open '%s': %s", devtype, strerror(errno));
967 /* My dirty hack for computing CPU value
968 * Filedil, from forums.gentoo.org */
969 /* if (strstr(devtype, "temp1_input") != NULL) {
970 return -15.096 + 1.4893 * (val / 1000.0);
973 /* divide voltage and temperature by 1000 */
974 /* or if any other divisor is given, use that */
975 if (strcmp(type, "tempf") == 0) {
977 return ((val / divisor + 40) * 9.0 / 5) - 40;
978 } else if (divisor) {
979 return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
981 return ((val + 40) * 9.0 / 5) - 40;
985 return val / divisor;
986 } else if (divisor) {
994 /* Prior to kernel version 2.6.12, the CPU fan speed was available in
995 * ADT746X_FAN_OLD, whereas later kernel versions provide this information in
997 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
998 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
1000 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
1003 char adt746x_fan_state[64];
1006 if (!p_client_buffer || client_buffer_size <= 0) {
1010 if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
1011 && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL) {
1012 sprintf(adt746x_fan_state, "adt746x not found");
1014 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
1015 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
1019 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_fan_state);
1022 /* Prior to kernel version 2.6.12, the CPU temperature was found in
1023 * ADT746X_CPU_OLD, whereas later kernel versions provide this information in
1025 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
1026 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
1028 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
1031 char adt746x_cpu_state[64];
1034 if (!p_client_buffer || client_buffer_size <= 0) {
1038 if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
1039 && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL) {
1040 sprintf(adt746x_cpu_state, "adt746x not found");
1042 fscanf(fp, "%2s", adt746x_cpu_state);
1046 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state);
1049 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1050 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1052 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1053 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1054 const char *p_format, int divisor, unsigned int cpu)
1062 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1068 char current_freq_file[128];
1070 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1072 f = fopen(current_freq_file, "r");
1074 /* if there's a cpufreq /sys node, read the current frequency from
1075 * this node and divide by 1000 to get Mhz. */
1076 if (fgets(s, sizeof(s), f)) {
1077 s[strlen(s) - 1] = '\0';
1078 freq = strtod(s, NULL);
1081 snprintf(p_client_buffer, client_buffer_size, p_format,
1082 (freq / 1000) / divisor);
1087 // open the CPU information file
1088 f = open_file("/proc/cpuinfo", &rep);
1090 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1095 while (fgets(s, sizeof(s), f) != NULL) {
1097 #if defined(__i386) || defined(__x86_64)
1098 // and search for the cpu mhz
1099 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1101 #if defined(__alpha)
1102 // different on alpha
1103 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1105 // this is different on ppc for some reason
1106 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1107 #endif // defined(__alpha)
1108 #endif // defined(__i386) || defined(__x86_64)
1110 // copy just the number
1111 strcpy(frequency, strchr(s, ':') + 2);
1112 #if defined(__alpha)
1114 frequency[strlen(frequency) - 6] = '\0';
1115 // kernel reports in Hz
1116 freq = strtod(frequency, NULL) / 1000000;
1119 frequency[strlen(frequency) - 1] = '\0';
1120 freq = strtod(frequency, NULL);
1124 if (strncmp(s, "processor", 9) == 0) {
1131 snprintf(p_client_buffer, client_buffer_size, p_format,
1132 (float) freq / divisor);
1136 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1138 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1148 * Peter Tarjan (ptarjan@citromail.hu) */
1150 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1151 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1152 const char *p_format, int divisor, unsigned int cpu)
1158 char current_freq_file[128];
1161 /* build the voltage file name */
1163 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1166 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1171 /* read the current cpu frequency from the /sys node */
1172 f = fopen(current_freq_file, "r");
1174 if (fgets(s, sizeof(s), f)) {
1175 s[strlen(s) - 1] = '\0';
1176 freq = strtod(s, NULL);
1180 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1181 perror("get_voltage()");
1188 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1191 /* use the current cpu frequency to find the corresponding voltage */
1192 f = fopen(current_freq_file, "r");
1198 if (fgets(line, 255, f) == NULL) {
1201 sscanf(line, "%d %d", &freq_comp, &voltage);
1202 if (freq_comp == freq) {
1208 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1209 perror("get_voltage()");
1215 snprintf(p_client_buffer, client_buffer_size, p_format,
1216 (float) voltage / divisor);
1220 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1222 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1229 if (!p_client_buffer || client_buffer_size <= 0) {
1233 /* yeah, slow... :/ */
1234 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1235 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1239 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1241 fp = open_file(buf2, &rep);
1243 snprintf(p_client_buffer, client_buffer_size,
1244 "can't open fan's state file");
1247 memset(buf, 0, sizeof(buf));
1248 fscanf(fp, "%*s %99s", buf);
1251 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1254 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1255 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1256 /* Linux 2.6.25 onwards ac adapter info is in
1257 /sys/class/power_supply/AC/
1258 On my system I get the following.
1259 /sys/class/power_supply/AC/uevent:
1260 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1263 POWER_SUPPLY_NAME=AC
1264 POWER_SUPPLY_TYPE=Mains
1265 POWER_SUPPLY_ONLINE=1
1268 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1276 if (!p_client_buffer || client_buffer_size <= 0) {
1280 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1281 fp = open_file(buf2, &rep);
1283 /* sysfs processing */
1285 if (fgets(buf, sizeof(buf), fp) == NULL)
1288 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1290 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1291 snprintf(p_client_buffer, client_buffer_size,
1292 "%s-line", (online ? "on" : "off"));
1298 /* yeah, slow... :/ */
1299 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1300 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1304 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1306 fp = open_file(buf2, &rep);
1308 snprintf(p_client_buffer, client_buffer_size,
1309 "No ac adapter found.... where is it?");
1312 memset(buf, 0, sizeof(buf));
1313 fscanf(fp, "%*s %99s", buf);
1316 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1321 /proc/acpi/thermal_zone/THRM/cooling_mode
1322 cooling mode: active
1323 /proc/acpi/thermal_zone/THRM/polling_frequency
1325 /proc/acpi/thermal_zone/THRM/state
1327 /proc/acpi/thermal_zone/THRM/temperature
1329 /proc/acpi/thermal_zone/THRM/trip_points
1331 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1334 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1335 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1337 int open_acpi_temperature(const char *name)
1343 if (name == NULL || strcmp(name, "*") == 0) {
1346 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1352 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1354 fd = open(path, O_RDONLY);
1356 ERR("can't open '%s': %s", path, strerror(errno));
1362 static double last_acpi_temp;
1363 static double last_acpi_temp_time;
1365 double get_acpi_temperature(int fd)
1371 /* don't update acpi temperature too often */
1372 if (current_update_time - last_acpi_temp_time < 11.32) {
1373 return last_acpi_temp;
1375 last_acpi_temp_time = current_update_time;
1377 /* seek to beginning */
1378 lseek(fd, 0, SEEK_SET);
1385 n = read(fd, buf, 255);
1387 ERR("can't read fd %d: %s", fd, strerror(errno));
1390 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1394 return last_acpi_temp;
1398 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1400 design capacity: 4400 mAh
1401 last full capacity: 4064 mAh
1402 battery technology: rechargeable
1403 design voltage: 14800 mV
1404 design capacity warning: 300 mAh
1405 design capacity low: 200 mAh
1406 capacity granularity 1: 32 mAh
1407 capacity granularity 2: 32 mAh
1409 serial number: 16922
1415 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1418 charging state: unknown
1420 remaining capacity: 4064 mAh
1421 present voltage: 16608 mV
1425 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1426 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1427 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1428 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1429 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1431 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1432 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1434 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1435 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1438 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1439 Linux 2.6.24 onwards battery info is in
1440 /sys/class/power_supply/BAT0/
1441 On my system I get the following.
1442 /sys/class/power_supply/BAT0/uevent:
1443 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1445 PHYSDEVDRIVER=battery
1446 POWER_SUPPLY_NAME=BAT0
1447 POWER_SUPPLY_TYPE=Battery
1448 POWER_SUPPLY_STATUS=Discharging
1449 POWER_SUPPLY_PRESENT=1
1450 POWER_SUPPLY_TECHNOLOGY=Li-ion
1451 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1452 POWER_SUPPLY_VOLTAGE_NOW=10780000
1453 POWER_SUPPLY_CURRENT_NOW=13970000
1454 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1455 POWER_SUPPLY_ENERGY_FULL=27370000
1456 POWER_SUPPLY_ENERGY_NOW=11810000
1457 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1458 POWER_SUPPLY_MANUFACTURER=Panasonic
1459 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1462 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1463 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1464 #define APM_PATH "/proc/apm"
1465 #define MAX_BATTERY_COUNT 4
1467 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1468 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1469 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1471 static int batteries_initialized = 0;
1472 static char batteries[MAX_BATTERY_COUNT][32];
1474 static int acpi_last_full[MAX_BATTERY_COUNT];
1475 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1477 /* e.g. "charging 75%" */
1478 static char last_battery_str[MAX_BATTERY_COUNT][64];
1480 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1482 static double last_battery_time[MAX_BATTERY_COUNT];
1484 static int last_battery_perct[MAX_BATTERY_COUNT];
1485 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1487 void init_batteries(void)
1491 if (batteries_initialized) {
1495 #pragma omp parallel for
1496 #endif /* HAVE_OPENMP */
1497 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1498 batteries[idx][0] = '\0';
1500 batteries_initialized = 1;
1503 int get_battery_idx(const char *bat)
1507 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1508 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1513 /* if not found, enter a new entry */
1514 if (!strlen(batteries[idx])) {
1515 snprintf(batteries[idx], 31, "%s", bat);
1521 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1523 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1525 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1526 char acpi_path[128];
1527 char sysfs_path[128];
1529 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1530 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1534 idx = get_battery_idx(bat);
1536 /* don't update battery too often */
1537 if (current_update_time - last_battery_time[idx] < 29.5) {
1538 set_return_value(buffer, n, item, idx);
1542 last_battery_time[idx] = current_update_time;
1544 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1545 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1547 /* first try SYSFS if that fails try ACPI */
1549 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1550 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1553 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1554 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1557 if (sysfs_bat_fp[idx] != NULL) {
1559 int present_rate = -1;
1560 int remaining_capacity = -1;
1561 char charging_state[64];
1564 strcpy(charging_state, "unknown");
1566 while (!feof(sysfs_bat_fp[idx])) {
1568 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1571 /* let's just hope units are ok */
1572 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1573 strcpy(present, "yes");
1574 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1575 strcpy(present, "no");
1576 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1577 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1578 /* present_rate is not the same as the
1579 current flowing now but it is the same value
1580 which was used in the past. so we continue
1582 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1583 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1584 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1585 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1586 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1587 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1588 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1589 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1590 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1591 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1594 fclose(sysfs_bat_fp[idx]);
1595 sysfs_bat_fp[idx] = NULL;
1597 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1598 if (remaining_capacity > acpi_last_full[idx])
1599 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1602 if (strcmp(present, "No") == 0) {
1603 strncpy(last_battery_str[idx], "not present", 64);
1606 else if (strcmp(charging_state, "Charging") == 0) {
1607 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1608 /* e.g. charging 75% */
1609 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1610 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1612 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1613 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1614 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1615 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1616 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1617 snprintf(last_battery_time_str[idx],
1618 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1620 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1621 snprintf(last_battery_time_str[idx],
1622 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1626 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1627 if (present_rate > 0) {
1628 /* e.g. discharging 35% */
1629 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1630 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1632 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1633 (long) (((float) remaining_capacity / present_rate) * 3600));
1634 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1635 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1636 snprintf(last_battery_time_str[idx],
1637 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1639 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1641 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1642 snprintf(last_battery_time_str[idx],
1643 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1647 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1648 else if (strncmp(charging_state, "Charged", 64) == 0 || strncmp(charging_state, "Full", 64) == 0) {
1649 /* Below happens with the second battery on my X40,
1650 * when the second one is empty and the first one
1652 if (remaining_capacity == 0)
1653 strcpy(last_battery_str[idx], "empty");
1655 strcpy(last_battery_str[idx], "charged");
1657 /* unknown, probably full / AC */
1659 if (acpi_last_full[idx] != 0
1660 && remaining_capacity != acpi_last_full[idx])
1661 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1662 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1664 strncpy(last_battery_str[idx], "AC", 64);
1666 } else if (acpi_bat_fp[idx] != NULL) {
1668 int present_rate = -1;
1669 int remaining_capacity = -1;
1670 char charging_state[64];
1673 /* read last full capacity if it's zero */
1674 if (acpi_last_full[idx] == 0) {
1675 static int rep3 = 0;
1679 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1680 fp = open_file(path, &rep3);
1685 if (fgets(b, 256, fp) == NULL) {
1688 if (sscanf(b, "last full capacity: %d",
1689 &acpi_last_full[idx]) != 0) {
1698 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1700 strcpy(charging_state, "unknown");
1702 while (!feof(acpi_bat_fp[idx])) {
1705 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1709 /* let's just hope units are ok */
1710 if (strncmp(buf, "present:", 8) == 0) {
1711 sscanf(buf, "present: %4s", present);
1712 } else if (strncmp(buf, "charging state:", 15) == 0) {
1713 sscanf(buf, "charging state: %63s", charging_state);
1714 } else if (strncmp(buf, "present rate:", 13) == 0) {
1715 sscanf(buf, "present rate: %d", &present_rate);
1716 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1717 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1720 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1721 if (remaining_capacity > acpi_last_full[idx]) {
1722 /* normalize to 100% */
1723 acpi_last_full[idx] = remaining_capacity;
1727 if (strcmp(present, "no") == 0) {
1728 strncpy(last_battery_str[idx], "not present", 64);
1730 } else if (strcmp(charging_state, "charging") == 0) {
1731 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1732 /* e.g. charging 75% */
1733 snprintf(last_battery_str[idx],
1734 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1735 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1737 format_seconds(last_battery_time_str[idx],
1738 sizeof(last_battery_time_str[idx]) - 1,
1739 (long) (((acpi_last_full[idx] - remaining_capacity) *
1740 3600) / present_rate));
1741 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1742 snprintf(last_battery_str[idx],
1743 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1744 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1745 snprintf(last_battery_time_str[idx],
1746 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1748 strncpy(last_battery_str[idx], "charging",
1749 sizeof(last_battery_str[idx]) - 1);
1750 snprintf(last_battery_time_str[idx],
1751 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1754 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1755 if (present_rate > 0) {
1756 /* e.g. discharging 35% */
1757 snprintf(last_battery_str[idx],
1758 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1759 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1761 format_seconds(last_battery_time_str[idx],
1762 sizeof(last_battery_time_str[idx]) - 1,
1763 (long) ((remaining_capacity * 3600) / present_rate));
1764 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1765 snprintf(last_battery_str[idx],
1766 sizeof(last_battery_str[idx]) - 1, "full");
1767 snprintf(last_battery_time_str[idx],
1768 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1770 snprintf(last_battery_str[idx],
1771 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1772 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1773 snprintf(last_battery_time_str[idx],
1774 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1777 } else if (strncmp(charging_state, "charged", 64) == 0) {
1778 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1779 /* Below happens with the second battery on my X40,
1780 * when the second one is empty and the first one being charged. */
1781 if (remaining_capacity == 0) {
1782 strcpy(last_battery_str[idx], "empty");
1784 strcpy(last_battery_str[idx], "charged");
1786 /* unknown, probably full / AC */
1788 if (strncmp(charging_state, "Full", 64) == 0) {
1789 strncpy(last_battery_str[idx], "full", 64);
1790 } else if (acpi_last_full[idx] != 0
1791 && remaining_capacity != acpi_last_full[idx]) {
1792 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1793 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1795 strncpy(last_battery_str[idx], "AC", 64);
1800 if (apm_bat_fp[idx] == NULL) {
1801 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1804 if (apm_bat_fp[idx] != NULL) {
1805 unsigned int ac, status, flag;
1808 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1809 &ac, &status, &flag, &life);
1812 /* could check now that there is ac */
1813 snprintf(last_battery_str[idx], 64, "AC");
1815 /* could check that status == 3 here? */
1816 } else if (ac && life != 100) {
1817 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1819 snprintf(last_battery_str[idx], 64, "%d%%", life);
1822 /* it seemed to buffer it so file must be closed (or could use
1823 * syscalls directly but I don't feel like coding it now) */
1824 fclose(apm_bat_fp[idx]);
1825 apm_bat_fp[idx] = NULL;
1828 set_return_value(buffer, n, item, idx);
1831 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1834 case BATTERY_STATUS:
1835 snprintf(buffer, n, "%s", last_battery_str[idx]);
1838 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1845 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1847 get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1848 if (0 == strncmp("charging", buffer, 8)) {
1850 memmove(buffer + 1, buffer + 8, n - 8);
1851 } else if (0 == strncmp("discharging", buffer, 11)) {
1853 memmove(buffer + 1, buffer + 11, n - 11);
1854 } else if (0 == strncmp("charged", buffer, 7)) {
1856 memmove(buffer + 1, buffer + 7, n - 7);
1857 } else if (0 == strncmp("not present", buffer, 11)) {
1859 memmove(buffer + 1, buffer + 11, n - 11);
1860 } else if (0 == strncmp("empty", buffer, 5)) {
1862 memmove(buffer + 1, buffer + 5, n - 5);
1863 } else if (0 != strncmp("AC", buffer, 2)) {
1865 memmove(buffer + 1, buffer + 11, n - 11);
1869 int get_battery_perct(const char *bat)
1873 char acpi_path[128];
1874 char sysfs_path[128];
1875 int remaining_capacity = -1;
1877 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1878 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1882 idx = get_battery_idx(bat);
1884 /* don't update battery too often */
1885 if (current_update_time - last_battery_perct_time[idx] < 30) {
1886 return last_battery_perct[idx];
1888 last_battery_perct_time[idx] = current_update_time;
1890 /* Only check for SYSFS or ACPI */
1892 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1893 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1897 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1898 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1901 if (sysfs_bat_fp[idx] != NULL) {
1903 while (!feof(sysfs_bat_fp[idx])) {
1905 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1908 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1909 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1910 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1911 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1912 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1913 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1914 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1915 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1919 fclose(sysfs_bat_fp[idx]);
1920 sysfs_bat_fp[idx] = NULL;
1922 } else if (acpi_bat_fp[idx] != NULL) {
1924 /* read last full capacity if it's zero */
1925 if (acpi_design_capacity[idx] == 0) {
1930 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1931 fp = open_file(path, &rep2);
1936 if (fgets(b, 256, fp) == NULL) {
1939 if (sscanf(b, "last full capacity: %d",
1940 &acpi_design_capacity[idx]) != 0) {
1948 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1950 while (!feof(acpi_bat_fp[idx])) {
1953 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1957 if (buf[0] == 'r') {
1958 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1962 if (remaining_capacity < 0) {
1965 /* compute the battery percentage */
1966 last_battery_perct[idx] =
1967 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
1968 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
1969 return last_battery_perct[idx];
1972 int get_battery_perct_bar(const char *bar)
1976 get_battery_perct(bar);
1977 idx = get_battery_idx(bar);
1978 return (int) (last_battery_perct[idx] * 2.56 - 1);
1981 /* On Apple powerbook and ibook:
1982 $ cat /proc/pmu/battery_0
1989 $ cat /proc/pmu/info
1990 PMU driver version : 2
1991 PMU firmware version : 0c
1996 /* defines as in <linux/pmu.h> */
1997 #define PMU_BATT_PRESENT 0x00000001
1998 #define PMU_BATT_CHARGING 0x00000002
2000 static FILE *pmu_battery_fp;
2001 static FILE *pmu_info_fp;
2002 static char pb_battery_info[3][32];
2003 static double pb_battery_info_update;
2005 #define PMU_PATH "/proc/pmu"
2006 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2009 const char *batt_path = PMU_PATH "/battery_0";
2010 const char *info_path = PMU_PATH "/info";
2012 int charge, max_charge, ac = -1;
2015 /* don't update battery too often */
2016 if (current_update_time - pb_battery_info_update < 29.5) {
2017 snprintf(buffer, n, "%s", pb_battery_info[i]);
2020 pb_battery_info_update = current_update_time;
2022 if (pmu_battery_fp == NULL) {
2023 pmu_battery_fp = open_file(batt_path, &rep);
2024 if (pmu_battery_fp == NULL) {
2029 if (pmu_battery_fp != NULL) {
2030 rewind(pmu_battery_fp);
2031 while (!feof(pmu_battery_fp)) {
2034 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2038 if (buf[0] == 'f') {
2039 sscanf(buf, "flags : %8x", &flags);
2040 } else if (buf[0] == 'c' && buf[1] == 'h') {
2041 sscanf(buf, "charge : %d", &charge);
2042 } else if (buf[0] == 'm') {
2043 sscanf(buf, "max_charge : %d", &max_charge);
2044 } else if (buf[0] == 't') {
2045 sscanf(buf, "time rem. : %ld", &timeval);
2049 if (pmu_info_fp == NULL) {
2050 pmu_info_fp = open_file(info_path, &rep);
2051 if (pmu_info_fp == NULL) {
2056 if (pmu_info_fp != NULL) {
2057 rewind(pmu_info_fp);
2058 while (!feof(pmu_info_fp)) {
2061 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2064 if (buf[0] == 'A') {
2065 sscanf(buf, "AC Power : %d", &ac);
2069 /* update status string */
2070 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2071 strncpy(pb_battery_info[PB_BATT_STATUS], "AC", sizeof(pb_battery_info[PB_BATT_STATUS]));
2072 } else if (ac && (flags & PMU_BATT_PRESENT)
2073 && !(flags & PMU_BATT_CHARGING)) {
2074 strncpy(pb_battery_info[PB_BATT_STATUS], "charged", sizeof(pb_battery_info[PB_BATT_STATUS]));
2075 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2076 strncpy(pb_battery_info[PB_BATT_STATUS], "charging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2078 strncpy(pb_battery_info[PB_BATT_STATUS], "discharging", sizeof(pb_battery_info[PB_BATT_STATUS]));
2081 /* update percentage string */
2082 if (timeval == 0 && ac && (flags & PMU_BATT_PRESENT)
2083 && !(flags & PMU_BATT_CHARGING)) {
2084 snprintf(pb_battery_info[PB_BATT_PERCENT],
2085 sizeof(pb_battery_info[PB_BATT_PERCENT]), "100%%");
2086 } else if (timeval == 0) {
2087 snprintf(pb_battery_info[PB_BATT_PERCENT],
2088 sizeof(pb_battery_info[PB_BATT_PERCENT]), "unknown");
2090 snprintf(pb_battery_info[PB_BATT_PERCENT],
2091 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2092 (charge * 100) / max_charge);
2095 /* update time string */
2096 if (timeval == 0) { /* fully charged or battery not present */
2097 snprintf(pb_battery_info[PB_BATT_TIME],
2098 sizeof(pb_battery_info[PB_BATT_TIME]), "unknown");
2099 } else if (timeval < 60 * 60) { /* don't show secs */
2100 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2101 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2103 format_seconds(pb_battery_info[PB_BATT_TIME],
2104 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2107 snprintf(buffer, n, "%s", pb_battery_info[i]);
2110 void update_top(void)
2112 process_find_top(info.cpu, info.memu, info.time
2117 info.first_process = get_first_process();
2120 void update_entropy(void)
2123 const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2124 const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2127 info.entropy.entropy_avail = 0;
2128 info.entropy.poolsize = 0;
2130 if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2134 if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2139 fscanf(fp1, "%u", &info.entropy.entropy_avail);
2140 fscanf(fp2, "%u", &info.entropy.poolsize);
2145 info.mask |= (1 << INFO_ENTROPY);
2148 const char *get_disk_protect_queue(const char *disk)
2154 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2155 if (access(path, F_OK)) {
2156 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2158 if ((fp = fopen(path, "r")) == NULL)
2160 if (fscanf(fp, "%d\n", &state) != 1) {
2165 return (state > 0) ? "frozen" : "free ";
2168 void update_diskio(void)
2172 char buf[512], devbuf[64];
2173 unsigned int major, minor;
2175 struct diskio_stat *cur;
2176 unsigned int reads, writes;
2177 unsigned int total_reads = 0, total_writes = 0;
2180 stats.current_read = 0;
2181 stats.current_write = 0;
2183 if (!(fp = open_file("/proc/diskstats", &rep))) {
2187 /* read reads and writes from all disks (minor = 0), including cd-roms
2188 * and floppies, and sum them up */
2189 while (fgets(buf, 512, fp)) {
2190 col_count = sscanf(buf, "%u %u %s %*u %*u %u %*u %*u %*u %u", &major,
2191 &minor, devbuf, &reads, &writes);
2192 /* ignore subdevices (they have only 3 matching entries in their line)
2193 * and virtual devices (LVM, network block devices, RAM disks, Loopback)
2195 * XXX: ignore devices which are part of a SW RAID (MD_MAJOR) */
2196 if (col_count == 5 && major != LVM_BLK_MAJOR && major != NBD_MAJOR
2197 && major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
2198 total_reads += reads;
2199 total_writes += writes;
2201 col_count = sscanf(buf, "%u %u %s %*u %u %*u %u",
2202 &major, &minor, devbuf, &reads, &writes);
2203 if (col_count != 5) {
2208 while (cur && strcmp(devbuf, cur->dev))
2212 update_diskio_values(cur, reads, writes);
2214 update_diskio_values(&stats, total_reads, total_writes);