1 /* Conky, a system monitor, based on torsmo
3 * Any original torsmo code is licensed under the BSD license
5 * All code written since the fork of torsmo is licensed under the GPL
7 * Please see COPYING for details
9 * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
10 * Copyright (c) 2007 Toni Spets
11 * Copyright (c) 2005-2008 Brenden Matthews, Philip Kovacs, et. al.
13 * All rights reserved.
15 * This program is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation, either version 3 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program. If not, see <http://www.gnu.org/licenses/>.
34 #include <sys/types.h>
35 #include <sys/sysinfo.h>
37 #ifndef HAVE_CLOCK_GETTIME
42 // #include <assert.h>
46 #include <sys/ioctl.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <linux/sockios.h>
51 #include <arpa/inet.h>
55 #include <linux/route.h>
62 #define SHORTSTAT_TEMPL "%*s %llu %llu %llu"
63 #define LONGSTAT_TEMPL "%*s %llu %llu %llu "
65 static int show_nice_processes;
67 /* This flag tells the linux routines to use the /proc system where possible,
68 * even if other api's are available, e.g. sysinfo() or getloadavg().
69 * the reason for this is to allow for /proc-based distributed monitoring.
70 * using a flag in this manner creates less confusing code. */
71 static int prefer_proc = 0;
73 void prepare_update(void)
77 void update_uptime(void)
81 struct sysinfo s_info;
84 info.uptime = (double) s_info.uptime;
91 if (!(fp = open_file("/proc/uptime", &rep))) {
95 fscanf(fp, "%lf", &info.uptime);
98 info.mask |= (1 << INFO_UPTIME);
101 int check_mount(char *s)
104 FILE *mtab = fopen("/etc/mtab", "r");
107 char buf1[256], buf2[128];
109 while (fgets(buf1, 256, mtab)) {
110 sscanf(buf1, "%*s %128s", buf2);
111 if (!strcmp(s, buf2)) {
118 ERR("Could not open mtab");
123 /* these things are also in sysinfo except Buffers:
124 * (that's why I'm reading them from proc) */
126 void update_meminfo(void)
131 /* unsigned int a; */
134 info.mem = info.memmax = info.swap = info.swapmax = info.bufmem =
135 info.buffers = info.cached = info.memfree = info.memeasyfree = 0;
137 if (!(meminfo_fp = open_file("/proc/meminfo", &rep))) {
141 while (!feof(meminfo_fp)) {
142 if (fgets(buf, 255, meminfo_fp) == NULL) {
146 if (strncmp(buf, "MemTotal:", 9) == 0) {
147 sscanf(buf, "%*s %llu", &info.memmax);
148 } else if (strncmp(buf, "MemFree:", 8) == 0) {
149 sscanf(buf, "%*s %llu", &info.memfree);
150 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
151 sscanf(buf, "%*s %llu", &info.swapmax);
152 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
153 sscanf(buf, "%*s %llu", &info.swap);
154 } else if (strncmp(buf, "Buffers:", 8) == 0) {
155 sscanf(buf, "%*s %llu", &info.buffers);
156 } else if (strncmp(buf, "Cached:", 7) == 0) {
157 sscanf(buf, "%*s %llu", &info.cached);
161 info.mem = info.memmax - info.memfree;
162 info.memeasyfree = info.memfree;
163 info.swap = info.swapmax - info.swap;
165 info.bufmem = info.cached + info.buffers;
167 info.mask |= (1 << INFO_MEM) | (1 << INFO_BUFFERS);
172 int get_laptop_mode(void)
177 if ((fp = fopen("/proc/sys/vm/laptop_mode", "r")) != NULL)
178 fscanf(fp, "%d\n", &val);
184 * # cat /sys/block/sda/queue/scheduler
185 * noop [anticipatory] cfq
187 char *get_ioscheduler(char *disk)
193 return strndup("n/a", text_buffer_size);
195 snprintf(buf, 127, "/sys/block/%s/queue/scheduler", disk);
196 if ((fp = fopen(buf, "r")) == NULL) {
197 return strndup("n/a", text_buffer_size);
200 fscanf(fp, "%127s", buf);
202 buf[strlen(buf) - 1] = '\0';
204 return strndup(buf + 1, text_buffer_size);
208 return strndup("n/a", text_buffer_size);
211 int interface_up(const char *dev)
216 if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
217 CRIT_ERR("could not create sockfd");
220 strncpy(ifr.ifr_name, dev, IFNAMSIZ);
221 if (ioctl(fd, SIOCGIFFLAGS, &ifr)) {
222 /* if device does not exist, treat like not up */
224 perror("SIOCGIFFLAGS");
228 if (!(ifr.ifr_flags & IFF_UP)) /* iface is not up */
230 if (ifup_strictness == IFUP_UP)
233 if (!(ifr.ifr_flags & IFF_RUNNING))
235 if (ifup_strictness == IFUP_LINK)
238 if (ioctl(fd, SIOCGIFADDR, &ifr)) {
239 perror("SIOCGIFADDR");
242 if (((struct sockaddr_in *)&(ifr.ifr_ifru.ifru_addr))->sin_addr.s_addr)
253 #define COND_FREE(x) if(x) free(x); x = 0
254 #define SAVE_SET_STRING(x, y) \
255 if (x && strcmp((char *)x, (char *)y)) { \
257 x = strndup("multiple", text_buffer_size); \
259 x = strndup(y, text_buffer_size); \
262 void update_gateway_info_failure(const char *reason)
267 //2 pointers to 1 location causes a crash when we try to free them both
268 info.gw_info.iface = strndup("failed", text_buffer_size);
269 info.gw_info.ip = strndup("failed", text_buffer_size);
272 void update_gateway_info(void)
277 unsigned long dest, gate, mask;
279 short ref, use, metric, mtu, win, irtt;
281 struct gateway_info *gw_info = &info.gw_info;
283 COND_FREE(gw_info->iface);
284 COND_FREE(gw_info->ip);
287 if ((fp = fopen("/proc/net/route", "r")) == NULL) {
288 update_gateway_info_failure("fopen()");
291 if (fscanf(fp, "%*[^\n]\n") == EOF) {
292 //NULL because a empty table is not a error
293 update_gateway_info_failure(NULL);
298 // Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
299 if(fscanf(fp, "%63s %lx %lx %x %hd %hd %hd %lx %hd %hd %hd\n",
300 iface, &dest, &gate, &flags, &ref, &use,
301 &metric, &mask, &mtu, &win, &irtt) != 11) {
302 update_gateway_info_failure("fscanf()");
306 if (flags & RTF_GATEWAY && dest == 0 && mask == 0) {
308 SAVE_SET_STRING(gw_info->iface, iface)
310 SAVE_SET_STRING(gw_info->ip, inet_ntoa(ina))
317 void update_net_stats(void)
322 // FIXME: arbitrary size chosen to keep code simple.
324 unsigned int curtmp1, curtmp2;
331 // wireless info variables
332 int skfd, has_bitrate = 0;
333 struct wireless_info *winfo;
338 delta = current_update_time - last_update_time;
339 if (delta <= 0.0001) {
343 /* open file and ignore first two lines */
344 if (!(net_dev_fp = open_file("/proc/net/dev", &rep))) {
349 fgets(buf, 255, net_dev_fp); /* garbage */
350 fgets(buf, 255, net_dev_fp); /* garbage (field names) */
352 /* read each interface */
353 for (i2 = 0; i2 < 16; i2++) {
357 long long r, t, last_recv, last_trans;
359 if (fgets(buf, 255, net_dev_fp) == NULL) {
363 while (isspace((int) *p)) {
369 while (*p && *p != ':') {
378 ns = get_net_stat(s);
380 memset(&(ns->addr.sa_data), 0, 14);
382 if(NULL == ns->addrs)
383 ns->addrs = (char*) malloc(17 * 16);
384 if(NULL != ns->addrs)
385 memset(ns->addrs, 0, 17 * 16); /* Up to 17 chars per ip, max 16 interfaces. Nasty memory usage... */
387 last_recv = ns->recv;
388 last_trans = ns->trans;
390 /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
391 sscanf(p, "%lld %*d %*d %*d %*d %*d %*d %*d %lld",
394 /* if recv or trans is less than last time, an overflow happened */
395 if (r < ns->last_read_recv) {
398 ns->recv += (r - ns->last_read_recv);
400 ns->last_read_recv = r;
402 if (t < ns->last_read_trans) {
405 ns->trans += (t - ns->last_read_trans);
407 ns->last_read_trans = t;
409 /*** ip addr patch ***/
410 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
412 conf.ifc_buf = malloc(sizeof(struct ifreq) * 16);
413 conf.ifc_len = sizeof(struct ifreq) * 16;
414 memset(conf.ifc_buf, 0, conf.ifc_len);
416 ioctl((long) i, SIOCGIFCONF, &conf);
418 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
419 struct net_stat *ns2;
421 if (!(((struct ifreq *) conf.ifc_buf) + k))
425 ((struct ifreq *) conf.ifc_buf)[k].ifr_ifrn.ifrn_name);
426 ns2->addr = ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.ifru_addr;
427 if(NULL != ns2->addrs) {
428 sprintf(temp_addr, "%u.%u.%u.%u, ",
429 ns2->addr.sa_data[2] & 255,
430 ns2->addr.sa_data[3] & 255,
431 ns2->addr.sa_data[4] & 255,
432 ns2->addr.sa_data[5] & 255);
433 if(NULL == strstr(ns2->addrs, temp_addr))
434 strncpy(ns2->addrs + strlen(ns2->addrs), temp_addr, 17);
442 /*** end ip addr patch ***/
444 /* calculate speeds */
445 ns->net_rec[0] = (ns->recv - last_recv) / delta;
446 ns->net_trans[0] = (ns->trans - last_trans) / delta;
450 for (i = 0; (unsigned) i < info.net_avg_samples; i++) {
451 curtmp1 += ns->net_rec[i];
452 curtmp2 += ns->net_trans[i];
460 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
461 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
462 if (info.net_avg_samples > 1) {
463 for (i = info.net_avg_samples; i > 1; i--) {
464 ns->net_rec[i - 1] = ns->net_rec[i - 2];
465 ns->net_trans[i - 1] = ns->net_trans[i - 2];
470 /* update wireless info */
471 winfo = malloc(sizeof(struct wireless_info));
472 memset(winfo, 0, sizeof(struct wireless_info));
474 skfd = iw_sockets_open();
475 if (iw_get_basic_config(skfd, s, &(winfo->b)) > -1) {
477 // set present winfo variables
478 if (iw_get_stats(skfd, s, &(winfo->stats),
479 &winfo->range, winfo->has_range) >= 0) {
480 winfo->has_stats = 1;
482 if (iw_get_range_info(skfd, s, &(winfo->range)) >= 0) {
483 winfo->has_range = 1;
485 if (iw_get_ext(skfd, s, SIOCGIWAP, &wrq) >= 0) {
486 winfo->has_ap_addr = 1;
487 memcpy(&(winfo->ap_addr), &(wrq.u.ap_addr), sizeof(sockaddr));
491 if (iw_get_ext(skfd, s, SIOCGIWRATE, &wrq) >= 0) {
492 memcpy(&(winfo->bitrate), &(wrq.u.bitrate), sizeof(iwparam));
493 iw_print_bitrate(ns->bitrate, 16, winfo->bitrate.value);
498 if (winfo->has_range && winfo->has_stats
499 && ((winfo->stats.qual.level != 0)
500 || (winfo->stats.qual.updated & IW_QUAL_DBM))) {
501 if (!(winfo->stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
502 ns->link_qual = winfo->stats.qual.qual;
503 ns->link_qual_max = winfo->range.max_qual.qual;
508 if (winfo->has_ap_addr) {
509 iw_sawap_ntop(&winfo->ap_addr, ns->ap);
513 if (winfo->b.has_essid) {
514 if (winfo->b.essid_on) {
515 snprintf(ns->essid, 32, "%s", winfo->b.essid);
517 snprintf(ns->essid, 32, "off/any");
521 snprintf(ns->mode, 16, "%s", iw_operation_mode[winfo->b.mode]);
523 iw_sockets_close(skfd);
530 info.mask |= (1 << INFO_NET);
535 void update_total_processes(void)
539 struct sysinfo s_info;
542 info.procs = s_info.procs;
549 if (!(fp = open_file("/proc/loadavg", &rep))) {
553 fscanf(fp, "%*f %*f %*f %*d/%hu", &info.procs);
556 info.mask |= (1 << INFO_PROCS);
559 #define CPU_SAMPLE_COUNT 15
561 unsigned long long cpu_user;
562 unsigned long long cpu_system;
563 unsigned long long cpu_nice;
564 unsigned long long cpu_idle;
565 unsigned long long cpu_iowait;
566 unsigned long long cpu_irq;
567 unsigned long long cpu_softirq;
568 unsigned long long cpu_steal;
569 unsigned long long cpu_total;
570 unsigned long long cpu_active_total;
571 unsigned long long cpu_last_total;
572 unsigned long long cpu_last_active_total;
573 double cpu_val[CPU_SAMPLE_COUNT];
575 static short cpu_setup = 0;
577 /* Determine if this kernel gives us "extended" statistics information in
579 * Kernels around 2.5 and earlier only reported user, system, nice, and
580 * idle values in proc stat.
581 * Kernels around 2.6 and greater report these PLUS iowait, irq, softirq,
583 void determine_longstat(char *buf)
585 unsigned long long iowait = 0;
587 KFLAG_SETOFF(KFLAG_IS_LONGSTAT);
588 /* scanf will either return -1 or 1 because there is only 1 assignment */
589 if (sscanf(buf, "%*s %*d %*d %*d %*d %llu", &iowait) > 0) {
590 KFLAG_SETON(KFLAG_IS_LONGSTAT);
594 void get_cpu_count(void)
600 if (info.cpu_usage) {
604 if (!(stat_fp = open_file("/proc/stat", &rep))) {
610 while (!feof(stat_fp)) {
611 if (fgets(buf, 255, stat_fp) == NULL) {
615 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
616 if (info.cpu_count == 0) {
617 determine_longstat(buf);
622 info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
627 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
628 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
630 inline static void update_stat(void)
634 static struct cpu_info *cpu = NULL;
639 const char *stat_template = NULL;
640 unsigned int malloc_cpu_size = 0;
642 /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
643 if (!cpu_setup || !info.cpu_usage) {
648 if (!stat_template) {
650 KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT;
654 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
655 cpu = malloc(malloc_cpu_size);
656 memset(cpu, 0, malloc_cpu_size);
659 if (!(stat_fp = open_file("/proc/stat", &rep))) {
661 if (info.cpu_usage) {
662 memset(info.cpu_usage, 0, info.cpu_count * sizeof(float));
668 while (!feof(stat_fp)) {
669 if (fgets(buf, 255, stat_fp) == NULL) {
673 if (strncmp(buf, "procs_running ", 14) == 0) {
674 sscanf(buf, "%*s %hu", &info.run_procs);
675 info.mask |= (1 << INFO_RUN_PROCS);
676 } else if (strncmp(buf, "cpu", 3) == 0) {
678 idx = isdigit(buf[3]) ? ((int) buf[3]) - 0x2F : 0;
679 sscanf(buf, stat_template, &(cpu[idx].cpu_user),
680 &(cpu[idx].cpu_nice), &(cpu[idx].cpu_system),
681 &(cpu[idx].cpu_idle), &(cpu[idx].cpu_iowait),
682 &(cpu[idx].cpu_irq), &(cpu[idx].cpu_softirq),
683 &(cpu[idx].cpu_steal));
685 cpu[idx].cpu_total = cpu[idx].cpu_user + cpu[idx].cpu_nice +
686 cpu[idx].cpu_system + cpu[idx].cpu_idle +
687 cpu[idx].cpu_iowait + cpu[idx].cpu_irq +
688 cpu[idx].cpu_softirq + cpu[idx].cpu_steal;
690 cpu[idx].cpu_active_total = cpu[idx].cpu_total -
691 (cpu[idx].cpu_idle + cpu[idx].cpu_iowait);
692 info.mask |= (1 << INFO_CPU);
694 delta = current_update_time - last_update_time;
696 if (delta <= 0.001) {
700 cpu[idx].cpu_val[0] = (cpu[idx].cpu_active_total -
701 cpu[idx].cpu_last_active_total) /
702 (float) (cpu[idx].cpu_total - cpu[idx].cpu_last_total);
704 for (i = 0; i < info.cpu_avg_samples; i++) {
705 curtmp += cpu[idx].cpu_val[i];
707 /* TESTING -- I've removed this, because I don't think it is right.
708 * You shouldn't divide by the cpu count here ...
709 * removing for testing */
711 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples /
714 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
716 /* TESTING -- this line replaces the prev. "suspect" if/else */
717 info.cpu_usage[idx] = curtmp / info.cpu_avg_samples;
719 cpu[idx].cpu_last_total = cpu[idx].cpu_total;
720 cpu[idx].cpu_last_active_total = cpu[idx].cpu_active_total;
721 for (i = info.cpu_avg_samples - 1; i > 0; i--) {
722 cpu[idx].cpu_val[i] = cpu[idx].cpu_val[i - 1];
729 void update_running_processes(void)
734 void update_cpu_usage(void)
739 void update_load_average(void)
741 #ifdef HAVE_GETLOADAVG
746 info.loadavg[0] = (float) v[0];
747 info.loadavg[1] = (float) v[1];
748 info.loadavg[2] = (float) v[2];
755 if (!(fp = open_file("/proc/loadavg", &rep))) {
756 info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
759 fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
763 info.mask |= (1 << INFO_LOADAVG);
766 #define PROC_I8K "/proc/i8k"
767 #define I8K_DELIM " "
768 static char *i8k_procbuf = NULL;
769 void update_i8k(void)
774 i8k_procbuf = (char *) malloc(128 * sizeof(char));
776 if ((fp = fopen(PROC_I8K, "r")) == NULL) {
777 CRIT_ERR("/proc/i8k doesn't exist! use insmod to make sure the kernel "
778 "driver is loaded...");
781 memset(&i8k_procbuf[0], 0, 128);
782 if (fread(&i8k_procbuf[0], sizeof(char), 128, fp) == 0) {
783 ERR("something wrong with /proc/i8k...");
788 i8k.version = strtok(&i8k_procbuf[0], I8K_DELIM);
789 i8k.bios = strtok(NULL, I8K_DELIM);
790 i8k.serial = strtok(NULL, I8K_DELIM);
791 i8k.cpu_temp = strtok(NULL, I8K_DELIM);
792 i8k.left_fan_status = strtok(NULL, I8K_DELIM);
793 i8k.right_fan_status = strtok(NULL, I8K_DELIM);
794 i8k.left_fan_rpm = strtok(NULL, I8K_DELIM);
795 i8k.right_fan_rpm = strtok(NULL, I8K_DELIM);
796 i8k.ac_status = strtok(NULL, I8K_DELIM);
797 i8k.buttons_status = strtok(NULL, I8K_DELIM);
800 /***********************************************************/
801 /***********************************************************/
802 /***********************************************************/
804 static int no_dots(const struct dirent *d)
806 if (d->d_name[0] == '.') {
812 static int get_first_file_in_a_directory(const char *dir, char *s, int *rep)
814 struct dirent **namelist;
817 n = scandir(dir, &namelist, no_dots, alphasort);
820 ERR("scandir for %s: %s", dir, strerror(errno));
831 strncpy(s, namelist[0]->d_name, 255);
834 for (i = 0; i < n; i++) {
843 int open_sysfs_sensor(const char *dir, const char *dev, const char *type, int n,
844 int *divisor, char *devtype)
851 memset(buf, 0, sizeof(buf));
853 /* if device is NULL or *, get first */
854 if (dev == NULL || strcmp(dev, "*") == 0) {
857 if (!get_first_file_in_a_directory(dir, buf, &rep)) {
863 if (strcmp(dir, "/sys/class/hwmon/") == 0) {
865 /* buf holds result from get_first_file_in_a_directory() above,
866 * e.g. "hwmon0" -- append "/device" */
867 strcat(buf, "/device");
869 /* dev holds device number N as a string,
870 * e.g. "0", -- convert to "hwmon0/device" */
871 sprintf(buf, "hwmon%s/device", dev);
876 /* change vol to in */
877 if (strcmp(type, "vol") == 0) {
881 if (strcmp(type, "tempf") == 0) {
882 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, "temp", n);
884 snprintf(path, 255, "%s%s/%s%d_input", dir, dev, type, n);
886 strncpy(devtype, path, 255);
889 fd = open(path, O_RDONLY);
891 CRIT_ERR("can't open '%s': %s\nplease check your device or remove this "
892 "var from Conky", path, strerror(errno));
895 if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
896 || strcmp(type, "tempf") == 0) {
901 /* fan does not use *_div as a read divisor */
902 if (strcmp("fan", type) == 0) {
906 /* test if *_div file exist, open it and use it as divisor */
907 if (strcmp(type, "tempf") == 0) {
908 snprintf(path, 255, "%s%s/%s%d_div", dir, "one", "two", n);
910 snprintf(path, 255, "%s%s/%s%d_div", dir, dev, type, n);
913 divfd = open(path, O_RDONLY);
919 divn = read(divfd, divbuf, 63);
920 /* should read until n == 0 but I doubt that kernel will give these
921 * in multiple pieces. :) */
923 ERR("open_sysfs_sensor(): can't read from sysfs");
926 *divisor = atoi(divbuf);
935 double get_sysfs_info(int *fd, int divisor, char *devtype, char *type)
943 lseek(*fd, 0, SEEK_SET);
949 n = read(*fd, buf, 63);
950 /* should read until n == 0 but I doubt that kernel will give these
951 * in multiple pieces. :) */
953 ERR("get_sysfs_info(): read from %s failed\n", devtype);
962 *fd = open(devtype, O_RDONLY);
964 ERR("can't open '%s': %s", devtype, strerror(errno));
967 /* My dirty hack for computing CPU value
968 * Filedil, from forums.gentoo.org */
969 /* if (strstr(devtype, "temp1_input") != NULL) {
970 return -15.096 + 1.4893 * (val / 1000.0);
973 /* divide voltage and temperature by 1000 */
974 /* or if any other divisor is given, use that */
975 if (strcmp(type, "tempf") == 0) {
977 return ((val / divisor + 40) * 9.0 / 5) - 40;
978 } else if (divisor) {
979 return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
981 return ((val + 40) * 9.0 / 5) - 40;
985 return val / divisor;
986 } else if (divisor) {
994 /* Prior to kernel version 2.6.12, the CPU fan speed was available in
995 * ADT746X_FAN_OLD, whereas later kernel versions provide this information in
997 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
998 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
1000 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
1003 char adt746x_fan_state[64];
1006 if (!p_client_buffer || client_buffer_size <= 0) {
1010 if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
1011 && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL) {
1012 sprintf(adt746x_fan_state, "adt746x not found");
1014 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
1015 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
1019 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_fan_state);
1022 /* Prior to kernel version 2.6.12, the CPU temperature was found in
1023 * ADT746X_CPU_OLD, whereas later kernel versions provide this information in
1025 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
1026 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
1028 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
1031 char adt746x_cpu_state[64];
1034 if (!p_client_buffer || client_buffer_size <= 0) {
1038 if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
1039 && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL) {
1040 sprintf(adt746x_cpu_state, "adt746x not found");
1042 fscanf(fp, "%2s", adt746x_cpu_state);
1046 snprintf(p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state);
1049 /* Thanks to "Walt Nelson" <wnelsonjr@comcast.net> */
1051 /***********************************************************************/
1052 /* This file is part of x86info.
1053 * (C) 2001 Dave Jones.
1055 * Licensed under the terms of the GNU GPL License version 2.
1057 * Estimate CPU MHz routine by Andrea Arcangeli <andrea@suse.de>
1058 * Small changes by David Sterba <sterd9am@ss1000.ms.mff.cuni.cz> */
1060 #if defined(__i386) || defined(__x86_64)
1061 unsigned long long int rdtsc(void)
1063 unsigned long long int x;
1065 __asm__ volatile(".byte 0x0f, 0x31":"=A" (x));
1070 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1071 void get_freq_dynamic(char *p_client_buffer, size_t client_buffer_size,
1072 const char *p_format, int divisor)
1074 #if defined(__i386) || defined(__x86_64)
1076 struct timeval tvstart, tvstop;
1077 unsigned long long cycles[2]; /* gotta be 64 bit */
1078 unsigned int microseconds; /* total time taken */
1080 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1085 memset(&tz, 0, sizeof(tz));
1087 /* get this function in cached memory */
1088 gettimeofday(&tvstart, &tz);
1089 cycles[0] = rdtsc();
1090 gettimeofday(&tvstart, &tz);
1092 /* we don't trust that this is any specific length of time */
1094 cycles[1] = rdtsc();
1095 gettimeofday(&tvstop, &tz);
1096 microseconds = ((tvstop.tv_sec - tvstart.tv_sec) * 1000000) +
1097 (tvstop.tv_usec - tvstart.tv_usec);
1099 snprintf(p_client_buffer, client_buffer_size, p_format,
1100 (float) ((cycles[1] - cycles[0]) / microseconds) / divisor);
1103 /* FIXME: hardwired: get freq for first cpu!
1104 * this whole function needs to be rethought and redone for
1105 * multi-cpu/multi-core/multi-threaded environments and
1106 * arbitrary combinations thereof */
1107 get_freq(p_client_buffer, client_buffer_size, p_format, divisor, 1);
1112 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
1113 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
1115 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
1116 char get_freq(char *p_client_buffer, size_t client_buffer_size,
1117 const char *p_format, int divisor, unsigned int cpu)
1125 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1131 char current_freq_file[128];
1133 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu - 1,
1135 f = fopen(current_freq_file, "r");
1137 /* if there's a cpufreq /sys node, read the current frequency from
1138 * this node and divide by 1000 to get Mhz. */
1139 if (fgets(s, sizeof(s), f)) {
1140 s[strlen(s) - 1] = '\0';
1141 freq = strtod(s, NULL);
1144 snprintf(p_client_buffer, client_buffer_size, p_format,
1145 (freq / 1000) / divisor);
1150 // open the CPU information file
1151 f = open_file("/proc/cpuinfo", &rep);
1153 perror("Conky: Failed to access '/proc/cpuinfo' at get_freq()");
1158 while (fgets(s, sizeof(s), f) != NULL) {
1160 #if defined(__i386) || defined(__x86_64)
1161 // and search for the cpu mhz
1162 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {
1164 #if defined(__alpha)
1165 // different on alpha
1166 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {
1168 // this is different on ppc for some reason
1169 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {
1170 #endif // defined(__alpha)
1171 #endif // defined(__i386) || defined(__x86_64)
1173 // copy just the number
1174 strcpy(frequency, strchr(s, ':') + 2);
1175 #if defined(__alpha)
1177 frequency[strlen(frequency) - 6] = '\0';
1178 // kernel reports in Hz
1179 freq = strtod(frequency, NULL) / 1000000;
1182 frequency[strlen(frequency) - 1] = '\0';
1183 freq = strtod(frequency, NULL);
1187 if (strncmp(s, "processor", 9) == 0) {
1194 snprintf(p_client_buffer, client_buffer_size, p_format,
1195 (float) freq / divisor);
1199 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
1201 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks something
1211 * Peter Tarjan (ptarjan@citromail.hu) */
1213 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
1214 char get_voltage(char *p_client_buffer, size_t client_buffer_size,
1215 const char *p_format, int divisor, unsigned int cpu)
1221 char current_freq_file[128];
1224 /* build the voltage file name */
1226 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1229 if (!p_client_buffer || client_buffer_size <= 0 || !p_format
1234 /* read the current cpu frequency from the /sys node */
1235 f = fopen(current_freq_file, "r");
1237 if (fgets(s, sizeof(s), f)) {
1238 s[strlen(s) - 1] = '\0';
1239 freq = strtod(s, NULL);
1243 fprintf(stderr, "Conky: Failed to access '%s' at ", current_freq_file);
1244 perror("get_voltage()");
1251 snprintf(current_freq_file, 127, "%s/cpu%d/%s", CPUFREQ_PREFIX, cpu,
1254 /* use the current cpu frequency to find the corresponding voltage */
1255 f = fopen(current_freq_file, "r");
1261 if (fgets(line, 255, f) == NULL) {
1264 sscanf(line, "%d %d", &freq_comp, &voltage);
1265 if (freq_comp == freq) {
1271 fprintf(stderr, "Conky: Failed to access '%s' at ", current_freq_file);
1272 perror("get_voltage()");
1278 snprintf(p_client_buffer, client_buffer_size, p_format,
1279 (float) voltage / divisor);
1283 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1285 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
1292 if (!p_client_buffer || client_buffer_size <= 0) {
1296 /* yeah, slow... :/ */
1297 if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep)) {
1298 snprintf(p_client_buffer, client_buffer_size, "no fans?");
1302 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf);
1304 fp = open_file(buf2, &rep);
1306 snprintf(p_client_buffer, client_buffer_size,
1307 "can't open fan's state file");
1310 memset(buf, 0, sizeof(buf));
1311 fscanf(fp, "%*s %99s", buf);
1314 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1317 #define SYSFS_AC_ADAPTER_DIR "/sys/class/power_supply/AC"
1318 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1319 /* Linux 2.6.25 onwards ac adapter info is in
1320 /sys/class/power_supply/AC/
1321 On my system I get the following.
1322 /sys/class/power_supply/AC/uevent:
1323 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A08:00/device:01/PNP0C09:00/ACPI0003:00
1326 POWER_SUPPLY_NAME=AC
1327 POWER_SUPPLY_TYPE=Mains
1328 POWER_SUPPLY_ONLINE=1
1331 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
1339 if (!p_client_buffer || client_buffer_size <= 0) {
1343 snprintf(buf2, sizeof(buf2), "%s/uevent", SYSFS_AC_ADAPTER_DIR);
1344 fp = open_file(buf2, &rep);
1346 /* sysfs processing */
1348 if (fgets(buf, sizeof(buf), fp) == NULL)
1351 if (strncmp(buf, "POWER_SUPPLY_ONLINE=", 20) == 0) {
1353 sscanf(buf, "POWER_SUPPLY_ONLINE=%d", &online);
1354 snprintf(p_client_buffer, client_buffer_size,
1355 "%s-line", (online ? "on" : "off"));
1361 /* yeah, slow... :/ */
1362 if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep)) {
1363 snprintf(p_client_buffer, client_buffer_size, "no ac_adapters?");
1367 snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
1369 fp = open_file(buf2, &rep);
1371 snprintf(p_client_buffer, client_buffer_size,
1372 "No ac adapter found.... where is it?");
1375 memset(buf, 0, sizeof(buf));
1376 fscanf(fp, "%*s %99s", buf);
1379 snprintf(p_client_buffer, client_buffer_size, "%s", buf);
1384 /proc/acpi/thermal_zone/THRM/cooling_mode
1385 cooling mode: active
1386 /proc/acpi/thermal_zone/THRM/polling_frequency
1388 /proc/acpi/thermal_zone/THRM/state
1390 /proc/acpi/thermal_zone/THRM/temperature
1392 /proc/acpi/thermal_zone/THRM/trip_points
1394 passive: 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1397 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1398 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1400 int open_acpi_temperature(const char *name)
1406 if (name == NULL || strcmp(name, "*") == 0) {
1409 if (!get_first_file_in_a_directory(ACPI_THERMAL_DIR, buf, &rep)) {
1415 snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1417 fd = open(path, O_RDONLY);
1419 ERR("can't open '%s': %s", path, strerror(errno));
1425 static double last_acpi_temp;
1426 static double last_acpi_temp_time;
1428 double get_acpi_temperature(int fd)
1434 /* don't update acpi temperature too often */
1435 if (current_update_time - last_acpi_temp_time < 11.32) {
1436 return last_acpi_temp;
1438 last_acpi_temp_time = current_update_time;
1440 /* seek to beginning */
1441 lseek(fd, 0, SEEK_SET);
1448 n = read(fd, buf, 255);
1450 ERR("can't read fd %d: %s", fd, strerror(errno));
1453 sscanf(buf, "temperature: %lf", &last_acpi_temp);
1457 return last_acpi_temp;
1461 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info
1463 design capacity: 4400 mAh
1464 last full capacity: 4064 mAh
1465 battery technology: rechargeable
1466 design voltage: 14800 mV
1467 design capacity warning: 300 mAh
1468 design capacity low: 200 mAh
1469 capacity granularity 1: 32 mAh
1470 capacity granularity 2: 32 mAh
1472 serial number: 16922
1478 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1481 charging state: unknown
1483 remaining capacity: 4064 mAh
1484 present voltage: 16608 mV
1488 2213<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1489 2213<@jupet�kellari��> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1490 2213<@jupet�kellari��> (-1 ollee ei akkua kiinni, koska akku on p�yd�ll�)
1491 2214<@jupet�kellari��> jupet@lagi-unstable:~$ cat /proc/apm
1492 2214<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1494 2238<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1495 2239<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1497 2240<@jupet�kellari��> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori p��ll�
1498 2241<@jupet�kellari��> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori p��ll� mutta ilman verkkovirtaa
1501 /* Kapil Hari Paranjape <kapil@imsc.res.in>
1502 Linux 2.6.24 onwards battery info is in
1503 /sys/class/power_supply/BAT0/
1504 On my system I get the following.
1505 /sys/class/power_supply/BAT0/uevent:
1506 PHYSDEVPATH=/devices/LNXSYSTM:00/device:00/PNP0A03:00/device:01/PNP0C09:00/PNP0C0A:00
1508 PHYSDEVDRIVER=battery
1509 POWER_SUPPLY_NAME=BAT0
1510 POWER_SUPPLY_TYPE=Battery
1511 POWER_SUPPLY_STATUS=Discharging
1512 POWER_SUPPLY_PRESENT=1
1513 POWER_SUPPLY_TECHNOLOGY=Li-ion
1514 POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
1515 POWER_SUPPLY_VOLTAGE_NOW=10780000
1516 POWER_SUPPLY_CURRENT_NOW=13970000
1517 POWER_SUPPLY_ENERGY_FULL_DESIGN=47510000
1518 POWER_SUPPLY_ENERGY_FULL=27370000
1519 POWER_SUPPLY_ENERGY_NOW=11810000
1520 POWER_SUPPLY_MODEL_NAME=IBM-92P1060
1521 POWER_SUPPLY_MANUFACTURER=Panasonic
1522 On some systems POWER_SUPPLY_ENERGY_* is replaced by POWER_SUPPLY_CHARGE_*
1525 #define SYSFS_BATTERY_BASE_PATH "/sys/class/power_supply"
1526 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1527 #define APM_PATH "/proc/apm"
1528 #define MAX_BATTERY_COUNT 4
1530 static FILE *sysfs_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1531 static FILE *acpi_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1532 static FILE *apm_bat_fp[MAX_BATTERY_COUNT] = { NULL, NULL, NULL, NULL };
1534 static int batteries_initialized = 0;
1535 static char batteries[MAX_BATTERY_COUNT][32];
1537 static int acpi_last_full[MAX_BATTERY_COUNT];
1538 static int acpi_design_capacity[MAX_BATTERY_COUNT];
1540 /* e.g. "charging 75%" */
1541 static char last_battery_str[MAX_BATTERY_COUNT][64];
1543 static char last_battery_time_str[MAX_BATTERY_COUNT][64];
1545 static double last_battery_time[MAX_BATTERY_COUNT];
1547 static int last_battery_perct[MAX_BATTERY_COUNT];
1548 static double last_battery_perct_time[MAX_BATTERY_COUNT];
1550 void init_batteries(void)
1554 if (batteries_initialized) {
1557 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1558 batteries[idx][0] = '\0';
1560 batteries_initialized = 1;
1563 int get_battery_idx(const char *bat)
1567 for (idx = 0; idx < MAX_BATTERY_COUNT; idx++) {
1568 if (!strlen(batteries[idx]) || !strcmp(batteries[idx], bat)) {
1573 /* if not found, enter a new entry */
1574 if (!strlen(batteries[idx])) {
1575 snprintf(batteries[idx], 31, "%s", bat);
1581 void set_return_value(char *buffer, unsigned int n, int item, int idx);
1583 void get_battery_stuff(char *buffer, unsigned int n, const char *bat, int item)
1585 static int idx, rep = 0, rep2 = 0;
1586 char acpi_path[128];
1587 char sysfs_path[128];
1589 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1590 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1594 idx = get_battery_idx(bat);
1596 /* don't update battery too often */
1597 if (current_update_time - last_battery_time[idx] < 29.5) {
1598 set_return_value(buffer, n, item, idx);
1602 last_battery_time[idx] = current_update_time;
1604 memset(last_battery_str[idx], 0, sizeof(last_battery_str[idx]));
1605 memset(last_battery_time_str[idx], 0, sizeof(last_battery_time_str[idx]));
1607 /* first try SYSFS if that fails try ACPI */
1609 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1610 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1614 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1615 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1618 if (sysfs_bat_fp[idx] != NULL) {
1620 int present_rate = -1;
1621 int remaining_capacity = -1;
1622 char charging_state[64];
1625 strcpy(charging_state, "unknown");
1627 while (!feof(sysfs_bat_fp[idx])) {
1629 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1632 /* let's just hope units are ok */
1633 if (strncmp (buf, "POWER_SUPPLY_PRESENT=1", 22) == 0)
1634 strcpy(present, "yes");
1635 else if (strncmp (buf, "POWER_SUPPLY_PRESENT=0", 22) == 0)
1636 strcpy(present, "no");
1637 else if (strncmp (buf, "POWER_SUPPLY_STATUS=", 20) == 0)
1638 sscanf(buf, "POWER_SUPPLY_STATUS=%63s", charging_state);
1639 /* present_rate is not the same as the
1640 current flowing now but it is the same value
1641 which was used in the past. so we continue
1643 else if (strncmp(buf, "POWER_SUPPLY_CURRENT_NOW=", 25) == 0)
1644 sscanf(buf, "POWER_SUPPLY_CURRENT_NOW=%d", &present_rate);
1645 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0)
1646 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1647 else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=", 25) == 0)
1648 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_last_full[idx]);
1649 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0)
1650 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1651 else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=", 25) == 0)
1652 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_last_full[idx]);
1655 fclose(sysfs_bat_fp[idx]);
1656 sysfs_bat_fp[idx] = NULL;
1658 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1659 if (remaining_capacity > acpi_last_full[idx])
1660 acpi_last_full[idx] = remaining_capacity; /* normalize to 100% */
1663 if (strcmp(present, "No") == 0) {
1664 strncpy(last_battery_str[idx], "not present", 64);
1667 else if (strcmp(charging_state, "Charging") == 0) {
1668 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1669 /* e.g. charging 75% */
1670 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %i%%",
1671 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1673 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1674 (long) (((float)(acpi_last_full[idx] - remaining_capacity) / present_rate) * 3600));
1675 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1676 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "charging %d%%",
1677 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1678 snprintf(last_battery_time_str[idx],
1679 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1681 strncpy(last_battery_str[idx], "charging", sizeof(last_battery_str[idx])-1);
1682 snprintf(last_battery_time_str[idx],
1683 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1687 else if (strncmp(charging_state, "Discharging", 64) == 0) {
1688 if (present_rate > 0) {
1689 /* e.g. discharging 35% */
1690 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "discharging %i%%",
1691 (int) (((float) remaining_capacity / acpi_last_full[idx]) * 100 ));
1693 format_seconds(last_battery_time_str[idx], sizeof(last_battery_time_str[idx])-1,
1694 (long) (((float) remaining_capacity / present_rate) * 3600));
1695 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1696 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1, "full");
1697 snprintf(last_battery_time_str[idx],
1698 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1700 snprintf(last_battery_str[idx], sizeof(last_battery_str[idx])-1,
1702 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1703 snprintf(last_battery_time_str[idx],
1704 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1708 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1709 else if (strncmp(charging_state, "Charged", 64) == 0) {
1710 /* Below happens with the second battery on my X40,
1711 * when the second one is empty and the first one
1713 if (remaining_capacity == 0)
1714 strcpy(last_battery_str[idx], "empty");
1716 strcpy(last_battery_str[idx], "charged");
1718 /* unknown, probably full / AC */
1720 if (acpi_last_full[idx] != 0
1721 && remaining_capacity != acpi_last_full[idx])
1722 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1723 (int) (((float)remaining_capacity / acpi_last_full[idx]) * 100));
1725 strncpy(last_battery_str[idx], "AC", 64);
1727 } else if (acpi_bat_fp[idx] != NULL) {
1729 int present_rate = -1;
1730 int remaining_capacity = -1;
1731 char charging_state[64];
1734 /* read last full capacity if it's zero */
1735 if (acpi_last_full[idx] == 0) {
1736 static int rep3 = 0;
1740 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1741 fp = open_file(path, &rep3);
1746 if (fgets(b, 256, fp) == NULL) {
1749 if (sscanf(b, "last full capacity: %d",
1750 &acpi_last_full[idx]) != 0) {
1759 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1761 strcpy(charging_state, "unknown");
1763 while (!feof(acpi_bat_fp[idx])) {
1766 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1770 /* let's just hope units are ok */
1771 if (strncmp(buf, "present:", 8) == 0) {
1772 sscanf(buf, "present: %4s", present);
1773 } else if (strncmp(buf, "charging state:", 15) == 0) {
1774 sscanf(buf, "charging state: %63s", charging_state);
1775 } else if (strncmp(buf, "present rate:", 13) == 0) {
1776 sscanf(buf, "present rate: %d", &present_rate);
1777 } else if (strncmp(buf, "remaining capacity:", 19) == 0) {
1778 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1781 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1782 if (remaining_capacity > acpi_last_full[idx]) {
1783 /* normalize to 100% */
1784 acpi_last_full[idx] = remaining_capacity;
1788 if (strcmp(present, "no") == 0) {
1789 strncpy(last_battery_str[idx], "not present", 64);
1791 } else if (strcmp(charging_state, "charging") == 0) {
1792 if (acpi_last_full[idx] != 0 && present_rate > 0) {
1793 /* e.g. charging 75% */
1794 snprintf(last_battery_str[idx],
1795 sizeof(last_battery_str[idx]) - 1, "charging %i%%",
1796 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1798 format_seconds(last_battery_time_str[idx],
1799 sizeof(last_battery_time_str[idx]) - 1,
1800 (long) (((acpi_last_full[idx] - remaining_capacity) *
1801 3600) / present_rate));
1802 } else if (acpi_last_full[idx] != 0 && present_rate <= 0) {
1803 snprintf(last_battery_str[idx],
1804 sizeof(last_battery_str[idx]) - 1, "charging %d%%",
1805 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1806 snprintf(last_battery_time_str[idx],
1807 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1809 strncpy(last_battery_str[idx], "charging",
1810 sizeof(last_battery_str[idx]) - 1);
1811 snprintf(last_battery_time_str[idx],
1812 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1815 } else if (strncmp(charging_state, "discharging", 64) == 0) {
1816 if (present_rate > 0) {
1817 /* e.g. discharging 35% */
1818 snprintf(last_battery_str[idx],
1819 sizeof(last_battery_str[idx]) - 1, "discharging %i%%",
1820 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1822 format_seconds(last_battery_time_str[idx],
1823 sizeof(last_battery_time_str[idx]) - 1,
1824 (long) ((remaining_capacity * 3600) / present_rate));
1825 } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1826 snprintf(last_battery_str[idx],
1827 sizeof(last_battery_str[idx]) - 1, "full");
1828 snprintf(last_battery_time_str[idx],
1829 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1831 snprintf(last_battery_str[idx],
1832 sizeof(last_battery_str[idx]) - 1, "discharging %d%%",
1833 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1834 snprintf(last_battery_time_str[idx],
1835 sizeof(last_battery_time_str[idx]) - 1, "unknown");
1838 } else if (strncmp(charging_state, "charged", 64) == 0) {
1839 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1840 /* Below happens with the second battery on my X40,
1841 * when the second one is empty and the first one being charged. */
1842 if (remaining_capacity == 0) {
1843 strcpy(last_battery_str[idx], "empty");
1845 strcpy(last_battery_str[idx], "charged");
1847 /* unknown, probably full / AC */
1849 if (acpi_last_full[idx] != 0
1850 && remaining_capacity != acpi_last_full[idx]) {
1851 snprintf(last_battery_str[idx], 64, "unknown %d%%",
1852 (int) ((remaining_capacity * 100) / acpi_last_full[idx]));
1854 strncpy(last_battery_str[idx], "AC", 64);
1859 if (apm_bat_fp[idx] == NULL) {
1860 apm_bat_fp[idx] = open_file(APM_PATH, &rep2);
1863 if (apm_bat_fp[idx] != NULL) {
1864 unsigned int ac, status, flag;
1867 fscanf(apm_bat_fp[idx], "%*s %*s %*x %x %x %x %d%%",
1868 &ac, &status, &flag, &life);
1871 /* could check now that there is ac */
1872 snprintf(last_battery_str[idx], 64, "AC");
1874 /* could check that status == 3 here? */
1875 } else if (ac && life != 100) {
1876 snprintf(last_battery_str[idx], 64, "charging %d%%", life);
1878 snprintf(last_battery_str[idx], 64, "%d%%", life);
1881 /* it seemed to buffer it so file must be closed (or could use
1882 * syscalls directly but I don't feel like coding it now) */
1883 fclose(apm_bat_fp[idx]);
1884 apm_bat_fp[idx] = NULL;
1887 set_return_value(buffer, n, item, idx);
1890 void set_return_value(char *buffer, unsigned int n, int item, int idx)
1893 case BATTERY_STATUS:
1894 snprintf(buffer, n, "%s", last_battery_str[idx]);
1897 snprintf(buffer, n, "%s", last_battery_time_str[idx]);
1904 int get_battery_perct(const char *bat)
1908 char acpi_path[128];
1909 char sysfs_path[128];
1910 int remaining_capacity = -1;
1912 snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1913 snprintf(sysfs_path, 127, SYSFS_BATTERY_BASE_PATH "/%s/uevent", bat);
1917 idx = get_battery_idx(bat);
1919 /* don't update battery too often */
1920 if (current_update_time - last_battery_perct_time[idx] < 30) {
1921 return last_battery_perct[idx];
1923 last_battery_perct_time[idx] = current_update_time;
1925 /* Only check for SYSFS or ACPI */
1927 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1928 sysfs_bat_fp[idx] = open_file(sysfs_path, &rep);
1932 if (sysfs_bat_fp[idx] == NULL && acpi_bat_fp[idx] == NULL && apm_bat_fp[idx] == NULL) {
1933 acpi_bat_fp[idx] = open_file(acpi_path, &rep);
1936 if (sysfs_bat_fp[idx] != NULL) {
1938 while (!feof(sysfs_bat_fp[idx])) {
1940 if (fgets(buf, 256, sysfs_bat_fp[idx]) == NULL)
1943 if (strncmp(buf, "POWER_SUPPLY_CHARGE_NOW=", 24) == 0) {
1944 sscanf(buf, "POWER_SUPPLY_CHARGE_NOW=%d", &remaining_capacity);
1945 } else if (strncmp(buf, "POWER_SUPPLY_CHARGE_FULL=",25) == 0) {
1946 sscanf(buf, "POWER_SUPPLY_CHARGE_FULL=%d", &acpi_design_capacity[idx]);
1947 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_NOW=", 24) == 0) {
1948 sscanf(buf, "POWER_SUPPLY_ENERGY_NOW=%d", &remaining_capacity);
1949 } else if (strncmp(buf, "POWER_SUPPLY_ENERGY_FULL=",25) == 0) {
1950 sscanf(buf, "POWER_SUPPLY_ENERGY_FULL=%d", &acpi_design_capacity[idx]);
1954 fclose(sysfs_bat_fp[idx]);
1955 sysfs_bat_fp[idx] = NULL;
1957 } else if (acpi_bat_fp[idx] != NULL) {
1959 /* read last full capacity if it's zero */
1960 if (acpi_design_capacity[idx] == 0) {
1965 snprintf(path, 127, ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1966 fp = open_file(path, &rep2);
1971 if (fgets(b, 256, fp) == NULL) {
1974 if (sscanf(b, "last full capacity: %d",
1975 &acpi_design_capacity[idx]) != 0) {
1983 fseek(acpi_bat_fp[idx], 0, SEEK_SET);
1985 while (!feof(acpi_bat_fp[idx])) {
1988 if (fgets(buf, 256, acpi_bat_fp[idx]) == NULL) {
1992 if (buf[0] == 'r') {
1993 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1997 if (remaining_capacity < 0) {
2000 /* compute the battery percentage */
2001 last_battery_perct[idx] =
2002 (int) (((float) remaining_capacity / acpi_design_capacity[idx]) * 100);
2003 return last_battery_perct[idx];
2006 int get_battery_perct_bar(const char *bar)
2010 get_battery_perct(bar);
2011 idx = get_battery_idx(bar);
2012 return (int) (last_battery_perct[idx] * 2.56 - 1);
2015 /* On Apple powerbook and ibook:
2016 $ cat /proc/pmu/battery_0
2023 $ cat /proc/pmu/info
2024 PMU driver version : 2
2025 PMU firmware version : 0c
2030 /* defines as in <linux/pmu.h> */
2031 #define PMU_BATT_PRESENT 0x00000001
2032 #define PMU_BATT_CHARGING 0x00000002
2034 static FILE *pmu_battery_fp;
2035 static FILE *pmu_info_fp;
2036 static char pb_battery_info[3][32];
2037 static double pb_battery_info_update;
2039 #define PMU_PATH "/proc/pmu"
2040 void get_powerbook_batt_info(char *buffer, size_t n, int i)
2043 const char *batt_path = PMU_PATH "/battery_0";
2044 const char *info_path = PMU_PATH "/info";
2046 int charge, max_charge, ac = -1;
2049 /* don't update battery too often */
2050 if (current_update_time - pb_battery_info_update < 29.5) {
2051 snprintf(buffer, n, "%s", pb_battery_info[i]);
2054 pb_battery_info_update = current_update_time;
2056 if (pmu_battery_fp == NULL) {
2057 pmu_battery_fp = open_file(batt_path, &rep);
2060 if (pmu_battery_fp != NULL) {
2061 rewind(pmu_battery_fp);
2062 while (!feof(pmu_battery_fp)) {
2065 if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL) {
2069 if (buf[0] == 'f') {
2070 sscanf(buf, "flags : %8x", &flags);
2071 } else if (buf[0] == 'c' && buf[1] == 'h') {
2072 sscanf(buf, "charge : %d", &charge);
2073 } else if (buf[0] == 'm') {
2074 sscanf(buf, "max_charge : %d", &max_charge);
2075 } else if (buf[0] == 't') {
2076 sscanf(buf, "time rem. : %ld", &timeval);
2080 if (pmu_info_fp == NULL) {
2081 pmu_info_fp = open_file(info_path, &rep);
2084 if (pmu_info_fp != NULL) {
2085 rewind(pmu_info_fp);
2086 while (!feof(pmu_info_fp)) {
2089 if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL) {
2092 if (buf[0] == 'A') {
2093 sscanf(buf, "AC Power : %d", &ac);
2097 /* update status string */
2098 if ((ac && !(flags & PMU_BATT_PRESENT))) {
2099 strcpy(pb_battery_info[PB_BATT_STATUS], "AC");
2100 } else if (ac && (flags & PMU_BATT_PRESENT)
2101 && !(flags & PMU_BATT_CHARGING)) {
2102 strcpy(pb_battery_info[PB_BATT_STATUS], "charged");
2103 } else if ((flags & PMU_BATT_PRESENT) && (flags & PMU_BATT_CHARGING)) {
2104 strcpy(pb_battery_info[PB_BATT_STATUS], "charging");
2106 strcpy(pb_battery_info[PB_BATT_STATUS], "discharging");
2109 /* update percentage string */
2111 pb_battery_info[PB_BATT_PERCENT][0] = 0;
2113 snprintf(pb_battery_info[PB_BATT_PERCENT],
2114 sizeof(pb_battery_info[PB_BATT_PERCENT]), "%d%%",
2115 (charge * 100) / max_charge);
2118 /* update time string */
2119 if (timeval == 0) { /* fully charged or battery not present */
2120 pb_battery_info[PB_BATT_TIME][0] = 0;
2121 } else if (timeval < 60 * 60) { /* don't show secs */
2122 format_seconds_short(pb_battery_info[PB_BATT_TIME],
2123 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2125 format_seconds(pb_battery_info[PB_BATT_TIME],
2126 sizeof(pb_battery_info[PB_BATT_TIME]), timeval);
2129 snprintf(buffer, n, "%s", pb_battery_info[i]);
2132 void update_top(void)
2134 show_nice_processes = 1;
2135 process_find_top(info.cpu, info.memu);
2136 info.first_process = get_first_process();
2139 /* Here come the IBM ACPI-specific things. For reference, see
2140 * http://ibm-acpi.sourceforge.net/README
2141 * If IBM ACPI is installed, /proc/acpi/ibm contains the following files:
2157 * The content of these files is described in detail in the aforementioned
2158 * README - some of them also in the following functions accessing them.
2159 * Peter Tarjan (ptarjan@citromail.hu) */
2161 #define IBM_ACPI_DIR "/proc/acpi/ibm"
2163 /* get fan speed on IBM/Lenovo laptops running the ibm acpi.
2164 * /proc/acpi/ibm/fan looks like this (3 lines):
2167 commands: enable, disable
2168 * Peter Tarjan (ptarjan@citromail.hu) */
2170 void get_ibm_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
2173 unsigned int speed = 0;
2176 if (!p_client_buffer || client_buffer_size <= 0) {
2180 snprintf(fan, 127, "%s/fan", IBM_ACPI_DIR);
2182 fp = fopen(fan, "r");
2187 if (fgets(line, 255, fp) == NULL) {
2190 if (sscanf(line, "speed: %u", &speed)) {
2195 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2196 "ibm* from your Conky config file.", fan, strerror(errno));
2200 snprintf(p_client_buffer, client_buffer_size, "%d", speed);
2203 /* get the measured temperatures from the temperature sensors
2204 * on IBM/Lenovo laptops running the ibm acpi.
2205 * There are 8 values in /proc/acpi/ibm/thermal, and according to
2206 * http://ibm-acpi.sourceforge.net/README
2207 * these mean the following (at least on an IBM R51...)
2208 * 0: CPU (also on the T series laptops)
2209 * 1: Mini PCI Module (?)
2211 * 3: GPU (also on the T series laptops)
2216 * I'm not too sure about those with the question mark, but the values I'm
2217 * reading from *my* thermal file (on a T42p) look realistic for the
2218 * hdd and the battery.
2219 * #5 and #7 are always -128.
2220 * /proc/acpi/ibm/thermal looks like this (1 line):
2221 temperatures: 41 43 31 46 33 -128 29 -128
2222 * Peter Tarjan (ptarjan@citromail.hu) */
2224 static double last_ibm_acpi_temp_time;
2225 void get_ibm_acpi_temps(void)
2231 /* don't update too often */
2232 if (current_update_time - last_ibm_acpi_temp_time < 10.00) {
2235 last_ibm_acpi_temp_time = current_update_time;
2237 /* if (!p_client_buffer || client_buffer_size <= 0) {
2241 snprintf(thermal, 127, "%s/thermal", IBM_ACPI_DIR);
2242 fp = fopen(thermal, "r");
2248 if (fgets(line, 255, fp) == NULL) {
2251 if (sscanf(line, "temperatures: %d %d %d %d %d %d %d %d",
2252 &ibm_acpi.temps[0], &ibm_acpi.temps[1], &ibm_acpi.temps[2],
2253 &ibm_acpi.temps[3], &ibm_acpi.temps[4], &ibm_acpi.temps[5],
2254 &ibm_acpi.temps[6], &ibm_acpi.temps[7])) {
2259 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2260 "ibm* from your Conky config file.", thermal, strerror(errno));
2266 /* get volume (0-14) on IBM/Lenovo laptops running the ibm acpi.
2267 * "Volume" here is none of the mixer volumes, but a "master of masters"
2268 * volume adjusted by the IBM volume keys.
2269 * /proc/acpi/ibm/fan looks like this (4 lines):
2272 commands: up, down, mute
2273 commands: level <level> (<level> is 0-15)
2274 * Peter Tarjan (ptarjan@citromail.hu) */
2276 void get_ibm_acpi_volume(char *p_client_buffer, size_t client_buffer_size)
2280 unsigned int vol = -1;
2283 if (!p_client_buffer || client_buffer_size <= 0) {
2287 snprintf(volume, 127, "%s/volume", IBM_ACPI_DIR);
2289 fp = fopen(volume, "r");
2293 unsigned int read_vol = -1;
2295 if (fgets(line, 255, fp) == NULL) {
2298 if (sscanf(line, "level: %u", &read_vol)) {
2302 if (sscanf(line, "mute: %s", mute)) {
2307 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2308 "ibm* from your Conky config file.", volume, strerror(errno));
2313 if (strcmp(mute, "on") == 0) {
2314 snprintf(p_client_buffer, client_buffer_size, "%s", "mute");
2317 snprintf(p_client_buffer, client_buffer_size, "%d", vol);
2322 /* static FILE *fp = NULL; */
2324 /* get LCD brightness on IBM/Lenovo laptops running the ibm acpi.
2325 * /proc/acpi/ibm/brightness looks like this (3 lines):
2328 commands: level <level> (<level> is 0-7)
2329 * Peter Tarjan (ptarjan@citromail.hu) */
2331 void get_ibm_acpi_brightness(char *p_client_buffer, size_t client_buffer_size)
2334 unsigned int brightness = 0;
2337 if (!p_client_buffer || client_buffer_size <= 0) {
2341 snprintf(filename, 127, "%s/brightness", IBM_ACPI_DIR);
2343 fp = fopen(filename, "r");
2348 if (fgets(line, 255, fp) == NULL) {
2351 if (sscanf(line, "level: %u", &brightness)) {
2356 CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove "
2357 "ibm* from your Conky config file.", filename, strerror(errno));
2362 snprintf(p_client_buffer, client_buffer_size, "%d", brightness);
2365 void update_entropy(void)
2368 const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
2369 const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
2372 info.entropy.entropy_avail = 0;
2373 info.entropy.poolsize = 0;
2375 if ((fp1 = open_file(entropy_avail, &rep)) == NULL) {
2379 if ((fp2 = open_file(entropy_poolsize, &rep)) == NULL) {
2384 fscanf(fp1, "%u", &info.entropy.entropy_avail);
2385 fscanf(fp2, "%u", &info.entropy.poolsize);
2390 info.mask |= (1 << INFO_ENTROPY);
2393 const char *get_disk_protect_queue(const char *disk)
2399 snprintf(path, 127, "/sys/block/%s/queue/protect", disk);
2400 if ((fp = fopen(path, "r")) == NULL)
2402 if (fscanf(fp, "%d\n", &state) != 1) {
2407 return state ? "frozen" : "free ";