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 + 1);
384 if(NULL != ns->addrs)
385 memset(ns->addrs, 0, 17 * 16 + 1); /* 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;
1066 __asm__ volatile(".byte 0x0f, 0x31":"=a" (a),"=d" (d));
1072 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1073 void get_freq_dynamic(char *p_client_buffer, size_t client_buffer_size,
1074 const char *p_format, int divisor)
1076 #if defined(__i386) || defined(__x86_64)
1078 struct timeval tvstart, tvstop;
1079 unsigned long long cycles[2]; /* gotta be 64 bit */
1080 unsigned int microseconds; /* total time taken */
1082 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1087 memset(&tz, 0, sizeof(tz));
1089 /* get this function in cached memory */
1090 gettimeofday(&tvstart, &tz);
1091 cycles[0] = rdtsc();
1092 gettimeofday(&tvstart, &tz);
1094 /* we don't trust that this is any specific length of time */
1096 cycles[1] = rdtsc();
1097 gettimeofday(&tvstop, &tz);
1098 microseconds = ((tvstop.tv_sec - tvstart.tv_sec) * 1000000) +
1099 (tvstop.tv_usec - tvstart.tv_usec);
1101 snprintf(p_client_buffer, client_buffer_size, p_format,
1102 (float) ((cycles[1] - cycles[0]) / microseconds) / divisor);
1105 /* FIXME: hardwired: get freq for first cpu!
1106 * this whole function needs to be rethought and redone for
1107 * multi-cpu/multi-core/multi-threaded environments and
1108 * arbitrary combinations thereof */
1109 get_freq(p_client_buffer, client_buffer_size, p_format, divisor, 1);
1114 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1115 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1117 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1118 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1119 const char *p_format, int divisor, unsigned int cpu)
1127 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1133 char current_freq_file[128];
1135 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1137 f = fopen(current_freq_file, "r");
1139 /* if there's a cpufreq /sys node, read the current frequency from
1140 * this node and divide by 1000 to get Mhz. */
1141 if (fgets(s, sizeof(s), f)) {
1142 s[strlen(s) - 1] = '\0';
1143 freq = strtod(s, NULL);
1146 snprintf(p_client_buffer, client_buffer_size, p_format,
1147 (freq / 1000) / divisor);
1152 // open the CPU information file
1153 f = open_file("/proc/cpuinfo", &rep);
1155 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1160 while (fgets(s, sizeof(s), f) != NULL) {
1162 #if defined(__i386) || defined(__x86_64)
1163 // and search for the cpu mhz
1164 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1166 #if defined(__alpha)
1167 // different on alpha
1168 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1170 // this is different on ppc for some reason
1171 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1172 #endif // defined(__alpha)
1173 #endif // defined(__i386) || defined(__x86_64)
1175 // copy just the number
1176 strcpy(frequency, strchr(s, ':') + 2);
1177 #if defined(__alpha)
1179 frequency[strlen(frequency) - 6] = '\0';
1180 // kernel reports in Hz
1181 freq = strtod(frequency, NULL) / 1000000;
1184 frequency[strlen(frequency) - 1] = '\0';
1185 freq = strtod(frequency, NULL);
1189 if (strncmp(s, "processor", 9) == 0) {
1196 snprintf(p_client_buffer, client_buffer_size, p_format,
1197 (float) freq / divisor);
1201 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1203 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1213 * Peter Tarjan (ptarjan@citromail.hu) */
1215 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1216 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1217 const char *p_format, int divisor, unsigned int cpu)
1223 char current_freq_file[128];
1226 /* build the voltage file name */
1228 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1231 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1236 /* read the current cpu frequency from the /sys node */
1237 f = fopen(current_freq_file, "r");
1239 if (fgets(s, sizeof(s), f)) {
1240 s[strlen(s) - 1] = '\0';
1241 freq = strtod(s, NULL);
1245 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1246 perror("get_voltage()");
1253 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1256 /* use the current cpu frequency to find the corresponding voltage */
1257 f = fopen(current_freq_file, "r");
1263 if (fgets(line, 255, f) == NULL) {
1266 sscanf(line, "%d %d", &freq_comp, &voltage);
1267 if (freq_comp == freq) {
1273 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1274 perror("get_voltage()");
1280 snprintf(p_client_buffer, client_buffer_size, p_format,
1281 (float) voltage / divisor);
1285 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1287 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1294 if (!p_client_buffer || client_buffer_size <= 0) {
1298 /* yeah, slow... :/ */
1299 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1300 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1304 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1306 fp = open_file(buf2, &rep);
1308 snprintf(p_client_buffer, client_buffer_size,
1309 "can't open fan's state file");
1312 memset(buf, 0, sizeof(buf));
1313 fscanf(fp, "%*s %99s", buf);
1316 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1319 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1320 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1321 /* Linux 2.6.25 onwards ac adapter info is in
1322 /sys/class/power_supply/AC/
1323 On my system I get the following.
1324 /sys/class/power_supply/AC/uevent:
1325 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1328 POWER_SUPPLY_NAME=AC
1329 POWER_SUPPLY_TYPE=Mains
1330 POWER_SUPPLY_ONLINE=1
1333 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1341 if (!p_client_buffer || client_buffer_size <= 0) {
1345 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1346 fp = open_file(buf2, &rep);
1348 /* sysfs processing */
1350 if (fgets(buf, sizeof(buf), fp) == NULL)
1353 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1355 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1356 snprintf(p_client_buffer, client_buffer_size,
1357 "%s-line", (online ? "on" : "off"));
1363 /* yeah, slow... :/ */
1364 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1365 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1369 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1371 fp = open_file(buf2, &rep);
1373 snprintf(p_client_buffer, client_buffer_size,
1374 "No ac adapter found.... where is it?");
1377 memset(buf, 0, sizeof(buf));
1378 fscanf(fp, "%*s %99s", buf);
1381 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1386 /proc/acpi/thermal_zone/THRM/cooling_mode
1387 cooling mode: active
1388 /proc/acpi/thermal_zone/THRM/polling_frequency
1390 /proc/acpi/thermal_zone/THRM/state
1392 /proc/acpi/thermal_zone/THRM/temperature
1394 /proc/acpi/thermal_zone/THRM/trip_points
1396 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1399 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1400 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1402 int open_acpi_temperature(const char *name)
1408 if (name == NULL || strcmp(name, "*") == 0) {
1411 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1417 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1419 fd = open(path, O_RDONLY);
1421 ERR("can't open '%s': %s", path, strerror(errno));
1427 static double last_acpi_temp;
1428 static double last_acpi_temp_time;
1430 double get_acpi_temperature(int fd)
1436 /* don't update acpi temperature too often */
1437 if (current_update_time - last_acpi_temp_time < 11.32) {
1438 return last_acpi_temp;
1440 last_acpi_temp_time = current_update_time;
1442 /* seek to beginning */
1443 lseek(fd, 0, SEEK_SET);
1450 n = read(fd, buf, 255);
1452 ERR("can't read fd %d: %s", fd, strerror(errno));
1455 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1459 return last_acpi_temp;
1463 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1465 design capacity: 4400 mAh
1466 last full capacity: 4064 mAh
1467 battery technology: rechargeable
1468 design voltage: 14800 mV
1469 design capacity warning: 300 mAh
1470 design capacity low: 200 mAh
1471 capacity granularity 1: 32 mAh
1472 capacity granularity 2: 32 mAh
1474 serial number: 16922
1480 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1483 charging state: unknown
1485 remaining capacity: 4064 mAh
1486 present voltage: 16608 mV
1490 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1491 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1492 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1493 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1494 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1496 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1497 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1499 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1500 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1503 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1504 Linux 2.6.24 onwards battery info is in
1505 /sys/class/power_supply/BAT0/
1506 On my system I get the following.
1507 /sys/class/power_supply/BAT0/uevent:
1508 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1510 PHYSDEVDRIVER=battery
1511 POWER_SUPPLY_NAME=BAT0
1512 POWER_SUPPLY_TYPE=Battery
1513 POWER_SUPPLY_STATUS=Discharging
1514 POWER_SUPPLY_PRESENT=1
1515 POWER_SUPPLY_TECHNOLOGY=Li-ion
1516 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1517 POWER_SUPPLY_VOLTAGE_NOW=10780000
1518 POWER_SUPPLY_CURRENT_NOW=13970000
1519 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1520 POWER_SUPPLY_ENERGY_FULL=27370000
1521 POWER_SUPPLY_ENERGY_NOW=11810000
1522 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1523 POWER_SUPPLY_MANUFACTURER=Panasonic
1524 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1527 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1528 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1529 #define APM_PATH "/proc/apm"
1530 #define MAX_BATTERY_COUNT 4
1532 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1533 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1534 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1536 static int batteries_initialized = 0;
1537 static char batteries[MAX_BATTERY_COUNT][32];
1539 static int acpi_last_full[MAX_BATTERY_COUNT];
1540 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1542 /* e.g. "charging 75%" */
1543 static char last_battery_str[MAX_BATTERY_COUNT][64];
1545 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1547 static double last_battery_time[MAX_BATTERY_COUNT];
1549 static int last_battery_perct[MAX_BATTERY_COUNT];
1550 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1552 void init_batteries(void)
1556 if (batteries_initialized) {
1559 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1560 batteries[idx][0] = '\0';
1562 batteries_initialized = 1;
1565 int get_battery_idx(const char *bat)
1569 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1570 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1575 /* if not found, enter a new entry */
1576 if (!strlen(batteries[idx])) {
1577 snprintf(batteries[idx], 31, "%s", bat);
1583 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1585 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1587 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1588 char acpi_path[128];
1589 char sysfs_path[128];
1591 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1592 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1596 idx = get_battery_idx(bat);
1598 /* don't update battery too often */
1599 if (current_update_time - last_battery_time[idx] < 29.5) {
1600 set_return_value(buffer, n, item, idx);
1604 last_battery_time[idx] = current_update_time;
1606 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1607 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1609 /* first try SYSFS if that fails try ACPI */
1611 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1612 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1615 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1616 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1619 if (sysfs_bat_fp[idx] != NULL) {
1621 int present_rate = -1;
1622 int remaining_capacity = -1;
1623 char charging_state[64];
1626 strcpy(charging_state, "unknown");
1628 while (!feof(sysfs_bat_fp[idx])) {
1630 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1633 /* let's just hope units are ok */
1634 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1635 strcpy(present, "yes");
1636 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1637 strcpy(present, "no");
1638 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1639 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1640 /* present_rate is not the same as the
1641 current flowing now but it is the same value
1642 which was used in the past. so we continue
1644 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1645 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1646 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1647 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1648 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1649 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1650 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1651 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1652 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1653 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1656 fclose(sysfs_bat_fp[idx]);
1657 sysfs_bat_fp[idx] = NULL;
1659 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1660 if (remaining_capacity > acpi_last_full[idx])
1661 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1664 if (strcmp(present, "No") == 0) {
1665 strncpy(last_battery_str[idx], "not present", 64);
1668 else if (strcmp(charging_state, "Charging") == 0) {
1669 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1670 /* e.g. charging 75% */
1671 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1672 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1674 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1675 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1676 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1677 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1678 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1679 snprintf(last_battery_time_str[idx],
1680 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1682 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1683 snprintf(last_battery_time_str[idx],
1684 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1688 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1689 if (present_rate > 0) {
1690 /* e.g. discharging 35% */
1691 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1692 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1694 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1695 (long) (((float) remaining_capacity / present_rate) * 3600));
1696 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1697 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1698 snprintf(last_battery_time_str[idx],
1699 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1701 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1703 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1704 snprintf(last_battery_time_str[idx],
1705 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1709 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1710 else if (strncmp(charging_state, "Charged", 64) == 0) {
1711 /* Below happens with the second battery on my X40,
1712 * when the second one is empty and the first one
1714 if (remaining_capacity == 0)
1715 strcpy(last_battery_str[idx], "empty");
1717 strcpy(last_battery_str[idx], "charged");
1719 /* unknown, probably full / AC */
1721 if (acpi_last_full[idx] != 0
1722 && remaining_capacity != acpi_last_full[idx])
1723 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1724 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1726 strncpy(last_battery_str[idx], "AC", 64);
1728 } else if (acpi_bat_fp[idx] != NULL) {
1730 int present_rate = -1;
1731 int remaining_capacity = -1;
1732 char charging_state[64];
1735 /* read last full capacity if it's zero */
1736 if (acpi_last_full[idx] == 0) {
1737 static int rep3 = 0;
1741 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1742 fp = open_file(path, &rep3);
1747 if (fgets(b, 256, fp) == NULL) {
1750 if (sscanf(b, "last full capacity: %d",
1751 &acpi_last_full[idx]) != 0) {
1760 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1762 strcpy(charging_state, "unknown");
1764 while (!feof(acpi_bat_fp[idx])) {
1767 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1771 /* let's just hope units are ok */
1772 if (strncmp(buf, "present:", 8) == 0) {
1773 sscanf(buf, "present: %4s", present);
1774 } else if (strncmp(buf, "charging state:", 15) == 0) {
1775 sscanf(buf, "charging state: %63s", charging_state);
1776 } else if (strncmp(buf, "present rate:", 13) == 0) {
1777 sscanf(buf, "present rate: %d", &present_rate);
1778 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1779 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1782 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1783 if (remaining_capacity > acpi_last_full[idx]) {
1784 /* normalize to 100% */
1785 acpi_last_full[idx] = remaining_capacity;
1789 if (strcmp(present, "no") == 0) {
1790 strncpy(last_battery_str[idx], "not present", 64);
1792 } else if (strcmp(charging_state, "charging") == 0) {
1793 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1794 /* e.g. charging 75% */
1795 snprintf(last_battery_str[idx],
1796 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1797 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1799 format_seconds(last_battery_time_str[idx],
1800 sizeof(last_battery_time_str[idx]) - 1,
1801 (long) (((acpi_last_full[idx] - remaining_capacity) *
1802 3600) / present_rate));
1803 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1804 snprintf(last_battery_str[idx],
1805 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1806 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1807 snprintf(last_battery_time_str[idx],
1808 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1810 strncpy(last_battery_str[idx], "charging",
1811 sizeof(last_battery_str[idx]) - 1);
1812 snprintf(last_battery_time_str[idx],
1813 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1816 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1817 if (present_rate > 0) {
1818 /* e.g. discharging 35% */
1819 snprintf(last_battery_str[idx],
1820 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1821 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1823 format_seconds(last_battery_time_str[idx],
1824 sizeof(last_battery_time_str[idx]) - 1,
1825 (long) ((remaining_capacity * 3600) / present_rate));
1826 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1827 snprintf(last_battery_str[idx],
1828 sizeof(last_battery_str[idx]) - 1, "full");
1829 snprintf(last_battery_time_str[idx],
1830 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1832 snprintf(last_battery_str[idx],
1833 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1834 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1835 snprintf(last_battery_time_str[idx],
1836 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1839 } else if (strncmp(charging_state, "charged", 64) == 0) {
1840 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1841 /* Below happens with the second battery on my X40,
1842 * when the second one is empty and the first one being charged. */
1843 if (remaining_capacity == 0) {
1844 strcpy(last_battery_str[idx], "empty");
1846 strcpy(last_battery_str[idx], "charged");
1848 /* unknown, probably full / AC */
1850 if (acpi_last_full[idx] != 0
1851 && remaining_capacity != acpi_last_full[idx]) {
1852 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1853 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1855 strncpy(last_battery_str[idx], "AC", 64);
1860 if (apm_bat_fp[idx] == NULL) {
1861 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1864 if (apm_bat_fp[idx] != NULL) {
1865 unsigned int ac, status, flag;
1868 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1869 &ac, &status, &flag, &life);
1872 /* could check now that there is ac */
1873 snprintf(last_battery_str[idx], 64, "AC");
1875 /* could check that status == 3 here? */
1876 } else if (ac && life != 100) {
1877 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1879 snprintf(last_battery_str[idx], 64, "%d%%", life);
1882 /* it seemed to buffer it so file must be closed (or could use
1883 * syscalls directly but I don't feel like coding it now) */
1884 fclose(apm_bat_fp[idx]);
1885 apm_bat_fp[idx] = NULL;
1888 set_return_value(buffer, n, item, idx);
1891 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1894 case BATTERY_STATUS:
1895 snprintf(buffer, n, "%s", last_battery_str[idx]);
1898 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1905 int get_battery_perct(const char *bat)
1909 char acpi_path[128];
1910 char sysfs_path[128];
1911 int remaining_capacity = -1;
1913 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1914 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1918 idx = get_battery_idx(bat);
1920 /* don't update battery too often */
1921 if (current_update_time - last_battery_perct_time[idx] < 30) {
1922 return last_battery_perct[idx];
1924 last_battery_perct_time[idx] = current_update_time;
1926 /* Only check for SYSFS or ACPI */
1928 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1929 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1933 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1934 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1937 if (sysfs_bat_fp[idx] != NULL) {
1939 while (!feof(sysfs_bat_fp[idx])) {
1941 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1944 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1945 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1946 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1947 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1948 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1949 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1950 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1951 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1955 fclose(sysfs_bat_fp[idx]);
1956 sysfs_bat_fp[idx] = NULL;
1958 } else if (acpi_bat_fp[idx] != NULL) {
1960 /* read last full capacity if it's zero */
1961 if (acpi_design_capacity[idx] == 0) {
1966 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1967 fp = open_file(path, &rep2);
1972 if (fgets(b, 256, fp) == NULL) {
1975 if (sscanf(b, "last full capacity: %d",
1976 &acpi_design_capacity[idx]) != 0) {
1984 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1986 while (!feof(acpi_bat_fp[idx])) {
1989 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1993 if (buf[0] == 'r') {
1994 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1998 if (remaining_capacity < 0) {
2001 /* compute the battery percentage */
2002 last_battery_perct[idx] =
2003 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
2004 return last_battery_perct[idx];
2007 int get_battery_perct_bar(const char *bar)
2011 get_battery_perct(bar);
2012 idx = get_battery_idx(bar);
2013 return (int) (last_battery_perct[idx] * 2.56 - 1);
2016 /* On Apple powerbook and ibook:
2017 $ cat /proc/pmu/battery_0
2024 $ cat /proc/pmu/info
2025 PMU driver version : 2
2026 PMU firmware version : 0c
2031 /* defines as in <linux/pmu.h> */
2032 #define PMU_BATT_PRESENT 0x00000001
2033 #define PMU_BATT_CHARGING 0x00000002
2035 static FILE *pmu_battery_fp;
2036 static FILE *pmu_info_fp;
2037 static char pb_battery_info[3][32];
2038 static double pb_battery_info_update;
2040 #define PMU_PATH "/proc/pmu"
2041 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2044 const char *batt_path = PMU_PATH "/battery_0";
2045 const char *info_path = PMU_PATH "/info";
2047 int charge, max_charge, ac = -1;
2050 /* don't update battery too often */
2051 if (current_update_time - pb_battery_info_update < 29.5) {
2052 snprintf(buffer, n, "%s", pb_battery_info[i]);
2055 pb_battery_info_update = current_update_time;
2057 if (pmu_battery_fp == NULL) {
2058 pmu_battery_fp = open_file(batt_path, &rep);
2061 if (pmu_battery_fp != NULL) {
2062 rewind(pmu_battery_fp);
2063 while (!feof(pmu_battery_fp)) {
2066 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2070 if (buf[0] == 'f') {
2071 sscanf(buf, "flags : %8x", &flags);
2072 } else if (buf[0] == 'c' && buf[1] == 'h') {
2073 sscanf(buf, "charge : %d", &charge);
2074 } else if (buf[0] == 'm') {
2075 sscanf(buf, "max_charge : %d", &max_charge);
2076 } else if (buf[0] == 't') {
2077 sscanf(buf, "time rem. : %ld", &timeval);
2081 if (pmu_info_fp == NULL) {
2082 pmu_info_fp = open_file(info_path, &rep);
2085 if (pmu_info_fp != NULL) {
2086 rewind(pmu_info_fp);
2087 while (!feof(pmu_info_fp)) {
2090 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2093 if (buf[0] == 'A') {
2094 sscanf(buf, "AC Power : %d", &ac);
2098 /* update status string */
2099 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2100 strcpy(pb_battery_info[PB_BATT_STATUS], "AC");
2101 } else if (ac && (flags & PMU_BATT_PRESENT)
2102 && !(flags & PMU_BATT_CHARGING)) {
2103 strcpy(pb_battery_info[PB_BATT_STATUS], "charged");
2104 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2105 strcpy(pb_battery_info[PB_BATT_STATUS], "charging");
2107 strcpy(pb_battery_info[PB_BATT_STATUS], "discharging");
2110 /* update percentage string */
2112 pb_battery_info[PB_BATT_PERCENT][0] = 0;
2114 snprintf(pb_battery_info[PB_BATT_PERCENT],
2115 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2116 (charge * 100) / max_charge);
2119 /* update time string */
2120 if (timeval == 0) { /* fully charged or battery not present */
2121 pb_battery_info[PB_BATT_TIME][0] = 0;
2122 } else if (timeval < 60 * 60) { /* don't show secs */
2123 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2124 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2126 format_seconds(pb_battery_info[PB_BATT_TIME],
2127 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2130 snprintf(buffer, n, "%s", pb_battery_info[i]);
2133 void update_top(void)
2135 show_nice_processes = 1;
2136 process_find_top(info.cpu, info.memu);
2137 info.first_process = get_first_process();
2140 /* Here come the IBM ACPI-specific things. For reference, see
2141 * http://ibm-acpi.sourceforge.net/README
2142 * If IBM ACPI is installed, /proc/acpi/ibm contains the following files:
2158 * The content of these files is described in detail in the aforementioned
2159 * README - some of them also in the following functions accessing them.
2160 * Peter Tarjan (ptarjan@citromail.hu) */
2162 #define IBM_ACPI_DIR "/proc/acpi/ibm"
2164 /* get fan speed on IBM/Lenovo laptops running the ibm acpi.
2165 * /proc/acpi/ibm/fan looks like this (3 lines):
2168 commands: enable, disable
2169 * Peter Tarjan (ptarjan@citromail.hu) */
2171 void get_ibm_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
2174 unsigned int speed = 0;
2177 if (!p_client_buffer || client_buffer_size <= 0) {
2181 snprintf(fan, 127, "%s/fan", IBM_ACPI_DIR);
2183 fp = fopen(fan, "r");
2188 if (fgets(line, 255, fp) == NULL) {
2191 if (sscanf(line, "speed: %u", &speed)) {
2196 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2197 "ibm* from your "PACKAGE_NAME" config file.", fan, strerror(errno));
2201 snprintf(p_client_buffer, client_buffer_size, "%d", speed);
2204 /* get the measured temperatures from the temperature sensors
2205 * on IBM/Lenovo laptops running the ibm acpi.
2206 * There are 8 values in /proc/acpi/ibm/thermal, and according to
2207 * http://ibm-acpi.sourceforge.net/README
2208 * these mean the following (at least on an IBM R51...)
2209 * 0: CPU (also on the T series laptops)
2210 * 1: Mini PCI Module (?)
2212 * 3: GPU (also on the T series laptops)
2217 * I'm not too sure about those with the question mark, but the values I'm
2218 * reading from *my* thermal file (on a T42p) look realistic for the
2219 * hdd and the battery.
2220 * #5 and #7 are always -128.
2221 * /proc/acpi/ibm/thermal looks like this (1 line):
2222 temperatures: 41 43 31 46 33 -128 29 -128
2223 * Peter Tarjan (ptarjan@citromail.hu) */
2225 static double last_ibm_acpi_temp_time;
2226 void get_ibm_acpi_temps(void)
2232 /* don't update too often */
2233 if (current_update_time - last_ibm_acpi_temp_time < 10.00) {
2236 last_ibm_acpi_temp_time = current_update_time;
2238 /* if (!p_client_buffer || client_buffer_size <= 0) {
2242 snprintf(thermal, 127, "%s/thermal", IBM_ACPI_DIR);
2243 fp = fopen(thermal, "r");
2249 if (fgets(line, 255, fp) == NULL) {
2252 if (sscanf(line, "temperatures: %d %d %d %d %d %d %d %d",
2253 &ibm_acpi.temps[0], &ibm_acpi.temps[1], &ibm_acpi.temps[2],
2254 &ibm_acpi.temps[3], &ibm_acpi.temps[4], &ibm_acpi.temps[5],
2255 &ibm_acpi.temps[6], &ibm_acpi.temps[7])) {
2260 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2261 "ibm* from your "PACKAGE_NAME" config file.", thermal, strerror(errno));
2267 /* get volume (0-14) on IBM/Lenovo laptops running the ibm acpi.
2268 * "Volume" here is none of the mixer volumes, but a "master of masters"
2269 * volume adjusted by the IBM volume keys.
2270 * /proc/acpi/ibm/fan looks like this (4 lines):
2273 commands: up, down, mute
2274 commands: level <level> (<level> is 0-15)
2275 * Peter Tarjan (ptarjan@citromail.hu) */
2277 void get_ibm_acpi_volume(char *p_client_buffer, size_t client_buffer_size)
2281 unsigned int vol = -1;
2284 if (!p_client_buffer || client_buffer_size <= 0) {
2288 snprintf(volume, 127, "%s/volume", IBM_ACPI_DIR);
2290 fp = fopen(volume, "r");
2294 unsigned int read_vol = -1;
2296 if (fgets(line, 255, fp) == NULL) {
2299 if (sscanf(line, "level: %u", &read_vol)) {
2303 if (sscanf(line, "mute: %s", mute)) {
2308 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2309 "ibm* from your "PACKAGE_NAME" config file.", volume, strerror(errno));
2314 if (strcmp(mute, "on") == 0) {
2315 snprintf(p_client_buffer, client_buffer_size, "%s", "mute");
2318 snprintf(p_client_buffer, client_buffer_size, "%d", vol);
2323 /* static FILE *fp = NULL; */
2325 /* get LCD brightness on IBM/Lenovo laptops running the ibm acpi.
2326 * /proc/acpi/ibm/brightness looks like this (3 lines):
2329 commands: level <level> (<level> is 0-7)
2330 * Peter Tarjan (ptarjan@citromail.hu) */
2332 void get_ibm_acpi_brightness(char *p_client_buffer, size_t client_buffer_size)
2335 unsigned int brightness = 0;
2338 if (!p_client_buffer || client_buffer_size <= 0) {
2342 snprintf(filename, 127, "%s/brightness", IBM_ACPI_DIR);
2344 fp = fopen(filename, "r");
2349 if (fgets(line, 255, fp) == NULL) {
2352 if (sscanf(line, "level: %u", &brightness)) {
2357 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2358 "ibm* from your "PACKAGE_NAME" config file.", filename, strerror(errno));
2363 snprintf(p_client_buffer, client_buffer_size, "%d", brightness);
2366 void update_entropy(void)
2369 const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2370 const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2373 info.entropy.entropy_avail = 0;
2374 info.entropy.poolsize = 0;
2376 if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2380 if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2385 fscanf(fp1, "%u", &info.entropy.entropy_avail);
2386 fscanf(fp2, "%u", &info.entropy.poolsize);
2391 info.mask |= (1 << INFO_ENTROPY);
2394 const char *get_disk_protect_queue(const char *disk)
2400 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2401 if ((fp = fopen(path, "r")) == NULL)
2403 if (fscanf(fp, "%d\n", &state) != 1) {
2408 return state ? "frozen" : "free ";