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/>.
34 #include <sys/types.h>
35 #include <sys/sysinfo.h>
37 #ifndef HAVE_CLOCK_GETTIME
42 // #include <assert.h>
46 #include <sys/ioctl.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <linux/sockios.h>
51 #include <arpa/inet.h>
55 #include <linux/route.h>
62 #define SHORTSTAT_TEMPL "%*s %llu %llu %llu"
63 #define LONGSTAT_TEMPL "%*s %llu %llu %llu "
65 static int show_nice_processes;
67 /* This flag tells the linux routines to use the /proc system where possible,
68 * even if other api's are available, e.g. sysinfo() or getloadavg().
69 * the reason for this is to allow for /proc-based distributed monitoring.
70 * using a flag in this manner creates less confusing code. */
71 static int prefer_proc = 0;
73 void prepare_update(void)
77 void update_uptime(void)
81 struct sysinfo s_info;
84 info.uptime = (double) s_info.uptime;
91 if (!(fp = open_file("/proc/uptime", &rep))) {
95 fscanf(fp, "%lf", &info.uptime);
98 info.mask |= (1 << INFO_UPTIME);
101 int check_mount(char *s)
104 FILE *mtab = fopen("/etc/mtab", "r");
107 char buf1[256], buf2[128];
109 while (fgets(buf1, 256, mtab)) {
110 sscanf(buf1, "%*s %128s", buf2);
111 if (!strcmp(s, buf2)) {
118 ERR("Could not open mtab");
123 /* these things are also in sysinfo except Buffers:
124 * (that's why I'm reading them from proc) */
126 void update_meminfo(void)
131 /* unsigned int a; */
134 info.mem = info.memmax = info.swap = info.swapmax = info.bufmem =
135 info.buffers = info.cached = info.memfree = info.memeasyfree = 0;
137 if (!(meminfo_fp = open_file("/proc/meminfo", &rep))) {
141 while (!feof(meminfo_fp)) {
142 if (fgets(buf, 255, meminfo_fp) == NULL) {
146 if (strncmp(buf, "MemTotal:", 9) == 0) {
147 sscanf(buf, "%*s %llu", &info.memmax);
148 } else if (strncmp(buf, "MemFree:", 8) == 0) {
149 sscanf(buf, "%*s %llu", &info.memfree);
150 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
151 sscanf(buf, "%*s %llu", &info.swapmax);
152 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
153 sscanf(buf, "%*s %llu", &info.swap);
154 } else if (strncmp(buf, "Buffers:", 8) == 0) {
155 sscanf(buf, "%*s %llu", &info.buffers);
156 } else if (strncmp(buf, "Cached:", 7) == 0) {
157 sscanf(buf, "%*s %llu", &info.cached);
161 info.mem = info.memmax - info.memfree;
162 info.memeasyfree = info.memfree;
163 info.swap = info.swapmax - info.swap;
165 info.bufmem = info.cached + info.buffers;
167 info.mask |= (1 << INFO_MEM) | (1 << INFO_BUFFERS);
172 int get_laptop_mode(void)
177 if ((fp = fopen("/proc/sys/vm/laptop_mode", "r")) != NULL)
178 fscanf(fp, "%d\n", &val);
184 * # cat /sys/block/sda/queue/scheduler
185 * noop [anticipatory] cfq
187 char *get_ioscheduler(char *disk)
193 return strndup("n/a", text_buffer_size);
195 snprintf(buf, 127, "/sys/block/%s/queue/scheduler", disk);
196 if ((fp = fopen(buf, "r")) == NULL) {
197 return strndup("n/a", text_buffer_size);
200 fscanf(fp, "%127s", buf);
202 buf[strlen(buf) - 1] = '\0';
204 return strndup(buf + 1, text_buffer_size);
208 return strndup("n/a", text_buffer_size);
211 int interface_up(const char *dev)
216 if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
217 CRIT_ERR("could not create sockfd");
220 strncpy(ifr.ifr_name, dev, IFNAMSIZ);
221 if (ioctl(fd, SIOCGIFFLAGS, &ifr)) {
222 /* if device does not exist, treat like not up */
224 perror("SIOCGIFFLAGS");
228 if (!(ifr.ifr_flags & IFF_UP)) /* iface is not up */
230 if (ifup_strictness == IFUP_UP)
233 if (!(ifr.ifr_flags & IFF_RUNNING))
235 if (ifup_strictness == IFUP_LINK)
238 if (ioctl(fd, SIOCGIFADDR, &ifr)) {
239 perror("SIOCGIFADDR");
242 if (((struct sockaddr_in *)&(ifr.ifr_ifru.ifru_addr))->sin_addr.s_addr)
253 #define COND_FREE(x) if(x) free(x); x = 0
254 #define SAVE_SET_STRING(x, y) \
255 if (x && strcmp((char *)x, (char *)y)) { \
257 x = strndup("multiple", text_buffer_size); \
259 x = strndup(y, text_buffer_size); \
262 void update_gateway_info_failure(const char *reason)
267 //2 pointers to 1 location causes a crash when we try to free them both
268 info.gw_info.iface = strndup("failed", text_buffer_size);
269 info.gw_info.ip = strndup("failed", text_buffer_size);
272 void update_gateway_info(void)
277 unsigned long dest, gate, mask;
279 short ref, use, metric, mtu, win, irtt;
281 struct gateway_info *gw_info = &info.gw_info;
283 COND_FREE(gw_info->iface);
284 COND_FREE(gw_info->ip);
287 if ((fp = fopen("/proc/net/route", "r")) == NULL) {
288 update_gateway_info_failure("fopen()");
291 if (fscanf(fp, "%*[^\n]\n") == EOF) {
292 //NULL because a empty table is not a error
293 update_gateway_info_failure(NULL);
298 // Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
299 if(fscanf(fp, "%63s %lx %lx %x %hd %hd %hd %lx %hd %hd %hd\n",
300 iface, &dest, &gate, &flags, &ref, &use,
301 &metric, &mask, &mtu, &win, &irtt) != 11) {
302 update_gateway_info_failure("fscanf()");
306 if (flags & RTF_GATEWAY && dest == 0 && mask == 0) {
308 SAVE_SET_STRING(gw_info->iface, iface)
310 SAVE_SET_STRING(gw_info->ip, inet_ntoa(ina))
317 void update_net_stats(void)
322 // FIXME: arbitrary size chosen to keep code simple.
324 unsigned int curtmp1, curtmp2;
331 // wireless info variables
332 int skfd, has_bitrate = 0;
333 struct wireless_info *winfo;
338 delta = current_update_time - last_update_time;
339 if (delta <= 0.0001) {
343 /* open file and ignore first two lines */
344 if (!(net_dev_fp = open_file("/proc/net/dev", &rep))) {
349 fgets(buf, 255, net_dev_fp); /* garbage */
350 fgets(buf, 255, net_dev_fp); /* garbage (field names) */
352 /* read each interface */
353 for (i2 = 0; i2 < 16; i2++) {
357 long long r, t, last_recv, last_trans;
359 if (fgets(buf, 255, net_dev_fp) == NULL) {
363 while (isspace((int) *p)) {
369 while (*p && *p != ':') {
378 ns = get_net_stat(s);
380 memset(&(ns->addr.sa_data), 0, 14);
382 if(NULL == ns->addrs)
383 ns->addrs = (char*) malloc(17 * 16);
384 if(NULL != ns->addrs)
385 memset(ns->addrs, 0, 17 * 16); /* Up to 17 chars per ip, max 16 interfaces. Nasty memory usage... */
387 last_recv = ns->recv;
388 last_trans = ns->trans;
390 /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
391 sscanf(p, "%lld %*d %*d %*d %*d %*d %*d %*d %lld",
394 /* if recv or trans is less than last time, an overflow happened */
395 if (r < ns->last_read_recv) {
398 ns->recv += (r - ns->last_read_recv);
400 ns->last_read_recv = r;
402 if (t < ns->last_read_trans) {
405 ns->trans += (t - ns->last_read_trans);
407 ns->last_read_trans = t;
409 /*** ip addr patch ***/
410 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
412 conf.ifc_buf = malloc(sizeof(struct ifreq) * 16);
413 conf.ifc_len = sizeof(struct ifreq) * 16;
414 memset(conf.ifc_buf, 0, conf.ifc_len);
416 ioctl((long) i, SIOCGIFCONF, &conf);
418 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
419 struct net_stat *ns2;
421 if (!(((struct ifreq *) conf.ifc_buf) + k))
425 ((struct ifreq *) conf.ifc_buf)[k].ifr_ifrn.ifrn_name);
426 ns2->addr = ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.ifru_addr;
427 if(NULL != ns2->addrs) {
428 sprintf(temp_addr, "%u.%u.%u.%u, ",
429 ns2->addr.sa_data[2] & 255,
430 ns2->addr.sa_data[3] & 255,
431 ns2->addr.sa_data[4] & 255,
432 ns2->addr.sa_data[5] & 255);
433 if(NULL == strstr(ns2->addrs, temp_addr))
434 strncpy(ns2->addrs + strlen(ns2->addrs), temp_addr, 17);
442 /*** end ip addr patch ***/
444 /* calculate speeds */
445 ns->net_rec[0] = (ns->recv - last_recv) / delta;
446 ns->net_trans[0] = (ns->trans - last_trans) / delta;
450 for (i = 0; (unsigned) i < info.net_avg_samples; i++) {
451 curtmp1 += ns->net_rec[i];
452 curtmp2 += ns->net_trans[i];
460 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
461 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
462 if (info.net_avg_samples > 1) {
463 for (i = info.net_avg_samples; i > 1; i--) {
464 ns->net_rec[i - 1] = ns->net_rec[i - 2];
465 ns->net_trans[i - 1] = ns->net_trans[i - 2];
470 /* update wireless info */
471 winfo = malloc(sizeof(struct wireless_info));
472 memset(winfo, 0, sizeof(struct wireless_info));
474 skfd = iw_sockets_open();
475 if (iw_get_basic_config(skfd, s, &(winfo->b)) > -1) {
477 // set present winfo variables
478 if (iw_get_stats(skfd, s, &(winfo->stats),
479 &winfo->range, winfo->has_range) >= 0) {
480 winfo->has_stats = 1;
482 if (iw_get_range_info(skfd, s, &(winfo->range)) >= 0) {
483 winfo->has_range = 1;
485 if (iw_get_ext(skfd, s, SIOCGIWAP, &wrq) >= 0) {
486 winfo->has_ap_addr = 1;
487 memcpy(&(winfo->ap_addr), &(wrq.u.ap_addr), sizeof(sockaddr));
491 if (iw_get_ext(skfd, s, SIOCGIWRATE, &wrq) >= 0) {
492 memcpy(&(winfo->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
493 iw_print_bitrate(ns->bitrate, 16, winfo->bitrate.value);
498 if (winfo->has_range && winfo->has_stats
499 && ((winfo->stats.qual.level != 0)
500 || (winfo->stats.qual.updated & IW_QUAL_DBM))) {
501 if (!(winfo->stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
502 ns->link_qual = winfo->stats.qual.qual;
503 ns->link_qual_max = winfo->range.max_qual.qual;
508 if (winfo->has_ap_addr) {
509 iw_sawap_ntop(&winfo->ap_addr, ns->ap);
513 if (winfo->b.has_essid) {
514 if (winfo->b.essid_on) {
515 snprintf(ns->essid, 32, "%s", winfo->b.essid);
517 snprintf(ns->essid, 32, "off/any");
521 snprintf(ns->mode, 16, "%s", iw_operation_mode[winfo->b.mode]);
523 iw_sockets_close(skfd);
530 info.mask |= (1 << INFO_NET);
535 void update_total_processes(void)
539 struct sysinfo s_info;
542 info.procs = s_info.procs;
549 if (!(fp = open_file("/proc/loadavg", &rep))) {
553 fscanf(fp, "%*f %*f %*f %*d/%hu", &info.procs);
556 info.mask |= (1 << INFO_PROCS);
559 #define CPU_SAMPLE_COUNT 15
561 unsigned long long cpu_user;
562 unsigned long long cpu_system;
563 unsigned long long cpu_nice;
564 unsigned long long cpu_idle;
565 unsigned long long cpu_iowait;
566 unsigned long long cpu_irq;
567 unsigned long long cpu_softirq;
568 unsigned long long cpu_steal;
569 unsigned long long cpu_total;
570 unsigned long long cpu_active_total;
571 unsigned long long cpu_last_total;
572 unsigned long long cpu_last_active_total;
573 double cpu_val[CPU_SAMPLE_COUNT];
575 static short cpu_setup = 0;
577 /* Determine if this kernel gives us "extended" statistics information in
579 * Kernels around 2.5 and earlier only reported user, system, nice, and
580 * idle values in proc stat.
581 * Kernels around 2.6 and greater report these PLUS iowait, irq, softirq,
583 void determine_longstat(char *buf)
585 unsigned long long iowait = 0;
587 KFLAG_SETOFF(KFLAG_IS_LONGSTAT);
588 /* scanf will either return -1 or 1 because there is only 1 assignment */
589 if (sscanf(buf, "%*s %*d %*d %*d %*d %llu", &iowait) > 0) {
590 KFLAG_SETON(KFLAG_IS_LONGSTAT);
594 void get_cpu_count(void)
600 if (info.cpu_usage) {
604 if (!(stat_fp = open_file("/proc/stat", &rep))) {
610 while (!feof(stat_fp)) {
611 if (fgets(buf, 255, stat_fp) == NULL) {
615 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
616 if (info.cpu_count == 0) {
617 determine_longstat(buf);
622 info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
627 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
628 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
630 inline static void update_stat(void)
634 static struct cpu_info *cpu = NULL;
639 const char *stat_template = NULL;
640 unsigned int malloc_cpu_size = 0;
642 /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
643 if (!cpu_setup || !info.cpu_usage) {
648 if (!stat_template) {
650 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
654 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
655 cpu = malloc(malloc_cpu_size);
656 memset(cpu, 0, malloc_cpu_size);
659 if (!(stat_fp = open_file("/proc/stat", &rep))) {
661 if (info.cpu_usage) {
662 memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
668 while (!feof(stat_fp)) {
669 if (fgets(buf, 255, stat_fp) == NULL) {
673 if (strncmp(buf, "procs_running ", 14) == 0) {
674 sscanf(buf, "%*s %hu", &info.run_procs);
675 info.mask |= (1 << INFO_RUN_PROCS);
676 } else if (strncmp(buf, "cpu", 3) == 0) {
678 idx = isdigit(buf[3]) ? ((int) buf[3]) - 0x2F : 0;
679 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
680 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
681 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
682 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
683 &(cpu[idx].cpu_steal));
685 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
686 cpu[idx].cpu_system + cpu[idx].cpu_idle +
687 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
688 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
690 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
691 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
692 info.mask |= (1 << INFO_CPU);
694 delta = current_update_time - last_update_time;
696 if (delta <= 0.001) {
700 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
701 cpu[idx].cpu_last_active_total) /
702 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
704 for (i = 0; i < info.cpu_avg_samples; i++) {
705 curtmp += cpu[idx].cpu_val[i];
707 /* TESTING -- I've removed this, because I don't think it is right.
708 * You shouldn't divide by the cpu count here ...
709 * removing for testing */
711 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
714 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
716 /* TESTING -- this line replaces the prev. "suspect" if/else */
717 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
719 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
720 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
721 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
722 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
729 void update_running_processes(void)
734 void update_cpu_usage(void)
739 void update_load_average(void)
741 #ifdef HAVE_GETLOADAVG
746 info.loadavg[0] = (float) v[0];
747 info.loadavg[1] = (float) v[1];
748 info.loadavg[2] = (float) v[2];
755 if (!(fp = open_file("/proc/loadavg", &rep))) {
756 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
759 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
763 info.mask |= (1 << INFO_LOADAVG);
766 #define PROC_I8K "/proc/i8k"
767 #define I8K_DELIM " "
768 static char *i8k_procbuf = NULL;
769 void update_i8k(void)
774 i8k_procbuf = (char *) malloc(128 * sizeof(char));
776 if ((fp = fopen(PROC_I8K, "r")) == NULL) {
777 CRIT_ERR("/proc/i8k doesn't exist! use insmod to make sure the kernel "
778 "driver is loaded...");
781 memset(&i8k_procbuf[0], 0, 128);
782 if (fread(&i8k_procbuf[0], sizeof(char), 128, fp) == 0) {
783 ERR("something wrong with /proc/i8k...");
788 i8k.version = strtok(&i8k_procbuf[0], I8K_DELIM);
789 i8k.bios = strtok(NULL, I8K_DELIM);
790 i8k.serial = strtok(NULL, I8K_DELIM);
791 i8k.cpu_temp = strtok(NULL, I8K_DELIM);
792 i8k.left_fan_status = strtok(NULL, I8K_DELIM);
793 i8k.right_fan_status = strtok(NULL, I8K_DELIM);
794 i8k.left_fan_rpm = strtok(NULL, I8K_DELIM);
795 i8k.right_fan_rpm = strtok(NULL, I8K_DELIM);
796 i8k.ac_status = strtok(NULL, I8K_DELIM);
797 i8k.buttons_status = strtok(NULL, I8K_DELIM);
800 /***********************************************************/
801 /***********************************************************/
802 /***********************************************************/
804 static int no_dots(const struct dirent *d)
806 if (d->d_name[0] == '.') {
812 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
814 struct dirent **namelist;
817 n = scandir(dir, &namelist, no_dots, alphasort);
820 ERR("scandir for %s: %s", dir, strerror(errno));
831 strncpy(s, namelist[0]->d_name, 255);
834 for (i = 0; i < n; i++) {
843 int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
844 int *divisor, char *devtype)
851 memset(buf, 0, sizeof(buf));
853 /* if device is NULL or *, get first */
854 if (dev == NULL || strcmp(dev, "*") == 0) {
857 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
863 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
865 /* buf holds result from get_first_file_in_a_directory() above,
866 * e.g. "hwmon0" -- append "/device" */
867 strcat(buf, "/device");
869 /* dev holds device number N as a string,
870 * e.g. "0", -- convert to "hwmon0/device" */
871 sprintf(buf, "hwmon%s/device", dev);
876 /* change vol to in */
877 if (strcmp(type, "vol") == 0) {
881 if (strcmp(type, "tempf") == 0) {
882 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, "temp", n);
884 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 /* Thanks to "Walt Nelson" <wnelsonjr@comcast.net> */
1051 /***********************************************************************/
1052 /* This file is part of x86info.
1053 * (C) 2001 Dave Jones.
1055 * Licensed under the terms of the GNU GPL License version 2.
1057 * Estimate CPU MHz routine by Andrea Arcangeli <andrea@suse.de>
1058 * Small changes by David Sterba <sterd9am@ss1000.ms.mff.cuni.cz> */
1060 #if defined(__i386) || defined(__x86_64)
1061 unsigned long long int rdtsc(void)
1063 unsigned long long int x;
1065 __asm__ volatile(".byte 0x0f, 0x31":"=A" (x));
1070 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1071 void get_freq_dynamic(char *p_client_buffer, size_t client_buffer_size,
1072 const char *p_format, int divisor)
1074 #if defined(__i386) || defined(__x86_64)
1076 struct timeval tvstart, tvstop;
1077 unsigned long long cycles[2]; /* gotta be 64 bit */
1078 unsigned int microseconds; /* total time taken */
1080 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1085 memset(&tz, 0, sizeof(tz));
1087 /* get this function in cached memory */
1088 gettimeofday(&tvstart, &tz);
1089 cycles[0] = rdtsc();
1090 gettimeofday(&tvstart, &tz);
1092 /* we don't trust that this is any specific length of time */
1094 cycles[1] = rdtsc();
1095 gettimeofday(&tvstop, &tz);
1096 microseconds = ((tvstop.tv_sec - tvstart.tv_sec) * 1000000) +
1097 (tvstop.tv_usec - tvstart.tv_usec);
1099 snprintf(p_client_buffer, client_buffer_size, p_format,
1100 (float) ((cycles[1] - cycles[0]) / microseconds) / divisor);
1103 /* FIXME: hardwired: get freq for first cpu!
1104 * this whole function needs to be rethought and redone for
1105 * multi-cpu/multi-core/multi-threaded environments and
1106 * arbitrary combinations thereof */
1107 get_freq(p_client_buffer, client_buffer_size, p_format, divisor, 1);
1112 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1113 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1115 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1116 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1117 const char *p_format, int divisor, unsigned int cpu)
1125 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1131 char current_freq_file[128];
1133 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1135 f = fopen(current_freq_file, "r");
1137 /* if there's a cpufreq /sys node, read the current frequency from
1138 * this node and divide by 1000 to get Mhz. */
1139 if (fgets(s, sizeof(s), f)) {
1140 s[strlen(s) - 1] = '\0';
1141 freq = strtod(s, NULL);
1144 snprintf(p_client_buffer, client_buffer_size, p_format,
1145 (freq / 1000) / divisor);
1150 // open the CPU information file
1151 f = open_file("/proc/cpuinfo", &rep);
1153 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1158 while (fgets(s, sizeof(s), f) != NULL) {
1160 #if defined(__i386) || defined(__x86_64)
1161 // and search for the cpu mhz
1162 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1164 #if defined(__alpha)
1165 // different on alpha
1166 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1168 // this is different on ppc for some reason
1169 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1170 #endif // defined(__alpha)
1171 #endif // defined(__i386) || defined(__x86_64)
1173 // copy just the number
1174 strcpy(frequency, strchr(s, ':') + 2);
1175 #if defined(__alpha)
1177 frequency[strlen(frequency) - 6] = '\0';
1178 // kernel reports in Hz
1179 freq = strtod(frequency, NULL) / 1000000;
1182 frequency[strlen(frequency) - 1] = '\0';
1183 freq = strtod(frequency, NULL);
1187 if (strncmp(s, "processor", 9) == 0) {
1194 snprintf(p_client_buffer, client_buffer_size, p_format,
1195 (float) freq / divisor);
1199 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1201 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1211 * Peter Tarjan (ptarjan@citromail.hu) */
1213 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1214 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1215 const char *p_format, int divisor, unsigned int cpu)
1221 char current_freq_file[128];
1224 /* build the voltage file name */
1226 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1229 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1234 /* read the current cpu frequency from the /sys node */
1235 f = fopen(current_freq_file, "r");
1237 if (fgets(s, sizeof(s), f)) {
1238 s[strlen(s) - 1] = '\0';
1239 freq = strtod(s, NULL);
1243 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1244 perror("get_voltage()");
1251 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1254 /* use the current cpu frequency to find the corresponding voltage */
1255 f = fopen(current_freq_file, "r");
1261 if (fgets(line, 255, f) == NULL) {
1264 sscanf(line, "%d %d", &freq_comp, &voltage);
1265 if (freq_comp == freq) {
1271 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1272 perror("get_voltage()");
1278 snprintf(p_client_buffer, client_buffer_size, p_format,
1279 (float) voltage / divisor);
1283 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1285 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1292 if (!p_client_buffer || client_buffer_size <= 0) {
1296 /* yeah, slow... :/ */
1297 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1298 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1302 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1304 fp = open_file(buf2, &rep);
1306 snprintf(p_client_buffer, client_buffer_size,
1307 "can't open fan's state file");
1310 memset(buf, 0, sizeof(buf));
1311 fscanf(fp, "%*s %99s", buf);
1314 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1317 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1318 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1319 /* Linux 2.6.25 onwards ac adapter info is in
1320 /sys/class/power_supply/AC/
1321 On my system I get the following.
1322 /sys/class/power_supply/AC/uevent:
1323 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1326 POWER_SUPPLY_NAME=AC
1327 POWER_SUPPLY_TYPE=Mains
1328 POWER_SUPPLY_ONLINE=1
1331 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1339 if (!p_client_buffer || client_buffer_size <= 0) {
1343 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1344 fp = open_file(buf2, &rep);
1346 /* sysfs processing */
1348 if (fgets(buf, sizeof(buf), fp) == NULL)
1351 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1353 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1354 snprintf(p_client_buffer, client_buffer_size,
1355 "%s-line", (online ? "on" : "off"));
1361 /* yeah, slow... :/ */
1362 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1363 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1367 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1369 fp = open_file(buf2, &rep);
1371 snprintf(p_client_buffer, client_buffer_size,
1372 "No ac adapter found.... where is it?");
1375 memset(buf, 0, sizeof(buf));
1376 fscanf(fp, "%*s %99s", buf);
1379 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1384 /proc/acpi/thermal_zone/THRM/cooling_mode
1385 cooling mode: active
1386 /proc/acpi/thermal_zone/THRM/polling_frequency
1388 /proc/acpi/thermal_zone/THRM/state
1390 /proc/acpi/thermal_zone/THRM/temperature
1392 /proc/acpi/thermal_zone/THRM/trip_points
1394 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1397 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1398 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1400 int open_acpi_temperature(const char *name)
1406 if (name == NULL || strcmp(name, "*") == 0) {
1409 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1415 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1417 fd = open(path, O_RDONLY);
1419 ERR("can't open '%s': %s", path, strerror(errno));
1425 static double last_acpi_temp;
1426 static double last_acpi_temp_time;
1428 double get_acpi_temperature(int fd)
1434 /* don't update acpi temperature too often */
1435 if (current_update_time - last_acpi_temp_time < 11.32) {
1436 return last_acpi_temp;
1438 last_acpi_temp_time = current_update_time;
1440 /* seek to beginning */
1441 lseek(fd, 0, SEEK_SET);
1448 n = read(fd, buf, 255);
1450 ERR("can't read fd %d: %s", fd, strerror(errno));
1453 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1457 return last_acpi_temp;
1461 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1463 design capacity: 4400 mAh
1464 last full capacity: 4064 mAh
1465 battery technology: rechargeable
1466 design voltage: 14800 mV
1467 design capacity warning: 300 mAh
1468 design capacity low: 200 mAh
1469 capacity granularity 1: 32 mAh
1470 capacity granularity 2: 32 mAh
1472 serial number: 16922
1478 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1481 charging state: unknown
1483 remaining capacity: 4064 mAh
1484 present voltage: 16608 mV
1488 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1489 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1490 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1491 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1492 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1494 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1495 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1497 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1498 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1501 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1502 Linux 2.6.24 onwards battery info is in
1503 /sys/class/power_supply/BAT0/
1504 On my system I get the following.
1505 /sys/class/power_supply/BAT0/uevent:
1506 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1508 PHYSDEVDRIVER=battery
1509 POWER_SUPPLY_NAME=BAT0
1510 POWER_SUPPLY_TYPE=Battery
1511 POWER_SUPPLY_STATUS=Discharging
1512 POWER_SUPPLY_PRESENT=1
1513 POWER_SUPPLY_TECHNOLOGY=Li-ion
1514 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1515 POWER_SUPPLY_VOLTAGE_NOW=10780000
1516 POWER_SUPPLY_CURRENT_NOW=13970000
1517 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1518 POWER_SUPPLY_ENERGY_FULL=27370000
1519 POWER_SUPPLY_ENERGY_NOW=11810000
1520 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1521 POWER_SUPPLY_MANUFACTURER=Panasonic
1522 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1525 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1526 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1527 #define APM_PATH "/proc/apm"
1528 #define MAX_BATTERY_COUNT 4
1530 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1531 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1532 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1534 static int batteries_initialized = 0;
1535 static char batteries[MAX_BATTERY_COUNT][32];
1537 static int acpi_last_full[MAX_BATTERY_COUNT];
1538 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1540 /* e.g. "charging 75%" */
1541 static char last_battery_str[MAX_BATTERY_COUNT][64];
1543 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1545 static double last_battery_time[MAX_BATTERY_COUNT];
1547 static int last_battery_perct[MAX_BATTERY_COUNT];
1548 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1550 void init_batteries(void)
1554 if (batteries_initialized) {
1557 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1558 batteries[idx][0] = '\0';
1560 batteries_initialized = 1;
1563 int get_battery_idx(const char *bat)
1567 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1568 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1573 /* if not found, enter a new entry */
1574 if (!strlen(batteries[idx])) {
1575 snprintf(batteries[idx], 31, "%s", bat);
1581 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1583 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1585 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1586 char acpi_path[128];
1587 char sysfs_path[128];
1589 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1590 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1594 idx = get_battery_idx(bat);
1596 /* don't update battery too often */
1597 if (current_update_time - last_battery_time[idx] < 29.5) {
1598 set_return_value(buffer, n, item, idx);
1602 last_battery_time[idx] = current_update_time;
1604 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1605 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1607 /* first try SYSFS if that fails try ACPI */
1609 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1610 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1613 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1614 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1617 if (sysfs_bat_fp[idx] != NULL) {
1619 int present_rate = -1;
1620 int remaining_capacity = -1;
1621 char charging_state[64];
1624 strcpy(charging_state, "unknown");
1626 while (!feof(sysfs_bat_fp[idx])) {
1628 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1631 /* let's just hope units are ok */
1632 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1633 strcpy(present, "yes");
1634 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1635 strcpy(present, "no");
1636 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1637 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1638 /* present_rate is not the same as the
1639 current flowing now but it is the same value
1640 which was used in the past. so we continue
1642 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1643 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1644 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1645 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1646 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1647 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1648 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1649 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1650 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1651 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1654 fclose(sysfs_bat_fp[idx]);
1655 sysfs_bat_fp[idx] = NULL;
1657 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1658 if (remaining_capacity > acpi_last_full[idx])
1659 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1662 if (strcmp(present, "No") == 0) {
1663 strncpy(last_battery_str[idx], "not present", 64);
1666 else if (strcmp(charging_state, "Charging") == 0) {
1667 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1668 /* e.g. charging 75% */
1669 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1670 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1672 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1673 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1674 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1675 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1676 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1677 snprintf(last_battery_time_str[idx],
1678 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1680 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1681 snprintf(last_battery_time_str[idx],
1682 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1686 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1687 if (present_rate > 0) {
1688 /* e.g. discharging 35% */
1689 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1690 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1692 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1693 (long) (((float) remaining_capacity / present_rate) * 3600));
1694 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1695 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1696 snprintf(last_battery_time_str[idx],
1697 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1699 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1701 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1702 snprintf(last_battery_time_str[idx],
1703 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1707 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1708 else if (strncmp(charging_state, "Charged", 64) == 0) {
1709 /* Below happens with the second battery on my X40,
1710 * when the second one is empty and the first one
1712 if (remaining_capacity == 0)
1713 strcpy(last_battery_str[idx], "empty");
1715 strcpy(last_battery_str[idx], "charged");
1717 /* unknown, probably full / AC */
1719 if (acpi_last_full[idx] != 0
1720 && remaining_capacity != acpi_last_full[idx])
1721 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1722 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1724 strncpy(last_battery_str[idx], "AC", 64);
1726 } else if (acpi_bat_fp[idx] != NULL) {
1728 int present_rate = -1;
1729 int remaining_capacity = -1;
1730 char charging_state[64];
1733 /* read last full capacity if it's zero */
1734 if (acpi_last_full[idx] == 0) {
1735 static int rep3 = 0;
1739 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1740 fp = open_file(path, &rep3);
1745 if (fgets(b, 256, fp) == NULL) {
1748 if (sscanf(b, "last full capacity: %d",
1749 &acpi_last_full[idx]) != 0) {
1758 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1760 strcpy(charging_state, "unknown");
1762 while (!feof(acpi_bat_fp[idx])) {
1765 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1769 /* let's just hope units are ok */
1770 if (strncmp(buf, "present:", 8) == 0) {
1771 sscanf(buf, "present: %4s", present);
1772 } else if (strncmp(buf, "charging state:", 15) == 0) {
1773 sscanf(buf, "charging state: %63s", charging_state);
1774 } else if (strncmp(buf, "present rate:", 13) == 0) {
1775 sscanf(buf, "present rate: %d", &present_rate);
1776 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1777 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1780 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1781 if (remaining_capacity > acpi_last_full[idx]) {
1782 /* normalize to 100% */
1783 acpi_last_full[idx] = remaining_capacity;
1787 if (strcmp(present, "no") == 0) {
1788 strncpy(last_battery_str[idx], "not present", 64);
1790 } else if (strcmp(charging_state, "charging") == 0) {
1791 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1792 /* e.g. charging 75% */
1793 snprintf(last_battery_str[idx],
1794 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1795 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1797 format_seconds(last_battery_time_str[idx],
1798 sizeof(last_battery_time_str[idx]) - 1,
1799 (long) (((acpi_last_full[idx] - remaining_capacity) *
1800 3600) / present_rate));
1801 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1802 snprintf(last_battery_str[idx],
1803 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1804 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1805 snprintf(last_battery_time_str[idx],
1806 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1808 strncpy(last_battery_str[idx], "charging",
1809 sizeof(last_battery_str[idx]) - 1);
1810 snprintf(last_battery_time_str[idx],
1811 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1814 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1815 if (present_rate > 0) {
1816 /* e.g. discharging 35% */
1817 snprintf(last_battery_str[idx],
1818 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1819 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1821 format_seconds(last_battery_time_str[idx],
1822 sizeof(last_battery_time_str[idx]) - 1,
1823 (long) ((remaining_capacity * 3600) / present_rate));
1824 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1825 snprintf(last_battery_str[idx],
1826 sizeof(last_battery_str[idx]) - 1, "full");
1827 snprintf(last_battery_time_str[idx],
1828 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1830 snprintf(last_battery_str[idx],
1831 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1832 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1833 snprintf(last_battery_time_str[idx],
1834 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1837 } else if (strncmp(charging_state, "charged", 64) == 0) {
1838 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1839 /* Below happens with the second battery on my X40,
1840 * when the second one is empty and the first one being charged. */
1841 if (remaining_capacity == 0) {
1842 strcpy(last_battery_str[idx], "empty");
1844 strcpy(last_battery_str[idx], "charged");
1846 /* unknown, probably full / AC */
1848 if (acpi_last_full[idx] != 0
1849 && remaining_capacity != acpi_last_full[idx]) {
1850 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1851 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1853 strncpy(last_battery_str[idx], "AC", 64);
1858 if (apm_bat_fp[idx] == NULL) {
1859 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1862 if (apm_bat_fp[idx] != NULL) {
1863 unsigned int ac, status, flag;
1866 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1867 &ac, &status, &flag, &life);
1870 /* could check now that there is ac */
1871 snprintf(last_battery_str[idx], 64, "AC");
1873 /* could check that status == 3 here? */
1874 } else if (ac && life != 100) {
1875 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1877 snprintf(last_battery_str[idx], 64, "%d%%", life);
1880 /* it seemed to buffer it so file must be closed (or could use
1881 * syscalls directly but I don't feel like coding it now) */
1882 fclose(apm_bat_fp[idx]);
1883 apm_bat_fp[idx] = NULL;
1886 set_return_value(buffer, n, item, idx);
1889 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1892 case BATTERY_STATUS:
1893 snprintf(buffer, n, "%s", last_battery_str[idx]);
1896 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1903 int get_battery_perct(const char *bat)
1907 char acpi_path[128];
1908 char sysfs_path[128];
1909 int remaining_capacity = -1;
1911 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1912 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1916 idx = get_battery_idx(bat);
1918 /* don't update battery too often */
1919 if (current_update_time - last_battery_perct_time[idx] < 30) {
1920 return last_battery_perct[idx];
1922 last_battery_perct_time[idx] = current_update_time;
1924 /* Only check for SYSFS or ACPI */
1926 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1927 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1931 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1932 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1935 if (sysfs_bat_fp[idx] != NULL) {
1937 while (!feof(sysfs_bat_fp[idx])) {
1939 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1942 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1943 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1944 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1945 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1946 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1947 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1948 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1949 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1953 fclose(sysfs_bat_fp[idx]);
1954 sysfs_bat_fp[idx] = NULL;
1956 } else if (acpi_bat_fp[idx] != NULL) {
1958 /* read last full capacity if it's zero */
1959 if (acpi_design_capacity[idx] == 0) {
1964 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1965 fp = open_file(path, &rep2);
1970 if (fgets(b, 256, fp) == NULL) {
1973 if (sscanf(b, "last full capacity: %d",
1974 &acpi_design_capacity[idx]) != 0) {
1982 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1984 while (!feof(acpi_bat_fp[idx])) {
1987 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1991 if (buf[0] == 'r') {
1992 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1996 if (remaining_capacity < 0) {
1999 /* compute the battery percentage */
2000 last_battery_perct[idx] =
2001 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
2002 return last_battery_perct[idx];
2005 int get_battery_perct_bar(const char *bar)
2009 get_battery_perct(bar);
2010 idx = get_battery_idx(bar);
2011 return (int) (last_battery_perct[idx] * 2.56 - 1);
2014 /* On Apple powerbook and ibook:
2015 $ cat /proc/pmu/battery_0
2022 $ cat /proc/pmu/info
2023 PMU driver version : 2
2024 PMU firmware version : 0c
2029 /* defines as in <linux/pmu.h> */
2030 #define PMU_BATT_PRESENT 0x00000001
2031 #define PMU_BATT_CHARGING 0x00000002
2033 static FILE *pmu_battery_fp;
2034 static FILE *pmu_info_fp;
2035 static char pb_battery_info[3][32];
2036 static double pb_battery_info_update;
2038 #define PMU_PATH "/proc/pmu"
2039 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2042 const char *batt_path = PMU_PATH "/battery_0";
2043 const char *info_path = PMU_PATH "/info";
2045 int charge, max_charge, ac = -1;
2048 /* don't update battery too often */
2049 if (current_update_time - pb_battery_info_update < 29.5) {
2050 snprintf(buffer, n, "%s", pb_battery_info[i]);
2053 pb_battery_info_update = current_update_time;
2055 if (pmu_battery_fp == NULL) {
2056 pmu_battery_fp = open_file(batt_path, &rep);
2059 if (pmu_battery_fp != NULL) {
2060 rewind(pmu_battery_fp);
2061 while (!feof(pmu_battery_fp)) {
2064 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2068 if (buf[0] == 'f') {
2069 sscanf(buf, "flags : %8x", &flags);
2070 } else if (buf[0] == 'c' && buf[1] == 'h') {
2071 sscanf(buf, "charge : %d", &charge);
2072 } else if (buf[0] == 'm') {
2073 sscanf(buf, "max_charge : %d", &max_charge);
2074 } else if (buf[0] == 't') {
2075 sscanf(buf, "time rem. : %ld", &timeval);
2079 if (pmu_info_fp == NULL) {
2080 pmu_info_fp = open_file(info_path, &rep);
2083 if (pmu_info_fp != NULL) {
2084 rewind(pmu_info_fp);
2085 while (!feof(pmu_info_fp)) {
2088 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2091 if (buf[0] == 'A') {
2092 sscanf(buf, "AC Power : %d", &ac);
2096 /* update status string */
2097 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2098 strcpy(pb_battery_info[PB_BATT_STATUS], "AC");
2099 } else if (ac && (flags & PMU_BATT_PRESENT)
2100 && !(flags & PMU_BATT_CHARGING)) {
2101 strcpy(pb_battery_info[PB_BATT_STATUS], "charged");
2102 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2103 strcpy(pb_battery_info[PB_BATT_STATUS], "charging");
2105 strcpy(pb_battery_info[PB_BATT_STATUS], "discharging");
2108 /* update percentage string */
2110 pb_battery_info[PB_BATT_PERCENT][0] = 0;
2112 snprintf(pb_battery_info[PB_BATT_PERCENT],
2113 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2114 (charge * 100) / max_charge);
2117 /* update time string */
2118 if (timeval == 0) { /* fully charged or battery not present */
2119 pb_battery_info[PB_BATT_TIME][0] = 0;
2120 } else if (timeval < 60 * 60) { /* don't show secs */
2121 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2122 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2124 format_seconds(pb_battery_info[PB_BATT_TIME],
2125 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2128 snprintf(buffer, n, "%s", pb_battery_info[i]);
2131 void update_top(void)
2133 show_nice_processes = 1;
2134 process_find_top(info.cpu, info.memu);
2135 info.first_process = get_first_process();
2138 /* Here come the IBM ACPI-specific things. For reference, see
2139 * http://ibm-acpi.sourceforge.net/README
2140 * If IBM ACPI is installed, /proc/acpi/ibm contains the following files:
2156 * The content of these files is described in detail in the aforementioned
2157 * README - some of them also in the following functions accessing them.
2158 * Peter Tarjan (ptarjan@citromail.hu) */
2160 #define IBM_ACPI_DIR "/proc/acpi/ibm"
2162 /* get fan speed on IBM/Lenovo laptops running the ibm acpi.
2163 * /proc/acpi/ibm/fan looks like this (3 lines):
2166 commands: enable, disable
2167 * Peter Tarjan (ptarjan@citromail.hu) */
2169 void get_ibm_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
2172 unsigned int speed = 0;
2175 if (!p_client_buffer || client_buffer_size <= 0) {
2179 snprintf(fan, 127, "%s/fan", IBM_ACPI_DIR);
2181 fp = fopen(fan, "r");
2186 if (fgets(line, 255, fp) == NULL) {
2189 if (sscanf(line, "speed: %u", &speed)) {
2194 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2195 "ibm* from your "PACKAGE_NAME" config file.", fan, strerror(errno));
2199 snprintf(p_client_buffer, client_buffer_size, "%d", speed);
2202 /* get the measured temperatures from the temperature sensors
2203 * on IBM/Lenovo laptops running the ibm acpi.
2204 * There are 8 values in /proc/acpi/ibm/thermal, and according to
2205 * http://ibm-acpi.sourceforge.net/README
2206 * these mean the following (at least on an IBM R51...)
2207 * 0: CPU (also on the T series laptops)
2208 * 1: Mini PCI Module (?)
2210 * 3: GPU (also on the T series laptops)
2215 * I'm not too sure about those with the question mark, but the values I'm
2216 * reading from *my* thermal file (on a T42p) look realistic for the
2217 * hdd and the battery.
2218 * #5 and #7 are always -128.
2219 * /proc/acpi/ibm/thermal looks like this (1 line):
2220 temperatures: 41 43 31 46 33 -128 29 -128
2221 * Peter Tarjan (ptarjan@citromail.hu) */
2223 static double last_ibm_acpi_temp_time;
2224 void get_ibm_acpi_temps(void)
2230 /* don't update too often */
2231 if (current_update_time - last_ibm_acpi_temp_time < 10.00) {
2234 last_ibm_acpi_temp_time = current_update_time;
2236 /* if (!p_client_buffer || client_buffer_size <= 0) {
2240 snprintf(thermal, 127, "%s/thermal", IBM_ACPI_DIR);
2241 fp = fopen(thermal, "r");
2247 if (fgets(line, 255, fp) == NULL) {
2250 if (sscanf(line, "temperatures: %d %d %d %d %d %d %d %d",
2251 &ibm_acpi.temps[0], &ibm_acpi.temps[1], &ibm_acpi.temps[2],
2252 &ibm_acpi.temps[3], &ibm_acpi.temps[4], &ibm_acpi.temps[5],
2253 &ibm_acpi.temps[6], &ibm_acpi.temps[7])) {
2258 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2259 "ibm* from your "PACKAGE_NAME" config file.", thermal, strerror(errno));
2265 /* get volume (0-14) on IBM/Lenovo laptops running the ibm acpi.
2266 * "Volume" here is none of the mixer volumes, but a "master of masters"
2267 * volume adjusted by the IBM volume keys.
2268 * /proc/acpi/ibm/fan looks like this (4 lines):
2271 commands: up, down, mute
2272 commands: level <level> (<level> is 0-15)
2273 * Peter Tarjan (ptarjan@citromail.hu) */
2275 void get_ibm_acpi_volume(char *p_client_buffer, size_t client_buffer_size)
2279 unsigned int vol = -1;
2282 if (!p_client_buffer || client_buffer_size <= 0) {
2286 snprintf(volume, 127, "%s/volume", IBM_ACPI_DIR);
2288 fp = fopen(volume, "r");
2292 unsigned int read_vol = -1;
2294 if (fgets(line, 255, fp) == NULL) {
2297 if (sscanf(line, "level: %u", &read_vol)) {
2301 if (sscanf(line, "mute: %s", mute)) {
2306 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2307 "ibm* from your "PACKAGE_NAME" config file.", volume, strerror(errno));
2312 if (strcmp(mute, "on") == 0) {
2313 snprintf(p_client_buffer, client_buffer_size, "%s", "mute");
2316 snprintf(p_client_buffer, client_buffer_size, "%d", vol);
2321 /* static FILE *fp = NULL; */
2323 /* get LCD brightness on IBM/Lenovo laptops running the ibm acpi.
2324 * /proc/acpi/ibm/brightness looks like this (3 lines):
2327 commands: level <level> (<level> is 0-7)
2328 * Peter Tarjan (ptarjan@citromail.hu) */
2330 void get_ibm_acpi_brightness(char *p_client_buffer, size_t client_buffer_size)
2333 unsigned int brightness = 0;
2336 if (!p_client_buffer || client_buffer_size <= 0) {
2340 snprintf(filename, 127, "%s/brightness", IBM_ACPI_DIR);
2342 fp = fopen(filename, "r");
2347 if (fgets(line, 255, fp) == NULL) {
2350 if (sscanf(line, "level: %u", &brightness)) {
2355 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2356 "ibm* from your "PACKAGE_NAME" config file.", filename, strerror(errno));
2361 snprintf(p_client_buffer, client_buffer_size, "%d", brightness);
2364 void update_entropy(void)
2367 const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2368 const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2371 info.entropy.entropy_avail = 0;
2372 info.entropy.poolsize = 0;
2374 if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2378 if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2383 fscanf(fp1, "%u", &info.entropy.entropy_avail);
2384 fscanf(fp2, "%u", &info.entropy.poolsize);
2389 info.mask |= (1 << INFO_ENTROPY);
2392 const char *get_disk_protect_queue(const char *disk)
2398 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2399 if ((fp = fopen(path, "r")) == NULL)
2401 if (fscanf(fp, "%d\n", &state) != 1) {
2406 return state ? "frozen" : "free ";