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 if (isdigit(buf[3])) {
679 idx = atoi(&buf[3]) + 1;
683 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
684 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
685 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
686 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
687 &(cpu[idx].cpu_steal));
689 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
690 cpu[idx].cpu_system + cpu[idx].cpu_idle +
691 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
692 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
694 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
695 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
696 info.mask |= (1 << INFO_CPU);
698 delta = current_update_time - last_update_time;
700 if (delta <= 0.001) {
704 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
705 cpu[idx].cpu_last_active_total) /
706 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
708 for (i = 0; i < info.cpu_avg_samples; i++) {
709 curtmp += cpu[idx].cpu_val[i];
711 /* TESTING -- I've removed this, because I don't think it is right.
712 * You shouldn't divide by the cpu count here ...
713 * removing for testing */
715 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
718 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
720 /* TESTING -- this line replaces the prev. "suspect" if/else */
721 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
723 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
724 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
725 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
726 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
733 void update_running_processes(void)
738 void update_cpu_usage(void)
743 void update_load_average(void)
745 #ifdef HAVE_GETLOADAVG
750 info.loadavg[0] = (float) v[0];
751 info.loadavg[1] = (float) v[1];
752 info.loadavg[2] = (float) v[2];
759 if (!(fp = open_file("/proc/loadavg", &rep))) {
760 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
763 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
767 info.mask |= (1 << INFO_LOADAVG);
770 #define PROC_I8K "/proc/i8k"
771 #define I8K_DELIM " "
772 static char *i8k_procbuf = NULL;
773 void update_i8k(void)
778 i8k_procbuf = (char *) malloc(128 * sizeof(char));
780 if ((fp = fopen(PROC_I8K, "r")) == NULL) {
781 CRIT_ERR("/proc/i8k doesn't exist! use insmod to make sure the kernel "
782 "driver is loaded...");
785 memset(&i8k_procbuf[0], 0, 128);
786 if (fread(&i8k_procbuf[0], sizeof(char), 128, fp) == 0) {
787 ERR("something wrong with /proc/i8k...");
792 i8k.version = strtok(&i8k_procbuf[0], I8K_DELIM);
793 i8k.bios = strtok(NULL, I8K_DELIM);
794 i8k.serial = strtok(NULL, I8K_DELIM);
795 i8k.cpu_temp = strtok(NULL, I8K_DELIM);
796 i8k.left_fan_status = strtok(NULL, I8K_DELIM);
797 i8k.right_fan_status = strtok(NULL, I8K_DELIM);
798 i8k.left_fan_rpm = strtok(NULL, I8K_DELIM);
799 i8k.right_fan_rpm = strtok(NULL, I8K_DELIM);
800 i8k.ac_status = strtok(NULL, I8K_DELIM);
801 i8k.buttons_status = strtok(NULL, I8K_DELIM);
804 /***********************************************************/
805 /***********************************************************/
806 /***********************************************************/
808 static int no_dots(const struct dirent *d)
810 if (d->d_name[0] == '.') {
816 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
818 struct dirent **namelist;
821 n = scandir(dir, &namelist, no_dots, alphasort);
824 ERR("scandir for %s: %s", dir, strerror(errno));
835 strncpy(s, namelist[0]->d_name, 255);
838 for (i = 0; i < n; i++) {
847 int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
848 int *divisor, char *devtype)
855 memset(buf, 0, sizeof(buf));
857 /* if device is NULL or *, get first */
858 if (dev == NULL || strcmp(dev, "*") == 0) {
861 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
867 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
869 /* buf holds result from get_first_file_in_a_directory() above,
870 * e.g. "hwmon0" -- append "/device" */
871 strcat(buf, "/device");
873 /* dev holds device number N as a string,
874 * e.g. "0", -- convert to "hwmon0/device" */
875 sprintf(buf, "hwmon%s/device", dev);
880 /* change vol to in */
881 if (strcmp(type, "vol") == 0) {
885 if (strcmp(type, "tempf") == 0) {
886 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, "temp", n);
888 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
890 strncpy(devtype, path, 255);
893 fd = open(path, O_RDONLY);
895 CRIT_ERR("can't open '%s': %s\nplease check your device or remove this "
896 "var from "PACKAGE_NAME, path, strerror(errno));
899 if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
900 || strcmp(type, "tempf") == 0) {
905 /* fan does not use *_div as a read divisor */
906 if (strcmp("fan", type) == 0) {
910 /* test if *_div file exist, open it and use it as divisor */
911 if (strcmp(type, "tempf") == 0) {
912 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
914 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
917 divfd = open(path, O_RDONLY);
923 divn = read(divfd, divbuf, 63);
924 /* should read until n == 0 but I doubt that kernel will give these
925 * in multiple pieces. :) */
927 ERR("open_sysfs_sensor(): can't read from sysfs");
930 *divisor = atoi(divbuf);
939 double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
947 lseek(*fd, 0, SEEK_SET);
953 n = read(*fd, buf, 63);
954 /* should read until n == 0 but I doubt that kernel will give these
955 * in multiple pieces. :) */
957 ERR("get_sysfs_info(): read from %s failed\n", devtype);
966 *fd = open(devtype, O_RDONLY);
968 ERR("can't open '%s': %s", devtype, strerror(errno));
971 /* My dirty hack for computing CPU value
972 * Filedil, from forums.gentoo.org */
973 /* if (strstr(devtype, "temp1_input") != NULL) {
974 return -15.096 + 1.4893 * (val / 1000.0);
977 /* divide voltage and temperature by 1000 */
978 /* or if any other divisor is given, use that */
979 if (strcmp(type, "tempf") == 0) {
981 return ((val / divisor + 40) * 9.0 / 5) - 40;
982 } else if (divisor) {
983 return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
985 return ((val + 40) * 9.0 / 5) - 40;
989 return val / divisor;
990 } else if (divisor) {
998 /* Prior to kernel version 2.6.12, the CPU fan speed was available in
999 * ADT746X_FAN_OLD, whereas later kernel versions provide this information in
1001 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
1002 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
1004 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
1007 char adt746x_fan_state[64];
1010 if (!p_client_buffer || client_buffer_size <= 0) {
1014 if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
1015 && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL) {
1016 sprintf(adt746x_fan_state, "adt746x not found");
1018 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
1019 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
1023 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_fan_state);
1026 /* Prior to kernel version 2.6.12, the CPU temperature was found in
1027 * ADT746X_CPU_OLD, whereas later kernel versions provide this information in
1029 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
1030 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
1032 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
1035 char adt746x_cpu_state[64];
1038 if (!p_client_buffer || client_buffer_size <= 0) {
1042 if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
1043 && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL) {
1044 sprintf(adt746x_cpu_state, "adt746x not found");
1046 fscanf(fp, "%2s", adt746x_cpu_state);
1050 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state);
1053 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1054 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1056 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1057 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1058 const char *p_format, int divisor, unsigned int cpu)
1066 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1072 char current_freq_file[128];
1074 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1076 f = fopen(current_freq_file, "r");
1078 /* if there's a cpufreq /sys node, read the current frequency from
1079 * this node and divide by 1000 to get Mhz. */
1080 if (fgets(s, sizeof(s), f)) {
1081 s[strlen(s) - 1] = '\0';
1082 freq = strtod(s, NULL);
1085 snprintf(p_client_buffer, client_buffer_size, p_format,
1086 (freq / 1000) / divisor);
1091 // open the CPU information file
1092 f = open_file("/proc/cpuinfo", &rep);
1094 perror(PACKAGE_NAME": Failed to access '/proc/cpuinfo' at get_freq()");
1099 while (fgets(s, sizeof(s), f) != NULL) {
1101 #if defined(__i386) || defined(__x86_64)
1102 // and search for the cpu mhz
1103 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1105 #if defined(__alpha)
1106 // different on alpha
1107 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1109 // this is different on ppc for some reason
1110 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1111 #endif // defined(__alpha)
1112 #endif // defined(__i386) || defined(__x86_64)
1114 // copy just the number
1115 strcpy(frequency, strchr(s, ':') + 2);
1116 #if defined(__alpha)
1118 frequency[strlen(frequency) - 6] = '\0';
1119 // kernel reports in Hz
1120 freq = strtod(frequency, NULL) / 1000000;
1123 frequency[strlen(frequency) - 1] = '\0';
1124 freq = strtod(frequency, NULL);
1128 if (strncmp(s, "processor", 9) == 0) {
1135 snprintf(p_client_buffer, client_buffer_size, p_format,
1136 (float) freq / divisor);
1140 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1142 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1152 * Peter Tarjan (ptarjan@citromail.hu) */
1154 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1155 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1156 const char *p_format, int divisor, unsigned int cpu)
1162 char current_freq_file[128];
1165 /* build the voltage file name */
1167 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1170 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1175 /* read the current cpu frequency from the /sys node */
1176 f = fopen(current_freq_file, "r");
1178 if (fgets(s, sizeof(s), f)) {
1179 s[strlen(s) - 1] = '\0';
1180 freq = strtod(s, NULL);
1184 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1185 perror("get_voltage()");
1192 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1195 /* use the current cpu frequency to find the corresponding voltage */
1196 f = fopen(current_freq_file, "r");
1202 if (fgets(line, 255, f) == NULL) {
1205 sscanf(line, "%d %d", &freq_comp, &voltage);
1206 if (freq_comp == freq) {
1212 fprintf(stderr, PACKAGE_NAME": Failed to access '%s' at ", current_freq_file);
1213 perror("get_voltage()");
1219 snprintf(p_client_buffer, client_buffer_size, p_format,
1220 (float) voltage / divisor);
1224 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1226 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1233 if (!p_client_buffer || client_buffer_size <= 0) {
1237 /* yeah, slow... :/ */
1238 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1239 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1243 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1245 fp = open_file(buf2, &rep);
1247 snprintf(p_client_buffer, client_buffer_size,
1248 "can't open fan's state file");
1251 memset(buf, 0, sizeof(buf));
1252 fscanf(fp, "%*s %99s", buf);
1255 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1258 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1259 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1260 /* Linux 2.6.25 onwards ac adapter info is in
1261 /sys/class/power_supply/AC/
1262 On my system I get the following.
1263 /sys/class/power_supply/AC/uevent:
1264 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1267 POWER_SUPPLY_NAME=AC
1268 POWER_SUPPLY_TYPE=Mains
1269 POWER_SUPPLY_ONLINE=1
1272 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1280 if (!p_client_buffer || client_buffer_size <= 0) {
1284 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1285 fp = open_file(buf2, &rep);
1287 /* sysfs processing */
1289 if (fgets(buf, sizeof(buf), fp) == NULL)
1292 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1294 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1295 snprintf(p_client_buffer, client_buffer_size,
1296 "%s-line", (online ? "on" : "off"));
1302 /* yeah, slow... :/ */
1303 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1304 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1308 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1310 fp = open_file(buf2, &rep);
1312 snprintf(p_client_buffer, client_buffer_size,
1313 "No ac adapter found.... where is it?");
1316 memset(buf, 0, sizeof(buf));
1317 fscanf(fp, "%*s %99s", buf);
1320 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1325 /proc/acpi/thermal_zone/THRM/cooling_mode
1326 cooling mode: active
1327 /proc/acpi/thermal_zone/THRM/polling_frequency
1329 /proc/acpi/thermal_zone/THRM/state
1331 /proc/acpi/thermal_zone/THRM/temperature
1333 /proc/acpi/thermal_zone/THRM/trip_points
1335 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1338 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1339 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1341 int open_acpi_temperature(const char *name)
1347 if (name == NULL || strcmp(name, "*") == 0) {
1350 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1356 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1358 fd = open(path, O_RDONLY);
1360 ERR("can't open '%s': %s", path, strerror(errno));
1366 static double last_acpi_temp;
1367 static double last_acpi_temp_time;
1369 double get_acpi_temperature(int fd)
1375 /* don't update acpi temperature too often */
1376 if (current_update_time - last_acpi_temp_time < 11.32) {
1377 return last_acpi_temp;
1379 last_acpi_temp_time = current_update_time;
1381 /* seek to beginning */
1382 lseek(fd, 0, SEEK_SET);
1389 n = read(fd, buf, 255);
1391 ERR("can't read fd %d: %s", fd, strerror(errno));
1394 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1398 return last_acpi_temp;
1402 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1404 design capacity: 4400 mAh
1405 last full capacity: 4064 mAh
1406 battery technology: rechargeable
1407 design voltage: 14800 mV
1408 design capacity warning: 300 mAh
1409 design capacity low: 200 mAh
1410 capacity granularity 1: 32 mAh
1411 capacity granularity 2: 32 mAh
1413 serial number: 16922
1419 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1422 charging state: unknown
1424 remaining capacity: 4064 mAh
1425 present voltage: 16608 mV
1429 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1430 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1431 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1432 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1433 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1435 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1436 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1438 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1439 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1442 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1443 Linux 2.6.24 onwards battery info is in
1444 /sys/class/power_supply/BAT0/
1445 On my system I get the following.
1446 /sys/class/power_supply/BAT0/uevent:
1447 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1449 PHYSDEVDRIVER=battery
1450 POWER_SUPPLY_NAME=BAT0
1451 POWER_SUPPLY_TYPE=Battery
1452 POWER_SUPPLY_STATUS=Discharging
1453 POWER_SUPPLY_PRESENT=1
1454 POWER_SUPPLY_TECHNOLOGY=Li-ion
1455 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1456 POWER_SUPPLY_VOLTAGE_NOW=10780000
1457 POWER_SUPPLY_CURRENT_NOW=13970000
1458 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1459 POWER_SUPPLY_ENERGY_FULL=27370000
1460 POWER_SUPPLY_ENERGY_NOW=11810000
1461 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1462 POWER_SUPPLY_MANUFACTURER=Panasonic
1463 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1466 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1467 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1468 #define APM_PATH "/proc/apm"
1469 #define MAX_BATTERY_COUNT 4
1471 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1472 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1473 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1475 static int batteries_initialized = 0;
1476 static char batteries[MAX_BATTERY_COUNT][32];
1478 static int acpi_last_full[MAX_BATTERY_COUNT];
1479 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1481 /* e.g. "charging 75%" */
1482 static char last_battery_str[MAX_BATTERY_COUNT][64];
1484 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1486 static double last_battery_time[MAX_BATTERY_COUNT];
1488 static int last_battery_perct[MAX_BATTERY_COUNT];
1489 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1491 void init_batteries(void)
1495 if (batteries_initialized) {
1498 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1499 batteries[idx][0] = '\0';
1501 batteries_initialized = 1;
1504 int get_battery_idx(const char *bat)
1508 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1509 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1514 /* if not found, enter a new entry */
1515 if (!strlen(batteries[idx])) {
1516 snprintf(batteries[idx], 31, "%s", bat);
1522 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1524 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1526 static int idx, rep = 0, rep1 = 0, rep2 = 0;
1527 char acpi_path[128];
1528 char sysfs_path[128];
1530 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1531 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1535 idx = get_battery_idx(bat);
1537 /* don't update battery too often */
1538 if (current_update_time - last_battery_time[idx] < 29.5) {
1539 set_return_value(buffer, n, item, idx);
1543 last_battery_time[idx] = current_update_time;
1545 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1546 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1548 /* first try SYSFS if that fails try ACPI */
1550 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1551 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1554 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1555 acpi_bat_fp[idx] = open_file(acpi_path, &rep1);
1558 if (sysfs_bat_fp[idx] != NULL) {
1560 int present_rate = -1;
1561 int remaining_capacity = -1;
1562 char charging_state[64];
1565 strcpy(charging_state, "unknown");
1567 while (!feof(sysfs_bat_fp[idx])) {
1569 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1572 /* let's just hope units are ok */
1573 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1574 strcpy(present, "yes");
1575 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1576 strcpy(present, "no");
1577 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1578 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1579 /* present_rate is not the same as the
1580 current flowing now but it is the same value
1581 which was used in the past. so we continue
1583 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1584 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1585 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1586 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1587 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1588 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1589 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1590 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1591 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1592 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1595 fclose(sysfs_bat_fp[idx]);
1596 sysfs_bat_fp[idx] = NULL;
1598 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1599 if (remaining_capacity > acpi_last_full[idx])
1600 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1603 if (strcmp(present, "No") == 0) {
1604 strncpy(last_battery_str[idx], "not present", 64);
1607 else if (strcmp(charging_state, "Charging") == 0) {
1608 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1609 /* e.g. charging 75% */
1610 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1611 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1613 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1614 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1615 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1616 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1617 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1618 snprintf(last_battery_time_str[idx],
1619 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1621 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1622 snprintf(last_battery_time_str[idx],
1623 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1627 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1628 if (present_rate > 0) {
1629 /* e.g. discharging 35% */
1630 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1631 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1633 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1634 (long) (((float) remaining_capacity / present_rate) * 3600));
1635 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1636 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1637 snprintf(last_battery_time_str[idx],
1638 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1640 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1642 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1643 snprintf(last_battery_time_str[idx],
1644 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1648 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1649 else if (strncmp(charging_state, "Charged", 64) == 0) {
1650 /* Below happens with the second battery on my X40,
1651 * when the second one is empty and the first one
1653 if (remaining_capacity == 0)
1654 strcpy(last_battery_str[idx], "empty");
1656 strcpy(last_battery_str[idx], "charged");
1658 /* unknown, probably full / AC */
1660 if (acpi_last_full[idx] != 0
1661 && remaining_capacity != acpi_last_full[idx])
1662 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1663 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1665 strncpy(last_battery_str[idx], "AC", 64);
1667 } else if (acpi_bat_fp[idx] != NULL) {
1669 int present_rate = -1;
1670 int remaining_capacity = -1;
1671 char charging_state[64];
1674 /* read last full capacity if it's zero */
1675 if (acpi_last_full[idx] == 0) {
1676 static int rep3 = 0;
1680 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1681 fp = open_file(path, &rep3);
1686 if (fgets(b, 256, fp) == NULL) {
1689 if (sscanf(b, "last full capacity: %d",
1690 &acpi_last_full[idx]) != 0) {
1699 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1701 strcpy(charging_state, "unknown");
1703 while (!feof(acpi_bat_fp[idx])) {
1706 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1710 /* let's just hope units are ok */
1711 if (strncmp(buf, "present:", 8) == 0) {
1712 sscanf(buf, "present: %4s", present);
1713 } else if (strncmp(buf, "charging state:", 15) == 0) {
1714 sscanf(buf, "charging state: %63s", charging_state);
1715 } else if (strncmp(buf, "present rate:", 13) == 0) {
1716 sscanf(buf, "present rate: %d", &present_rate);
1717 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1718 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1721 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1722 if (remaining_capacity > acpi_last_full[idx]) {
1723 /* normalize to 100% */
1724 acpi_last_full[idx] = remaining_capacity;
1728 if (strcmp(present, "no") == 0) {
1729 strncpy(last_battery_str[idx], "not present", 64);
1731 } else if (strcmp(charging_state, "charging") == 0) {
1732 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1733 /* e.g. charging 75% */
1734 snprintf(last_battery_str[idx],
1735 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1736 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1738 format_seconds(last_battery_time_str[idx],
1739 sizeof(last_battery_time_str[idx]) - 1,
1740 (long) (((acpi_last_full[idx] - remaining_capacity) *
1741 3600) / present_rate));
1742 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1743 snprintf(last_battery_str[idx],
1744 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1745 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1746 snprintf(last_battery_time_str[idx],
1747 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1749 strncpy(last_battery_str[idx], "charging",
1750 sizeof(last_battery_str[idx]) - 1);
1751 snprintf(last_battery_time_str[idx],
1752 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1755 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1756 if (present_rate > 0) {
1757 /* e.g. discharging 35% */
1758 snprintf(last_battery_str[idx],
1759 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1760 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1762 format_seconds(last_battery_time_str[idx],
1763 sizeof(last_battery_time_str[idx]) - 1,
1764 (long) ((remaining_capacity * 3600) / present_rate));
1765 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1766 snprintf(last_battery_str[idx],
1767 sizeof(last_battery_str[idx]) - 1, "full");
1768 snprintf(last_battery_time_str[idx],
1769 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1771 snprintf(last_battery_str[idx],
1772 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1773 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1774 snprintf(last_battery_time_str[idx],
1775 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1778 } else if (strncmp(charging_state, "charged", 64) == 0) {
1779 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1780 /* Below happens with the second battery on my X40,
1781 * when the second one is empty and the first one being charged. */
1782 if (remaining_capacity == 0) {
1783 strcpy(last_battery_str[idx], "empty");
1785 strcpy(last_battery_str[idx], "charged");
1787 /* unknown, probably full / AC */
1789 if (acpi_last_full[idx] != 0
1790 && remaining_capacity != acpi_last_full[idx]) {
1791 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1792 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1794 strncpy(last_battery_str[idx], "AC", 64);
1799 if (apm_bat_fp[idx] == NULL) {
1800 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1803 if (apm_bat_fp[idx] != NULL) {
1804 unsigned int ac, status, flag;
1807 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1808 &ac, &status, &flag, &life);
1811 /* could check now that there is ac */
1812 snprintf(last_battery_str[idx], 64, "AC");
1814 /* could check that status == 3 here? */
1815 } else if (ac && life != 100) {
1816 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1818 snprintf(last_battery_str[idx], 64, "%d%%", life);
1821 /* it seemed to buffer it so file must be closed (or could use
1822 * syscalls directly but I don't feel like coding it now) */
1823 fclose(apm_bat_fp[idx]);
1824 apm_bat_fp[idx] = NULL;
1827 set_return_value(buffer, n, item, idx);
1830 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1833 case BATTERY_STATUS:
1834 snprintf(buffer, n, "%s", last_battery_str[idx]);
1837 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1844 int get_battery_perct(const char *bat)
1848 char acpi_path[128];
1849 char sysfs_path[128];
1850 int remaining_capacity = -1;
1852 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1853 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1857 idx = get_battery_idx(bat);
1859 /* don't update battery too often */
1860 if (current_update_time - last_battery_perct_time[idx] < 30) {
1861 return last_battery_perct[idx];
1863 last_battery_perct_time[idx] = current_update_time;
1865 /* Only check for SYSFS or ACPI */
1867 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1868 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1872 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1873 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1876 if (sysfs_bat_fp[idx] != NULL) {
1878 while (!feof(sysfs_bat_fp[idx])) {
1880 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1883 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1884 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1885 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1886 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1887 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1888 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1889 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1890 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1894 fclose(sysfs_bat_fp[idx]);
1895 sysfs_bat_fp[idx] = NULL;
1897 } else if (acpi_bat_fp[idx] != NULL) {
1899 /* read last full capacity if it's zero */
1900 if (acpi_design_capacity[idx] == 0) {
1905 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1906 fp = open_file(path, &rep2);
1911 if (fgets(b, 256, fp) == NULL) {
1914 if (sscanf(b, "last full capacity: %d",
1915 &acpi_design_capacity[idx]) != 0) {
1923 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1925 while (!feof(acpi_bat_fp[idx])) {
1928 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1932 if (buf[0] == 'r') {
1933 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1937 if (remaining_capacity < 0) {
1940 /* compute the battery percentage */
1941 last_battery_perct[idx] =
1942 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
1943 if (last_battery_perct[idx] > 100) last_battery_perct[idx] = 100;
1944 return last_battery_perct[idx];
1947 int get_battery_perct_bar(const char *bar)
1951 get_battery_perct(bar);
1952 idx = get_battery_idx(bar);
1953 return (int) (last_battery_perct[idx] * 2.56 - 1);
1956 /* On Apple powerbook and ibook:
1957 $ cat /proc/pmu/battery_0
1964 $ cat /proc/pmu/info
1965 PMU driver version : 2
1966 PMU firmware version : 0c
1971 /* defines as in <linux/pmu.h> */
1972 #define PMU_BATT_PRESENT 0x00000001
1973 #define PMU_BATT_CHARGING 0x00000002
1975 static FILE *pmu_battery_fp;
1976 static FILE *pmu_info_fp;
1977 static char pb_battery_info[3][32];
1978 static double pb_battery_info_update;
1980 #define PMU_PATH "/proc/pmu"
1981 void get_powerbook_batt_info(char *buffer, size_t n, int i)
1984 const char *batt_path = PMU_PATH "/battery_0";
1985 const char *info_path = PMU_PATH "/info";
1987 int charge, max_charge, ac = -1;
1990 /* don't update battery too often */
1991 if (current_update_time - pb_battery_info_update < 29.5) {
1992 snprintf(buffer, n, "%s", pb_battery_info[i]);
1995 pb_battery_info_update = current_update_time;
1997 if (pmu_battery_fp == NULL) {
1998 pmu_battery_fp = open_file(batt_path, &rep);
2001 if (pmu_battery_fp != NULL) {
2002 rewind(pmu_battery_fp);
2003 while (!feof(pmu_battery_fp)) {
2006 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2010 if (buf[0] == 'f') {
2011 sscanf(buf, "flags : %8x", &flags);
2012 } else if (buf[0] == 'c' && buf[1] == 'h') {
2013 sscanf(buf, "charge : %d", &charge);
2014 } else if (buf[0] == 'm') {
2015 sscanf(buf, "max_charge : %d", &max_charge);
2016 } else if (buf[0] == 't') {
2017 sscanf(buf, "time rem. : %ld", &timeval);
2021 if (pmu_info_fp == NULL) {
2022 pmu_info_fp = open_file(info_path, &rep);
2025 if (pmu_info_fp != NULL) {
2026 rewind(pmu_info_fp);
2027 while (!feof(pmu_info_fp)) {
2030 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2033 if (buf[0] == 'A') {
2034 sscanf(buf, "AC Power : %d", &ac);
2038 /* update status string */
2039 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2040 strcpy(pb_battery_info[PB_BATT_STATUS], "AC");
2041 } else if (ac && (flags & PMU_BATT_PRESENT)
2042 && !(flags & PMU_BATT_CHARGING)) {
2043 strcpy(pb_battery_info[PB_BATT_STATUS], "charged");
2044 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2045 strcpy(pb_battery_info[PB_BATT_STATUS], "charging");
2047 strcpy(pb_battery_info[PB_BATT_STATUS], "discharging");
2050 /* update percentage string */
2052 pb_battery_info[PB_BATT_PERCENT][0] = 0;
2054 snprintf(pb_battery_info[PB_BATT_PERCENT],
2055 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2056 (charge * 100) / max_charge);
2059 /* update time string */
2060 if (timeval == 0) { /* fully charged or battery not present */
2061 pb_battery_info[PB_BATT_TIME][0] = 0;
2062 } else if (timeval < 60 * 60) { /* don't show secs */
2063 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2064 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2066 format_seconds(pb_battery_info[PB_BATT_TIME],
2067 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2070 snprintf(buffer, n, "%s", pb_battery_info[i]);
2073 void update_top(void)
2075 show_nice_processes = 1;
2076 process_find_top(info.cpu, info.memu);
2077 info.first_process = get_first_process();
2080 /* Here come the IBM ACPI-specific things. For reference, see
2081 * http://ibm-acpi.sourceforge.net/README
2082 * If IBM ACPI is installed, /proc/acpi/ibm contains the following files:
2098 * The content of these files is described in detail in the aforementioned
2099 * README - some of them also in the following functions accessing them.
2100 * Peter Tarjan (ptarjan@citromail.hu) */
2102 #define IBM_ACPI_DIR "/proc/acpi/ibm"
2104 /* get fan speed on IBM/Lenovo laptops running the ibm acpi.
2105 * /proc/acpi/ibm/fan looks like this (3 lines):
2108 commands: enable, disable
2109 * Peter Tarjan (ptarjan@citromail.hu) */
2111 void get_ibm_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
2114 unsigned int speed = 0;
2117 if (!p_client_buffer || client_buffer_size <= 0) {
2121 snprintf(fan, 127, "%s/fan", IBM_ACPI_DIR);
2123 fp = fopen(fan, "r");
2128 if (fgets(line, 255, fp) == NULL) {
2131 if (sscanf(line, "speed: %u", &speed)) {
2136 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2137 "ibm* from your "PACKAGE_NAME" config file.", fan, strerror(errno));
2141 snprintf(p_client_buffer, client_buffer_size, "%d", speed);
2144 /* get the measured temperatures from the temperature sensors
2145 * on IBM/Lenovo laptops running the ibm acpi.
2146 * There are 8 values in /proc/acpi/ibm/thermal, and according to
2147 * http://ibm-acpi.sourceforge.net/README
2148 * these mean the following (at least on an IBM R51...)
2149 * 0: CPU (also on the T series laptops)
2150 * 1: Mini PCI Module (?)
2152 * 3: GPU (also on the T series laptops)
2157 * I'm not too sure about those with the question mark, but the values I'm
2158 * reading from *my* thermal file (on a T42p) look realistic for the
2159 * hdd and the battery.
2160 * #5 and #7 are always -128.
2161 * /proc/acpi/ibm/thermal looks like this (1 line):
2162 temperatures: 41 43 31 46 33 -128 29 -128
2163 * Peter Tarjan (ptarjan@citromail.hu) */
2165 static double last_ibm_acpi_temp_time;
2166 void get_ibm_acpi_temps(void)
2172 /* don't update too often */
2173 if (current_update_time - last_ibm_acpi_temp_time < 10.00) {
2176 last_ibm_acpi_temp_time = current_update_time;
2178 /* if (!p_client_buffer || client_buffer_size <= 0) {
2182 snprintf(thermal, 127, "%s/thermal", IBM_ACPI_DIR);
2183 fp = fopen(thermal, "r");
2189 if (fgets(line, 255, fp) == NULL) {
2192 if (sscanf(line, "temperatures: %d %d %d %d %d %d %d %d",
2193 &ibm_acpi.temps[0], &ibm_acpi.temps[1], &ibm_acpi.temps[2],
2194 &ibm_acpi.temps[3], &ibm_acpi.temps[4], &ibm_acpi.temps[5],
2195 &ibm_acpi.temps[6], &ibm_acpi.temps[7])) {
2200 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2201 "ibm* from your "PACKAGE_NAME" config file.", thermal, strerror(errno));
2207 /* get volume (0-14) on IBM/Lenovo laptops running the ibm acpi.
2208 * "Volume" here is none of the mixer volumes, but a "master of masters"
2209 * volume adjusted by the IBM volume keys.
2210 * /proc/acpi/ibm/fan looks like this (4 lines):
2213 commands: up, down, mute
2214 commands: level <level> (<level> is 0-15)
2215 * Peter Tarjan (ptarjan@citromail.hu) */
2217 void get_ibm_acpi_volume(char *p_client_buffer, size_t client_buffer_size)
2221 unsigned int vol = -1;
2224 if (!p_client_buffer || client_buffer_size <= 0) {
2228 snprintf(volume, 127, "%s/volume", IBM_ACPI_DIR);
2230 fp = fopen(volume, "r");
2234 unsigned int read_vol = -1;
2236 if (fgets(line, 255, fp) == NULL) {
2239 if (sscanf(line, "level: %u", &read_vol)) {
2243 if (sscanf(line, "mute: %s", mute)) {
2248 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2249 "ibm* from your "PACKAGE_NAME" config file.", volume, strerror(errno));
2254 if (strcmp(mute, "on") == 0) {
2255 snprintf(p_client_buffer, client_buffer_size, "%s", "mute");
2258 snprintf(p_client_buffer, client_buffer_size, "%d", vol);
2263 /* static FILE *fp = NULL; */
2265 /* get LCD brightness on IBM/Lenovo laptops running the ibm acpi.
2266 * /proc/acpi/ibm/brightness looks like this (3 lines):
2269 commands: level <level> (<level> is 0-7)
2270 * Peter Tarjan (ptarjan@citromail.hu) */
2272 void get_ibm_acpi_brightness(char *p_client_buffer, size_t client_buffer_size)
2275 unsigned int brightness = 0;
2278 if (!p_client_buffer || client_buffer_size <= 0) {
2282 snprintf(filename, 127, "%s/brightness", IBM_ACPI_DIR);
2284 fp = fopen(filename, "r");
2289 if (fgets(line, 255, fp) == NULL) {
2292 if (sscanf(line, "level: %u", &brightness)) {
2297 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2298 "ibm* from your "PACKAGE_NAME" config file.", filename, strerror(errno));
2303 snprintf(p_client_buffer, client_buffer_size, "%d", brightness);
2306 void update_entropy(void)
2309 const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2310 const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2313 info.entropy.entropy_avail = 0;
2314 info.entropy.poolsize = 0;
2316 if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2320 if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2325 fscanf(fp1, "%u", &info.entropy.entropy_avail);
2326 fscanf(fp2, "%u", &info.entropy.poolsize);
2331 info.mask |= (1 << INFO_ENTROPY);
2334 const char *get_disk_protect_queue(const char *disk)
2340 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2341 if ((fp = fopen(path, "r")) == NULL)
2343 if (fscanf(fp, "%d\n", &state) != 1) {
2348 return state ? "frozen" : "free ";