1 /* Conky, a system monitor, based on torsmo
3 * Any original torsmo code is licensed under the BSD license
5 * All code written since the fork of torsmo is licensed under the GPL
7 * Please see COPYING for details
9 * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
10 * Copyright (c) 2007 Toni Spets
11 * Copyright (c) 2005-2008 Brenden Matthews, Philip Kovacs, et. al.
13 * All rights reserved.
15 * This program is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation, either version 3 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program. If not, see <http://www.gnu.org/licenses/>.
37 #include <sys/types.h>
38 #include <sys/sysinfo.h>
40 #ifndef HAVE_CLOCK_GETTIME
45 // #include <assert.h>
49 #include <sys/ioctl.h>
50 #include <sys/socket.h>
51 #include <netinet/in.h>
52 #include <linux/sockios.h>
54 #include <arpa/inet.h>
58 #include <linux/route.h>
65 #define SHORTSTAT_TEMPL "%*s %llu %llu %llu"
66 #define LONGSTAT_TEMPL "%*s %llu %llu %llu "
68 static int show_nice_processes;
70 /* This flag tells the linux routines to use the /proc system where possible,
71 * even if other api's are available, e.g. sysinfo() or getloadavg().
72 * the reason for this is to allow for /proc-based distributed monitoring.
73 * using a flag in this manner creates less confusing code. */
74 static int prefer_proc = 0;
76 void prepare_update(void)
80 void update_uptime(void)
84 struct sysinfo s_info;
87 info.uptime = (double) s_info.uptime;
94 if (!(fp = open_file("/proc/uptime", &rep))) {
98 fscanf(fp, "%lf", &info.uptime);
101 info.mask |= (1 << INFO_UPTIME);
104 int check_mount(char *s)
107 FILE *mtab = fopen("/etc/mtab", "r");
110 char buf1[256], buf2[128];
112 while (fgets(buf1, 256, mtab)) {
113 sscanf(buf1, "%*s %128s", buf2);
114 if (!strcmp(s, buf2)) {
121 ERR("Could not open mtab");
126 /* these things are also in sysinfo except Buffers:
127 * (that's why I'm reading them from proc) */
129 void update_meminfo(void)
134 /* unsigned int a; */
137 info.mem = info.memmax = info.swap = info.swapmax = info.bufmem =
138 info.buffers = info.cached = info.memfree = info.memeasyfree = 0;
140 if (!(meminfo_fp = open_file("/proc/meminfo", &rep))) {
144 while (!feof(meminfo_fp)) {
145 if (fgets(buf, 255, meminfo_fp) == NULL) {
149 if (strncmp(buf, "MemTotal:", 9) == 0) {
150 sscanf(buf, "%*s %llu", &info.memmax);
151 } else if (strncmp(buf, "MemFree:", 8) == 0) {
152 sscanf(buf, "%*s %llu", &info.memfree);
153 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
154 sscanf(buf, "%*s %llu", &info.swapmax);
155 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
156 sscanf(buf, "%*s %llu", &info.swap);
157 } else if (strncmp(buf, "Buffers:", 8) == 0) {
158 sscanf(buf, "%*s %llu", &info.buffers);
159 } else if (strncmp(buf, "Cached:", 7) == 0) {
160 sscanf(buf, "%*s %llu", &info.cached);
164 info.mem = info.memmax - info.memfree;
165 info.memeasyfree = info.memfree;
166 info.swap = info.swapmax - info.swap;
168 info.bufmem = info.cached + info.buffers;
170 info.mask |= (1 << INFO_MEM) | (1 << INFO_BUFFERS);
175 int get_laptop_mode(void)
180 if ((fp = fopen("/proc/sys/vm/laptop_mode", "r")) != NULL)
181 fscanf(fp, "%d\n", &val);
187 * # cat /sys/block/sda/queue/scheduler
188 * noop [anticipatory] cfq
190 char *get_ioscheduler(char *disk)
196 return strndup("n/a", text_buffer_size);
198 snprintf(buf, 127, "/sys/block/%s/queue/scheduler", disk);
199 if ((fp = fopen(buf, "r")) == NULL) {
200 return strndup("n/a", text_buffer_size);
203 fscanf(fp, "%127s", buf);
205 buf[strlen(buf) - 1] = '\0';
207 return strndup(buf + 1, text_buffer_size);
211 return strndup("n/a", text_buffer_size);
214 int interface_up(const char *dev)
219 if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
220 CRIT_ERR("could not create sockfd");
223 strncpy(ifr.ifr_name, dev, IFNAMSIZ);
224 if (ioctl(fd, SIOCGIFFLAGS, &ifr)) {
225 /* if device does not exist, treat like not up */
227 perror("SIOCGIFFLAGS");
231 if (!(ifr.ifr_flags & IFF_UP)) /* iface is not up */
233 if (ifup_strictness == IFUP_UP)
236 if (!(ifr.ifr_flags & IFF_RUNNING))
238 if (ifup_strictness == IFUP_LINK)
241 if (ioctl(fd, SIOCGIFADDR, &ifr)) {
242 perror("SIOCGIFADDR");
245 if (((struct sockaddr_in *)&(ifr.ifr_ifru.ifru_addr))->sin_addr.s_addr)
256 #define COND_FREE(x) if(x) free(x); x = 0
257 #define SAVE_SET_STRING(x, y) \
258 if (x && strcmp((char *)x, (char *)y)) { \
260 x = strndup("multiple", text_buffer_size); \
262 x = strndup(y, text_buffer_size); \
265 void update_gateway_info_failure(const char *reason)
270 //2 pointers to 1 location causes a crash when we try to free them both
271 info.gw_info.iface = strndup("failed", text_buffer_size);
272 info.gw_info.ip = strndup("failed", text_buffer_size);
275 void update_gateway_info(void)
280 unsigned long dest, gate, mask;
282 short ref, use, metric, mtu, win, irtt;
284 struct gateway_info *gw_info = &info.gw_info;
286 COND_FREE(gw_info->iface);
287 COND_FREE(gw_info->ip);
290 if ((fp = fopen("/proc/net/route", "r")) == NULL) {
291 update_gateway_info_failure("fopen()");
294 if (fscanf(fp, "%*[^\n]\n") == EOF) {
295 //NULL because a empty table is not a error
296 update_gateway_info_failure(NULL);
301 // Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
302 if(fscanf(fp, "%63s %lx %lx %x %hd %hd %hd %lx %hd %hd %hd\n",
303 iface, &dest, &gate, &flags, &ref, &use,
304 &metric, &mask, &mtu, &win, &irtt) != 11) {
305 update_gateway_info_failure("fscanf()");
309 if (flags & RTF_GATEWAY && dest == 0 && mask == 0) {
311 SAVE_SET_STRING(gw_info->iface, iface)
313 SAVE_SET_STRING(gw_info->ip, inet_ntoa(ina))
320 void update_net_stats(void)
325 // FIXME: arbitrary size chosen to keep code simple.
327 unsigned int curtmp1, curtmp2;
334 // wireless info variables
335 int skfd, has_bitrate = 0;
336 struct wireless_info *winfo;
341 delta = current_update_time - last_update_time;
342 if (delta <= 0.0001) {
346 /* open file and ignore first two lines */
347 if (!(net_dev_fp = open_file("/proc/net/dev", &rep))) {
352 fgets(buf, 255, net_dev_fp); /* garbage */
353 fgets(buf, 255, net_dev_fp); /* garbage (field names) */
355 /* read each interface */
356 for (i2 = 0; i2 < 16; i2++) {
360 long long r, t, last_recv, last_trans;
362 if (fgets(buf, 255, net_dev_fp) == NULL) {
366 while (isspace((int) *p)) {
372 while (*p && *p != ':') {
381 ns = get_net_stat(s);
383 memset(&(ns->addr.sa_data), 0, 14);
385 if(NULL == ns->addrs)
386 ns->addrs = (char*) malloc(17 * 16 + 1);
387 if(NULL != ns->addrs)
388 memset(ns->addrs, 0, 17 * 16 + 1); /* Up to 17 chars per ip, max 16 interfaces. Nasty memory usage... */
390 last_recv = ns->recv;
391 last_trans = ns->trans;
393 /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
394 sscanf(p, "%lld %*d %*d %*d %*d %*d %*d %*d %lld",
397 /* if recv or trans is less than last time, an overflow happened */
398 if (r < ns->last_read_recv) {
401 ns->recv += (r - ns->last_read_recv);
403 ns->last_read_recv = r;
405 if (t < ns->last_read_trans) {
408 ns->trans += (t - ns->last_read_trans);
410 ns->last_read_trans = t;
412 /*** ip addr patch ***/
413 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
415 conf.ifc_buf = malloc(sizeof(struct ifreq) * 16);
416 conf.ifc_len = sizeof(struct ifreq) * 16;
417 memset(conf.ifc_buf, 0, conf.ifc_len);
419 ioctl((long) i, SIOCGIFCONF, &conf);
421 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
422 struct net_stat *ns2;
424 if (!(((struct ifreq *) conf.ifc_buf) + k))
428 ((struct ifreq *) conf.ifc_buf)[k].ifr_ifrn.ifrn_name);
429 ns2->addr = ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.ifru_addr;
430 if(NULL != ns2->addrs) {
431 sprintf(temp_addr, "%u.%u.%u.%u, ",
432 ns2->addr.sa_data[2] & 255,
433 ns2->addr.sa_data[3] & 255,
434 ns2->addr.sa_data[4] & 255,
435 ns2->addr.sa_data[5] & 255);
436 if(NULL == strstr(ns2->addrs, temp_addr))
437 strncpy(ns2->addrs + strlen(ns2->addrs), temp_addr, 17);
445 /*** end ip addr patch ***/
447 /* calculate speeds */
448 ns->net_rec[0] = (ns->recv - last_recv) / delta;
449 ns->net_trans[0] = (ns->trans - last_trans) / delta;
453 for (i = 0; (unsigned) i < info.net_avg_samples; i++) {
454 curtmp1 += ns->net_rec[i];
455 curtmp2 += ns->net_trans[i];
463 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
464 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
465 if (info.net_avg_samples > 1) {
466 for (i = info.net_avg_samples; i > 1; i--) {
467 ns->net_rec[i - 1] = ns->net_rec[i - 2];
468 ns->net_trans[i - 1] = ns->net_trans[i - 2];
473 /* update wireless info */
474 winfo = malloc(sizeof(struct wireless_info));
475 memset(winfo, 0, sizeof(struct wireless_info));
477 skfd = iw_sockets_open();
478 if (iw_get_basic_config(skfd, s, &(winfo->b)) > -1) {
480 // set present winfo variables
481 if (iw_get_stats(skfd, s, &(winfo->stats),
482 &winfo->range, winfo->has_range) >= 0) {
483 winfo->has_stats = 1;
485 if (iw_get_range_info(skfd, s, &(winfo->range)) >= 0) {
486 winfo->has_range = 1;
488 if (iw_get_ext(skfd, s, SIOCGIWAP, &wrq) >= 0) {
489 winfo->has_ap_addr = 1;
490 memcpy(&(winfo->ap_addr), &(wrq.u.ap_addr), sizeof(sockaddr));
494 if (iw_get_ext(skfd, s, SIOCGIWRATE, &wrq) >= 0) {
495 memcpy(&(winfo->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
496 iw_print_bitrate(ns->bitrate, 16, winfo->bitrate.value);
501 if (winfo->has_range && winfo->has_stats
502 && ((winfo->stats.qual.level != 0)
503 || (winfo->stats.qual.updated & IW_QUAL_DBM))) {
504 if (!(winfo->stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
505 ns->link_qual = winfo->stats.qual.qual;
506 ns->link_qual_max = winfo->range.max_qual.qual;
511 if (winfo->has_ap_addr) {
512 iw_sawap_ntop(&winfo->ap_addr, ns->ap);
516 if (winfo->b.has_essid) {
517 if (winfo->b.essid_on) {
518 snprintf(ns->essid, 32, "%s", winfo->b.essid);
520 snprintf(ns->essid, 32, "off/any");
524 snprintf(ns->mode, 16, "%s", iw_operation_mode[winfo->b.mode]);
526 iw_sockets_close(skfd);
533 info.mask |= (1 << INFO_NET);
538 void update_total_processes(void)
542 struct sysinfo s_info;
545 info.procs = s_info.procs;
552 if (!(fp = open_file("/proc/loadavg", &rep))) {
556 fscanf(fp, "%*f %*f %*f %*d/%hu", &info.procs);
559 info.mask |= (1 << INFO_PROCS);
562 #define CPU_SAMPLE_COUNT 15
564 unsigned long long cpu_user;
565 unsigned long long cpu_system;
566 unsigned long long cpu_nice;
567 unsigned long long cpu_idle;
568 unsigned long long cpu_iowait;
569 unsigned long long cpu_irq;
570 unsigned long long cpu_softirq;
571 unsigned long long cpu_steal;
572 unsigned long long cpu_total;
573 unsigned long long cpu_active_total;
574 unsigned long long cpu_last_total;
575 unsigned long long cpu_last_active_total;
576 double cpu_val[CPU_SAMPLE_COUNT];
578 static short cpu_setup = 0;
580 /* Determine if this kernel gives us "extended" statistics information in
582 * Kernels around 2.5 and earlier only reported user, system, nice, and
583 * idle values in proc stat.
584 * Kernels around 2.6 and greater report these PLUS iowait, irq, softirq,
586 void determine_longstat(char *buf)
588 unsigned long long iowait = 0;
590 KFLAG_SETOFF(KFLAG_IS_LONGSTAT);
591 /* scanf will either return -1 or 1 because there is only 1 assignment */
592 if (sscanf(buf, "%*s %*d %*d %*d %*d %llu", &iowait) > 0) {
593 KFLAG_SETON(KFLAG_IS_LONGSTAT);
597 void get_cpu_count(void)
603 if (info.cpu_usage) {
607 if (!(stat_fp = open_file("/proc/stat", &rep))) {
613 while (!feof(stat_fp)) {
614 if (fgets(buf, 255, stat_fp) == NULL) {
618 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
619 if (info.cpu_count == 0) {
620 determine_longstat(buf);
625 info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
630 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
631 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
633 inline static void update_stat(void)
637 static struct cpu_info *cpu = NULL;
642 const char *stat_template = NULL;
643 unsigned int malloc_cpu_size = 0;
645 /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
646 if (!cpu_setup || !info.cpu_usage) {
651 if (!stat_template) {
653 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
657 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
658 cpu = malloc(malloc_cpu_size);
659 memset(cpu, 0, malloc_cpu_size);
662 if (!(stat_fp = open_file("/proc/stat", &rep))) {
664 if (info.cpu_usage) {
665 memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
671 while (!feof(stat_fp)) {
672 if (fgets(buf, 255, stat_fp) == NULL) {
676 if (strncmp(buf, "procs_running ", 14) == 0) {
677 sscanf(buf, "%*s %hu", &info.run_procs);
678 info.mask |= (1 << INFO_RUN_PROCS);
679 } else if (strncmp(buf, "cpu", 3) == 0) {
681 if (isdigit(buf[3])) {
682 idx = atoi(&buf[3]) + 1;
686 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
687 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
688 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
689 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
690 &(cpu[idx].cpu_steal));
692 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
693 cpu[idx].cpu_system + cpu[idx].cpu_idle +
694 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
695 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
697 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
698 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
699 info.mask |= (1 << INFO_CPU);
701 delta = current_update_time - last_update_time;
703 if (delta <= 0.001) {
707 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
708 cpu[idx].cpu_last_active_total) /
709 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
711 for (i = 0; i < info.cpu_avg_samples; i++) {
712 curtmp += cpu[idx].cpu_val[i];
714 /* TESTING -- I've removed this, because I don't think it is right.
715 * You shouldn't divide by the cpu count here ...
716 * removing for testing */
718 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
721 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
723 /* TESTING -- this line replaces the prev. "suspect" if/else */
724 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
726 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
727 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
728 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
729 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
736 void update_running_processes(void)
741 void update_cpu_usage(void)
746 void update_load_average(void)
748 #ifdef HAVE_GETLOADAVG
753 info.loadavg[0] = (float) v[0];
754 info.loadavg[1] = (float) v[1];
755 info.loadavg[2] = (float) v[2];
762 if (!(fp = open_file("/proc/loadavg", &rep))) {
763 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
766 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
770 info.mask |= (1 << INFO_LOADAVG);
773 #define PROC_I8K "/proc/i8k"
774 #define I8K_DELIM " "
775 static char *i8k_procbuf = NULL;
776 void update_i8k(void)
781 i8k_procbuf = (char *) malloc(128 * sizeof(char));
783 if ((fp = fopen(PROC_I8K, "r")) == NULL) {
784 CRIT_ERR("/proc/i8k doesn't exist! use insmod to make sure the kernel "
785 "driver is loaded...");
788 memset(&i8k_procbuf[0], 0, 128);
789 if (fread(&i8k_procbuf[0], sizeof(char), 128, fp) == 0) {
790 ERR("something wrong with /proc/i8k...");
795 i8k.version = strtok(&i8k_procbuf[0], I8K_DELIM);
796 i8k.bios = strtok(NULL, I8K_DELIM);
797 i8k.serial = strtok(NULL, I8K_DELIM);
798 i8k.cpu_temp = strtok(NULL, I8K_DELIM);
799 i8k.left_fan_status = strtok(NULL, I8K_DELIM);
800 i8k.right_fan_status = strtok(NULL, I8K_DELIM);
801 i8k.left_fan_rpm = strtok(NULL, I8K_DELIM);
802 i8k.right_fan_rpm = strtok(NULL, I8K_DELIM);
803 i8k.ac_status = strtok(NULL, I8K_DELIM);
804 i8k.buttons_status = strtok(NULL, I8K_DELIM);
807 /***********************************************************/
808 /***********************************************************/
809 /***********************************************************/
811 static int no_dots(const struct dirent *d)
813 if (d->d_name[0] == '.') {
819 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
821 struct dirent **namelist;
824 n = scandir(dir, &namelist, no_dots, alphasort);
827 ERR("scandir for %s: %s", dir, strerror(errno));
838 strncpy(s, namelist[0]->d_name, 255);
841 for (i = 0; i < n; i++) {
850 int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
851 int *divisor, char *devtype)
858 memset(buf, 0, sizeof(buf));
860 /* if device is NULL or *, get first */
861 if (dev == NULL || strcmp(dev, "*") == 0) {
864 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
870 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
872 /* buf holds result from get_first_file_in_a_directory() above,
873 * e.g. "hwmon0" -- append "/device" */
874 strcat(buf, "/device");
876 /* dev holds device number N as a string,
877 * e.g. "0", -- convert to "hwmon0/device" */
878 sprintf(buf, "hwmon%s/device", dev);
883 /* change vol to in */
884 if (strcmp(type, "vol") == 0) {
888 if (strcmp(type, "tempf") == 0) {
889 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, "temp", n);
891 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
893 strncpy(devtype, path, 255);
896 fd = open(path, O_RDONLY);
898 CRIT_ERR("can't open '%s': %s\nplease check your device or remove this "
899 "var from "PACKAGE_NAME, path, strerror(errno));
902 if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
903 || strcmp(type, "tempf") == 0) {
908 /* fan does not use *_div as a read divisor */
909 if (strcmp("fan", type) == 0) {
913 /* test if *_div file exist, open it and use it as divisor */
914 if (strcmp(type, "tempf") == 0) {
915 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
917 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
920 divfd = open(path, O_RDONLY);
926 divn = read(divfd, divbuf, 63);
927 /* should read until n == 0 but I doubt that kernel will give these
928 * in multiple pieces. :) */
930 ERR("open_sysfs_sensor(): can't read from sysfs");
933 *divisor = atoi(divbuf);
942 double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
950 lseek(*fd, 0, SEEK_SET);
956 n = read(*fd, buf, 63);
957 /* should read until n == 0 but I doubt that kernel will give these
958 * in multiple pieces. :) */
960 ERR("get_sysfs_info(): read from %s failed\n", devtype);
969 *fd = open(devtype, O_RDONLY);
971 ERR("can't open '%s': %s", devtype, strerror(errno));
974 /* My dirty hack for computing CPU value
975 * Filedil, from forums.gentoo.org */
976 /* if (strstr(devtype, "temp1_input") != NULL) {
977 return -15.096 + 1.4893 * (val / 1000.0);
980 /* divide voltage and temperature by 1000 */
981 /* or if any other divisor is given, use that */
982 if (strcmp(type, "tempf") == 0) {
984 return ((val / divisor + 40) * 9.0 / 5) - 40;
985 } else if (divisor) {
986 return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
988 return ((val + 40) * 9.0 / 5) - 40;
992 return val / divisor;
993 } else if (divisor) {
1001 /* Prior to kernel version 2.6.12, the CPU fan speed was available in
1002 * ADT746X_FAN_OLD, whereas later kernel versions provide this information in
1004 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
1005 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
1007 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
1010 char adt746x_fan_state[64];
1013 if (!p_client_buffer || client_buffer_size <= 0) {
1017 if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
1018 && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL) {
1019 sprintf(adt746x_fan_state, "adt746x not found");
1021 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
1022 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
1026 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_fan_state);
1029 /* Prior to kernel version 2.6.12, the CPU temperature was found in
1030 * ADT746X_CPU_OLD, whereas later kernel versions provide this information in
1032 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
1033 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
1035 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
1038 char adt746x_cpu_state[64];
1041 if (!p_client_buffer || client_buffer_size <= 0) {
1045 if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
1046 && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL) {
1047 sprintf(adt746x_cpu_state, "adt746x not found");
1049 fscanf(fp, "%2s", adt746x_cpu_state);
1053 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state);
1056 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1057 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1059 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1060 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1061 const char *p_format, int divisor, unsigned int cpu)
1069 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1075 char current_freq_file[128];
1077 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1079 f = fopen(current_freq_file, "r");
1081 /* if there's a cpufreq /sys node, read the current frequency from
1082 * this node and divide by 1000 to get Mhz. */
1083 if (fgets(s, sizeof(s), f)) {
1084 s[strlen(s) - 1] = '\0';
1085 freq = strtod(s, NULL);
1088 snprintf(p_client_buffer, client_buffer_size, p_format,
1089 (freq / 1000) / divisor);
1094 // open the CPU information file
1095 f = open_file("/proc/cpuinfo", &rep);
1097 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1102 while (fgets(s, sizeof(s), f) != NULL) {
1104 #if defined(__i386) || defined(__x86_64)
1105 // and search for the cpu mhz
1106 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1108 #if defined(__alpha)
1109 // different on alpha
1110 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1112 // this is different on ppc for some reason
1113 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1114 #endif // defined(__alpha)
1115 #endif // defined(__i386) || defined(__x86_64)
1117 // copy just the number
1118 strcpy(frequency, strchr(s, ':') + 2);
1119 #if defined(__alpha)
1121 frequency[strlen(frequency) - 6] = '\0';
1122 // kernel reports in Hz
1123 freq = strtod(frequency, NULL) / 1000000;
1126 frequency[strlen(frequency) - 1] = '\0';
1127 freq = strtod(frequency, NULL);
1131 if (strncmp(s, "processor", 9) == 0) {
1138 snprintf(p_client_buffer, client_buffer_size, p_format,
1139 (float) freq / divisor);
1143 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1145 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1155 * Peter Tarjan (ptarjan@citromail.hu) */
1157 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1158 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1159 const char *p_format, int divisor, unsigned int cpu)
1165 char current_freq_file[128];
1168 /* build the voltage file name */
1170 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1173 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1178 /* read the current cpu frequency from the /sys node */
1179 f = fopen(current_freq_file, "r");
1181 if (fgets(s, sizeof(s), f)) {
1182 s[strlen(s) - 1] = '\0';
1183 freq = strtod(s, NULL);
1187 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1188 perror("get_voltage()");
1195 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1198 /* use the current cpu frequency to find the corresponding voltage */
1199 f = fopen(current_freq_file, "r");
1205 if (fgets(line, 255, f) == NULL) {
1208 sscanf(line, "%d %d", &freq_comp, &voltage);
1209 if (freq_comp == freq) {
1215 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1216 perror("get_voltage()");
1222 snprintf(p_client_buffer, client_buffer_size, p_format,
1223 (float) voltage / divisor);
1227 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1229 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1236 if (!p_client_buffer || client_buffer_size <= 0) {
1240 /* yeah, slow... :/ */
1241 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1242 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1246 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1248 fp = open_file(buf2, &rep);
1250 snprintf(p_client_buffer, client_buffer_size,
1251 "can't open fan's state file");
1254 memset(buf, 0, sizeof(buf));
1255 fscanf(fp, "%*s %99s", buf);
1258 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1261 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1262 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1263 /* Linux 2.6.25 onwards ac adapter info is in
1264 /sys/class/power_supply/AC/
1265 On my system I get the following.
1266 /sys/class/power_supply/AC/uevent:
1267 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1270 POWER_SUPPLY_NAME=AC
1271 POWER_SUPPLY_TYPE=Mains
1272 POWER_SUPPLY_ONLINE=1
1275 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1283 if (!p_client_buffer || client_buffer_size <= 0) {
1287 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1288 fp = open_file(buf2, &rep);
1290 /* sysfs processing */
1292 if (fgets(buf, sizeof(buf), fp) == NULL)
1295 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1297 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1298 snprintf(p_client_buffer, client_buffer_size,
1299 "%s-line", (online ? "on" : "off"));
1305 /* yeah, slow... :/ */
1306 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1307 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1311 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1313 fp = open_file(buf2, &rep);
1315 snprintf(p_client_buffer, client_buffer_size,
1316 "No ac adapter found.... where is it?");
1319 memset(buf, 0, sizeof(buf));
1320 fscanf(fp, "%*s %99s", buf);
1323 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1328 /proc/acpi/thermal_zone/THRM/cooling_mode
1329 cooling mode: active
1330 /proc/acpi/thermal_zone/THRM/polling_frequency
1332 /proc/acpi/thermal_zone/THRM/state
1334 /proc/acpi/thermal_zone/THRM/temperature
1336 /proc/acpi/thermal_zone/THRM/trip_points
1338 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1341 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1342 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1344 int open_acpi_temperature(const char *name)
1350 if (name == NULL || strcmp(name, "*") == 0) {
1353 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1359 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1361 fd = open(path, O_RDONLY);
1363 ERR("can't open '%s': %s", path, strerror(errno));
1369 static double last_acpi_temp;
1370 static double last_acpi_temp_time;
1372 double get_acpi_temperature(int fd)
1378 /* don't update acpi temperature too often */
1379 if (current_update_time - last_acpi_temp_time < 11.32) {
1380 return last_acpi_temp;
1382 last_acpi_temp_time = current_update_time;
1384 /* seek to beginning */
1385 lseek(fd, 0, SEEK_SET);
1392 n = read(fd, buf, 255);
1394 ERR("can't read fd %d: %s", fd, strerror(errno));
1397 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1401 return last_acpi_temp;
1405 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1407 design capacity: 4400 mAh
1408 last full capacity: 4064 mAh
1409 battery technology: rechargeable
1410 design voltage: 14800 mV
1411 design capacity warning: 300 mAh
1412 design capacity low: 200 mAh
1413 capacity granularity 1: 32 mAh
1414 capacity granularity 2: 32 mAh
1416 serial number: 16922
1422 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1425 charging state: unknown
1427 remaining capacity: 4064 mAh
1428 present voltage: 16608 mV
1432 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1433 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1434 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1435 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1436 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1438 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1439 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1441 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1442 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1445 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1446 Linux 2.6.24 onwards battery info is in
1447 /sys/class/power_supply/BAT0/
1448 On my system I get the following.
1449 /sys/class/power_supply/BAT0/uevent:
1450 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1452 PHYSDEVDRIVER=battery
1453 POWER_SUPPLY_NAME=BAT0
1454 POWER_SUPPLY_TYPE=Battery
1455 POWER_SUPPLY_STATUS=Discharging
1456 POWER_SUPPLY_PRESENT=1
1457 POWER_SUPPLY_TECHNOLOGY=Li-ion
1458 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1459 POWER_SUPPLY_VOLTAGE_NOW=10780000
1460 POWER_SUPPLY_CURRENT_NOW=13970000
1461 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1462 POWER_SUPPLY_ENERGY_FULL=27370000
1463 POWER_SUPPLY_ENERGY_NOW=11810000
1464 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1465 POWER_SUPPLY_MANUFACTURER=Panasonic
1466 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1469 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1470 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1471 #define APM_PATH "/proc/apm"
1472 #define MAX_BATTERY_COUNT 4
1474 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1475 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1476 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1478 static int batteries_initialized = 0;
1479 static char batteries[MAX_BATTERY_COUNT][32];
1481 static int acpi_last_full[MAX_BATTERY_COUNT];
1482 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1484 /* e.g. "charging 75%" */
1485 static char last_battery_str[MAX_BATTERY_COUNT][64];
1487 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1489 static double last_battery_time[MAX_BATTERY_COUNT];
1491 static int last_battery_perct[MAX_BATTERY_COUNT];
1492 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1494 void init_batteries(void)
1498 if (batteries_initialized) {
1501 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1502 batteries[idx][0] = '\0';
1504 batteries_initialized = 1;
1507 int get_battery_idx(const char *bat)
1511 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1512 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1517 /* if not found, enter a new entry */
1518 if (!strlen(batteries[idx])) {
1519 snprintf(batteries[idx], 31, "%s", bat);
1525 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1527 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1529 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1530 char acpi_path[128];
1531 char sysfs_path[128];
1533 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1534 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1538 idx = get_battery_idx(bat);
1540 /* don't update battery too often */
1541 if (current_update_time - last_battery_time[idx] < 29.5) {
1542 set_return_value(buffer, n, item, idx);
1546 last_battery_time[idx] = current_update_time;
1548 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1549 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1551 /* first try SYSFS if that fails try ACPI */
1553 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1554 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1557 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1558 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1561 if (sysfs_bat_fp[idx] != NULL) {
1563 int present_rate = -1;
1564 int remaining_capacity = -1;
1565 char charging_state[64];
1568 strcpy(charging_state, "unknown");
1570 while (!feof(sysfs_bat_fp[idx])) {
1572 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1575 /* let's just hope units are ok */
1576 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1577 strcpy(present, "yes");
1578 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1579 strcpy(present, "no");
1580 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1581 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1582 /* present_rate is not the same as the
1583 current flowing now but it is the same value
1584 which was used in the past. so we continue
1586 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1587 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1588 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1589 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1590 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1591 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1592 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1593 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1594 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1595 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1598 fclose(sysfs_bat_fp[idx]);
1599 sysfs_bat_fp[idx] = NULL;
1601 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1602 if (remaining_capacity > acpi_last_full[idx])
1603 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1606 if (strcmp(present, "No") == 0) {
1607 strncpy(last_battery_str[idx], "not present", 64);
1610 else if (strcmp(charging_state, "Charging") == 0) {
1611 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1612 /* e.g. charging 75% */
1613 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1614 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1616 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1617 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1618 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1619 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1620 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1621 snprintf(last_battery_time_str[idx],
1622 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1624 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1625 snprintf(last_battery_time_str[idx],
1626 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1630 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1631 if (present_rate > 0) {
1632 /* e.g. discharging 35% */
1633 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1634 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1636 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1637 (long) (((float) remaining_capacity / present_rate) * 3600));
1638 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1639 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1640 snprintf(last_battery_time_str[idx],
1641 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1643 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1645 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1646 snprintf(last_battery_time_str[idx],
1647 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1651 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1652 else if (strncmp(charging_state, "Charged", 64) == 0) {
1653 /* Below happens with the second battery on my X40,
1654 * when the second one is empty and the first one
1656 if (remaining_capacity == 0)
1657 strcpy(last_battery_str[idx], "empty");
1659 strcpy(last_battery_str[idx], "charged");
1661 /* unknown, probably full / AC */
1663 if (acpi_last_full[idx] != 0
1664 && remaining_capacity != acpi_last_full[idx])
1665 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1666 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1668 strncpy(last_battery_str[idx], "AC", 64);
1670 } else if (acpi_bat_fp[idx] != NULL) {
1672 int present_rate = -1;
1673 int remaining_capacity = -1;
1674 char charging_state[64];
1677 /* read last full capacity if it's zero */
1678 if (acpi_last_full[idx] == 0) {
1679 static int rep3 = 0;
1683 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1684 fp = open_file(path, &rep3);
1689 if (fgets(b, 256, fp) == NULL) {
1692 if (sscanf(b, "last full capacity: %d",
1693 &acpi_last_full[idx]) != 0) {
1702 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1704 strcpy(charging_state, "unknown");
1706 while (!feof(acpi_bat_fp[idx])) {
1709 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1713 /* let's just hope units are ok */
1714 if (strncmp(buf, "present:", 8) == 0) {
1715 sscanf(buf, "present: %4s", present);
1716 } else if (strncmp(buf, "charging state:", 15) == 0) {
1717 sscanf(buf, "charging state: %63s", charging_state);
1718 } else if (strncmp(buf, "present rate:", 13) == 0) {
1719 sscanf(buf, "present rate: %d", &present_rate);
1720 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1721 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1724 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1725 if (remaining_capacity > acpi_last_full[idx]) {
1726 /* normalize to 100% */
1727 acpi_last_full[idx] = remaining_capacity;
1731 if (strcmp(present, "no") == 0) {
1732 strncpy(last_battery_str[idx], "not present", 64);
1734 } else if (strcmp(charging_state, "charging") == 0) {
1735 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1736 /* e.g. charging 75% */
1737 snprintf(last_battery_str[idx],
1738 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1739 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1741 format_seconds(last_battery_time_str[idx],
1742 sizeof(last_battery_time_str[idx]) - 1,
1743 (long) (((acpi_last_full[idx] - remaining_capacity) *
1744 3600) / present_rate));
1745 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1746 snprintf(last_battery_str[idx],
1747 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1748 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1749 snprintf(last_battery_time_str[idx],
1750 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1752 strncpy(last_battery_str[idx], "charging",
1753 sizeof(last_battery_str[idx]) - 1);
1754 snprintf(last_battery_time_str[idx],
1755 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1758 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1759 if (present_rate > 0) {
1760 /* e.g. discharging 35% */
1761 snprintf(last_battery_str[idx],
1762 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1763 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1765 format_seconds(last_battery_time_str[idx],
1766 sizeof(last_battery_time_str[idx]) - 1,
1767 (long) ((remaining_capacity * 3600) / present_rate));
1768 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1769 snprintf(last_battery_str[idx],
1770 sizeof(last_battery_str[idx]) - 1, "full");
1771 snprintf(last_battery_time_str[idx],
1772 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1774 snprintf(last_battery_str[idx],
1775 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1776 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1777 snprintf(last_battery_time_str[idx],
1778 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1781 } else if (strncmp(charging_state, "charged", 64) == 0) {
1782 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1783 /* Below happens with the second battery on my X40,
1784 * when the second one is empty and the first one being charged. */
1785 if (remaining_capacity == 0) {
1786 strcpy(last_battery_str[idx], "empty");
1788 strcpy(last_battery_str[idx], "charged");
1790 /* unknown, probably full / AC */
1792 if (acpi_last_full[idx] != 0
1793 && remaining_capacity != acpi_last_full[idx]) {
1794 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1795 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1797 strncpy(last_battery_str[idx], "AC", 64);
1802 if (apm_bat_fp[idx] == NULL) {
1803 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1806 if (apm_bat_fp[idx] != NULL) {
1807 unsigned int ac, status, flag;
1810 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1811 &ac, &status, &flag, &life);
1814 /* could check now that there is ac */
1815 snprintf(last_battery_str[idx], 64, "AC");
1817 /* could check that status == 3 here? */
1818 } else if (ac && life != 100) {
1819 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1821 snprintf(last_battery_str[idx], 64, "%d%%", life);
1824 /* it seemed to buffer it so file must be closed (or could use
1825 * syscalls directly but I don't feel like coding it now) */
1826 fclose(apm_bat_fp[idx]);
1827 apm_bat_fp[idx] = NULL;
1830 set_return_value(buffer, n, item, idx);
1833 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1836 case BATTERY_STATUS:
1837 snprintf(buffer, n, "%s", last_battery_str[idx]);
1840 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1847 int get_battery_perct(const char *bat)
1851 char acpi_path[128];
1852 char sysfs_path[128];
1853 int remaining_capacity = -1;
1855 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1856 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1860 idx = get_battery_idx(bat);
1862 /* don't update battery too often */
1863 if (current_update_time - last_battery_perct_time[idx] < 30) {
1864 return last_battery_perct[idx];
1866 last_battery_perct_time[idx] = current_update_time;
1868 /* Only check for SYSFS or ACPI */
1870 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1871 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1875 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1876 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1879 if (sysfs_bat_fp[idx] != NULL) {
1881 while (!feof(sysfs_bat_fp[idx])) {
1883 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1886 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1887 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1888 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1889 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1890 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1891 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1892 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1893 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1897 fclose(sysfs_bat_fp[idx]);
1898 sysfs_bat_fp[idx] = NULL;
1900 } else if (acpi_bat_fp[idx] != NULL) {
1902 /* read last full capacity if it's zero */
1903 if (acpi_design_capacity[idx] == 0) {
1908 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1909 fp = open_file(path, &rep2);
1914 if (fgets(b, 256, fp) == NULL) {
1917 if (sscanf(b, "last full capacity: %d",
1918 &acpi_design_capacity[idx]) != 0) {
1926 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1928 while (!feof(acpi_bat_fp[idx])) {
1931 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1935 if (buf[0] == 'r') {
1936 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1940 if (remaining_capacity < 0) {
1943 /* compute the battery percentage */
1944 last_battery_perct[idx] =
1945 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
1946 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
1947 return last_battery_perct[idx];
1950 int get_battery_perct_bar(const char *bar)
1954 get_battery_perct(bar);
1955 idx = get_battery_idx(bar);
1956 return (int) (last_battery_perct[idx] * 2.56 - 1);
1959 /* On Apple powerbook and ibook:
1960 $ cat /proc/pmu/battery_0
1967 $ cat /proc/pmu/info
1968 PMU driver version : 2
1969 PMU firmware version : 0c
1974 /* defines as in <linux/pmu.h> */
1975 #define PMU_BATT_PRESENT 0x00000001
1976 #define PMU_BATT_CHARGING 0x00000002
1978 static FILE *pmu_battery_fp;
1979 static FILE *pmu_info_fp;
1980 static char pb_battery_info[3][32];
1981 static double pb_battery_info_update;
1983 #define PMU_PATH "/proc/pmu"
1984 void get_powerbook_batt_info(char *buffer, size_t n, int i)
1987 const char *batt_path = PMU_PATH "/battery_0";
1988 const char *info_path = PMU_PATH "/info";
1990 int charge, max_charge, ac = -1;
1993 /* don't update battery too often */
1994 if (current_update_time - pb_battery_info_update < 29.5) {
1995 snprintf(buffer, n, "%s", pb_battery_info[i]);
1998 pb_battery_info_update = current_update_time;
2000 if (pmu_battery_fp == NULL) {
2001 pmu_battery_fp = open_file(batt_path, &rep);
2004 if (pmu_battery_fp != NULL) {
2005 rewind(pmu_battery_fp);
2006 while (!feof(pmu_battery_fp)) {
2009 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2013 if (buf[0] == 'f') {
2014 sscanf(buf, "flags : %8x", &flags);
2015 } else if (buf[0] == 'c' && buf[1] == 'h') {
2016 sscanf(buf, "charge : %d", &charge);
2017 } else if (buf[0] == 'm') {
2018 sscanf(buf, "max_charge : %d", &max_charge);
2019 } else if (buf[0] == 't') {
2020 sscanf(buf, "time rem. : %ld", &timeval);
2024 if (pmu_info_fp == NULL) {
2025 pmu_info_fp = open_file(info_path, &rep);
2028 if (pmu_info_fp != NULL) {
2029 rewind(pmu_info_fp);
2030 while (!feof(pmu_info_fp)) {
2033 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2036 if (buf[0] == 'A') {
2037 sscanf(buf, "AC Power : %d", &ac);
2041 /* update status string */
2042 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2043 strcpy(pb_battery_info[PB_BATT_STATUS], "AC");
2044 } else if (ac && (flags & PMU_BATT_PRESENT)
2045 && !(flags & PMU_BATT_CHARGING)) {
2046 strcpy(pb_battery_info[PB_BATT_STATUS], "charged");
2047 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2048 strcpy(pb_battery_info[PB_BATT_STATUS], "charging");
2050 strcpy(pb_battery_info[PB_BATT_STATUS], "discharging");
2053 /* update percentage string */
2055 pb_battery_info[PB_BATT_PERCENT][0] = 0;
2057 snprintf(pb_battery_info[PB_BATT_PERCENT],
2058 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2059 (charge * 100) / max_charge);
2062 /* update time string */
2063 if (timeval == 0) { /* fully charged or battery not present */
2064 pb_battery_info[PB_BATT_TIME][0] = 0;
2065 } else if (timeval < 60 * 60) { /* don't show secs */
2066 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2067 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2069 format_seconds(pb_battery_info[PB_BATT_TIME],
2070 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2073 snprintf(buffer, n, "%s", pb_battery_info[i]);
2076 void update_top(void)
2078 show_nice_processes = 1;
2079 process_find_top(info.cpu, info.memu);
2080 info.first_process = get_first_process();
2083 /* Here come the IBM ACPI-specific things. For reference, see
2084 * http://ibm-acpi.sourceforge.net/README
2085 * If IBM ACPI is installed, /proc/acpi/ibm contains the following files:
2101 * The content of these files is described in detail in the aforementioned
2102 * README - some of them also in the following functions accessing them.
2103 * Peter Tarjan (ptarjan@citromail.hu) */
2105 #define IBM_ACPI_DIR "/proc/acpi/ibm"
2107 /* get fan speed on IBM/Lenovo laptops running the ibm acpi.
2108 * /proc/acpi/ibm/fan looks like this (3 lines):
2111 commands: enable, disable
2112 * Peter Tarjan (ptarjan@citromail.hu) */
2114 void get_ibm_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
2117 unsigned int speed = 0;
2120 if (!p_client_buffer || client_buffer_size <= 0) {
2124 snprintf(fan, 127, "%s/fan", IBM_ACPI_DIR);
2126 fp = fopen(fan, "r");
2131 if (fgets(line, 255, fp) == NULL) {
2134 if (sscanf(line, "speed: %u", &speed)) {
2139 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2140 "ibm* from your "PACKAGE_NAME" config file.", fan, strerror(errno));
2144 snprintf(p_client_buffer, client_buffer_size, "%d", speed);
2147 /* get the measured temperatures from the temperature sensors
2148 * on IBM/Lenovo laptops running the ibm acpi.
2149 * There are 8 values in /proc/acpi/ibm/thermal, and according to
2150 * http://ibm-acpi.sourceforge.net/README
2151 * these mean the following (at least on an IBM R51...)
2152 * 0: CPU (also on the T series laptops)
2153 * 1: Mini PCI Module (?)
2155 * 3: GPU (also on the T series laptops)
2160 * I'm not too sure about those with the question mark, but the values I'm
2161 * reading from *my* thermal file (on a T42p) look realistic for the
2162 * hdd and the battery.
2163 * #5 and #7 are always -128.
2164 * /proc/acpi/ibm/thermal looks like this (1 line):
2165 temperatures: 41 43 31 46 33 -128 29 -128
2166 * Peter Tarjan (ptarjan@citromail.hu) */
2168 static double last_ibm_acpi_temp_time;
2169 void get_ibm_acpi_temps(void)
2175 /* don't update too often */
2176 if (current_update_time - last_ibm_acpi_temp_time < 10.00) {
2179 last_ibm_acpi_temp_time = current_update_time;
2181 /* if (!p_client_buffer || client_buffer_size <= 0) {
2185 snprintf(thermal, 127, "%s/thermal", IBM_ACPI_DIR);
2186 fp = fopen(thermal, "r");
2192 if (fgets(line, 255, fp) == NULL) {
2195 if (sscanf(line, "temperatures: %d %d %d %d %d %d %d %d",
2196 &ibm_acpi.temps[0], &ibm_acpi.temps[1], &ibm_acpi.temps[2],
2197 &ibm_acpi.temps[3], &ibm_acpi.temps[4], &ibm_acpi.temps[5],
2198 &ibm_acpi.temps[6], &ibm_acpi.temps[7])) {
2203 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2204 "ibm* from your "PACKAGE_NAME" config file.", thermal, strerror(errno));
2210 /* get volume (0-14) on IBM/Lenovo laptops running the ibm acpi.
2211 * "Volume" here is none of the mixer volumes, but a "master of masters"
2212 * volume adjusted by the IBM volume keys.
2213 * /proc/acpi/ibm/fan looks like this (4 lines):
2216 commands: up, down, mute
2217 commands: level <level> (<level> is 0-15)
2218 * Peter Tarjan (ptarjan@citromail.hu) */
2220 void get_ibm_acpi_volume(char *p_client_buffer, size_t client_buffer_size)
2224 unsigned int vol = -1;
2227 if (!p_client_buffer || client_buffer_size <= 0) {
2231 snprintf(volume, 127, "%s/volume", IBM_ACPI_DIR);
2233 fp = fopen(volume, "r");
2237 unsigned int read_vol = -1;
2239 if (fgets(line, 255, fp) == NULL) {
2242 if (sscanf(line, "level: %u", &read_vol)) {
2246 if (sscanf(line, "mute: %s", mute)) {
2251 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2252 "ibm* from your "PACKAGE_NAME" config file.", volume, strerror(errno));
2257 if (strcmp(mute, "on") == 0) {
2258 snprintf(p_client_buffer, client_buffer_size, "%s", "mute");
2261 snprintf(p_client_buffer, client_buffer_size, "%d", vol);
2266 /* static FILE *fp = NULL; */
2268 /* get LCD brightness on IBM/Lenovo laptops running the ibm acpi.
2269 * /proc/acpi/ibm/brightness looks like this (3 lines):
2272 commands: level <level> (<level> is 0-7)
2273 * Peter Tarjan (ptarjan@citromail.hu) */
2275 void get_ibm_acpi_brightness(char *p_client_buffer, size_t client_buffer_size)
2278 unsigned int brightness = 0;
2281 if (!p_client_buffer || client_buffer_size <= 0) {
2285 snprintf(filename, 127, "%s/brightness", IBM_ACPI_DIR);
2287 fp = fopen(filename, "r");
2292 if (fgets(line, 255, fp) == NULL) {
2295 if (sscanf(line, "level: %u", &brightness)) {
2300 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2301 "ibm* from your "PACKAGE_NAME" config file.", filename, strerror(errno));
2306 snprintf(p_client_buffer, client_buffer_size, "%d", brightness);
2309 void update_entropy(void)
2312 const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2313 const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2316 info.entropy.entropy_avail = 0;
2317 info.entropy.poolsize = 0;
2319 if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2323 if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2328 fscanf(fp1, "%u", &info.entropy.entropy_avail);
2329 fscanf(fp2, "%u", &info.entropy.poolsize);
2334 info.mask |= (1 << INFO_ENTROPY);
2337 const char *get_disk_protect_queue(const char *disk)
2343 snprintf(path, 127, "/sys/block/%s/device/unload_heads", disk);
2344 if (access(path, F_OK)) {
2345 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2347 if ((fp = fopen(path, "r")) == NULL)
2349 if (fscanf(fp, "%d\n", &state) != 1) {
2354 return (state > 0) ? "frozen" : "free ";