Experimental RSS code.
[monky] / src / linux.c
index 29488bc..c163ae9 100644 (file)
@@ -16,6 +16,9 @@
 #include <sys/types.h>
 #include <sys/sysinfo.h>
 #include <sys/stat.h>
+#ifndef HAVE_CLOCK_GETTIME
+#include <sys/time.h>
+#endif
 #include <fcntl.h>
 #include <unistd.h>
 // #include <assert.h>
 #define SHORTSTAT_TEMPL "%*s %llu %llu %llu"
 #define LONGSTAT_TEMPL "%*s %llu %llu %llu "
 
-
-static struct sysinfo s_info;
-
 static int show_nice_processes;
 
-void prepare_update()
-{
-}
+/* this flags tells the linux routines to use the /proc system
+ * where possible, even if other api's are available, e.g. sysinfo() 
+ * or getloadavg(). the reason for this is to allow for /proc-based 
+ * distributed monitoring. using a flag in this manner creates less
+ * confusing code.
+ */
+static int prefer_proc = 0;
 
-static void update_sysinfo()
+void prepare_update()
 {
-       sysinfo(&s_info);
-
-       info.uptime = (double) s_info.uptime;
-
-       /* there was some problem with these */
-#if 0
-//      info.loadavg[0] = s_info.loads[0] / 100000.0f;
-       info.loadavg[1] = s_info.loads[1] / 100000.0f;
-       info.loadavg[2] = s_info.loads[2] / 100000.0f;
-       gkrelltop_process_find_top_three info.mask |= 1 << INFO_LOADAVG;
-#endif
-
-       info.procs = s_info.procs;
-
-       /* these aren't nice, no cache and should check kernel version for mem_unit */
-#if 0
-       info.memmax = s_info.totalram;
-       info.mem = s_info.totalram - s_info.freeram;
-       info.swapmax = s_info.totalswap;
-       info.swap = s_info.totalswap - s_info.swap;
-       info.mask |= 1 << INFO_MEM;
-#endif
-
-       info.mask |= (1 << INFO_UPTIME) | (1 << INFO_PROCS);
 }
 
 void update_uptime()
 {
-       /* prefers sysinfo() for uptime, I don't really know which one is better
-        * (=faster?) */
-#ifdef USE_PROC_UPTIME
-       static int rep;
-       FILE *fp = open_file("/proc/uptime", &rep);
-       if (!fp)
-               return 0;
-       fscanf(fp, "%lf", &info.uptime);
-       fclose(fp);
-
-       info.mask |= (1 << INFO_UPTIME);
-#else
-       update_sysinfo();
+#ifdef HAVE_SYSINFO
+  if (!prefer_proc)
+  {
+    struct sysinfo s_info;
+    sysinfo(&s_info);
+    info.uptime = (double) s_info.uptime;
+  }
+  else
 #endif
+  {
+         static int rep = 0;
+         FILE *fp;
+  
+    if (!(fp = open_file("/proc/uptime", &rep)))
+    {
+      info.uptime=0.0;
+      return;
+    }
+         fscanf(fp, "%lf", &info.uptime);
+         fclose(fp);
+  }
+  info.mask |= (1 << INFO_UPTIME);
 }
 
 /* these things are also in sysinfo except Buffers:, that's why I'm reading
 * them from proc */
 
-static FILE *meminfo_fp;
-
 void update_meminfo()
 {
-       static int rep;
+  FILE *meminfo_fp;
+       static int rep = 0;
        /*  unsigned int a; */
        char buf[256];
 
        info.mem = info.memmax = info.swap = info.swapmax = info.bufmem =
            info.buffers = info.cached = 0;
 
-       if (meminfo_fp == NULL)
-               meminfo_fp = open_file("/proc/meminfo", &rep);
-       else
-               fseek(meminfo_fp, 0, SEEK_SET);
-       if (meminfo_fp == NULL)
-               return;
+  if (!(meminfo_fp = open_file("/proc/meminfo", &rep)))
+      return;
 
        while (!feof(meminfo_fp)) {
                if (fgets(buf, 255, meminfo_fp) == NULL)
                        break;
 
                if (strncmp(buf, "MemTotal:", 9) == 0) {
-                       sscanf(buf, "%*s %lu", &info.memmax);
+                       sscanf(buf, "%*s %Lu", &info.memmax);
                } else if (strncmp(buf, "MemFree:", 8) == 0) {
-                       sscanf(buf, "%*s %lu", &info.mem);
+                       sscanf(buf, "%*s %Lu", &info.mem);
                } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
-                       sscanf(buf, "%*s %lu", &info.swapmax);
+                       sscanf(buf, "%*s %Lu", &info.swapmax);
                } else if (strncmp(buf, "SwapFree:", 9) == 0) {
-                       sscanf(buf, "%*s %lu", &info.swap);
+                       sscanf(buf, "%*s %Lu", &info.swap);
                } else if (strncmp(buf, "Buffers:", 8) == 0) {
-                       sscanf(buf, "%*s %lu", &info.buffers);
+                       sscanf(buf, "%*s %Lu", &info.buffers);
                } else if (strncmp(buf, "Cached:", 7) == 0) {
-                       sscanf(buf, "%*s %lu", &info.cached);
+                       sscanf(buf, "%*s %Lu", &info.cached);
                }
        }
        
@@ -133,21 +116,21 @@ void update_meminfo()
        info.bufmem = info.cached + info.buffers;
 
        info.mask |= (1 << INFO_MEM) | (1 << INFO_BUFFERS);
+
+  fclose (meminfo_fp);
 }
 
-static FILE *net_dev_fp;
 static FILE *net_wireless_fp;
 
 inline void update_net_stats()
 {
-       static int rep;
+  FILE *net_dev_fp;
+       static int rep = 0;
        // FIXME: arbitrary size chosen to keep code simple.
        int i, i2;
        unsigned int curtmp1, curtmp2;
        unsigned int k;
        struct ifconf conf;
-
-
        char buf[256];
        double delta;
 
@@ -157,13 +140,11 @@ inline void update_net_stats()
                return;
 
        /* open file and ignore first two lines */
-       if (net_dev_fp == NULL) {
-               net_dev_fp = open_file("/proc/net/dev", &rep);
-       }
-       else
-               fseek(net_dev_fp, 0, SEEK_SET);
-       if (!net_dev_fp)
-               return;
+  if (!(net_dev_fp = open_file("/proc/net/dev", &rep)))
+  {
+    clear_net_stats ();
+    return;
+  }
 
        fgets(buf, 255, net_dev_fp);    /* garbage */
        fgets(buf, 255, net_dev_fp);    /* garbage (field names) */
@@ -265,17 +246,17 @@ inline void update_net_stats()
                        }
                }
 
-
-
        }
 
-       /* fclose(net_dev_fp); net_dev_fp = NULL; */
+       fclose(net_dev_fp);
+
+  info.mask |= (1 << INFO_NET);
 }
 
 inline void update_wifi_stats()
 {
        /** wireless stats patch by Bobby Beckmann **/
-       static int rep;
+       static int rep = 0;
        int i;
        char buf[256];
        /*open file and ignore first two lines       sorry, this code sucks ass right now, i'll clean it up later */
@@ -325,7 +306,28 @@ int result;
 
 void update_total_processes()
 {
-       update_sysinfo();
+#ifdef HAVE_SYSINFO
+  if (!prefer_proc)
+  {
+    struct sysinfo s_info;
+    sysinfo(&s_info);
+    info.procs = s_info.procs;
+  }
+  else
+#endif
+  {
+    static int rep = 0;
+    FILE *fp;
+
+    if (!(fp = open_file("/proc/loadavg", &rep)))
+    {
+      info.procs=0;
+      return;
+    }
+    fscanf(fp, "%*f %*f %*f %*d/%hd", &info.procs );
+    fclose(fp);
+  }
+  info.mask |= (1 << INFO_PROCS);
 }
 
 #define CPU_SAMPLE_COUNT 15
@@ -345,10 +347,6 @@ struct cpu_info {
        double cpu_val[CPU_SAMPLE_COUNT];
 };
 static short cpu_setup = 0;
-static int rep;
-
-
-static FILE *stat_fp;
 
 /* 
    determine if this kernel gives us "extended" statistics information in /proc/stat. 
@@ -364,13 +362,16 @@ void determine_longstat(char * buf) {
 
 void get_cpu_count()
 {
-       char buf[256];
-       if (stat_fp == NULL)
-               stat_fp = open_file("/proc/stat", &rep);
-       else
-               fseek(stat_fp, 0, SEEK_SET);
-       if (stat_fp == NULL)
+  FILE *stat_fp;
+  static int rep = 0;
+
+       if (info.cpu_usage) {
                return;
+       }
+       char buf[256];
+
+  if (!(stat_fp = open_file("/proc/stat", &rep)))
+    return;
 
        info.cpu_count = 0;
 
@@ -387,6 +388,7 @@ void get_cpu_count()
        }
        info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
 
+  fclose (stat_fp);
 }
 
 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
@@ -394,6 +396,8 @@ void get_cpu_count()
 
 inline static void update_stat()
 {
+  FILE *stat_fp;
+  static int rep = 0;
        static struct cpu_info *cpu = NULL;
        char buf[256];
        unsigned int i;
@@ -403,29 +407,32 @@ inline static void update_stat()
        unsigned int malloc_cpu_size=0;
        
 
-       if (!cpu_setup) {
+       /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
+       if (!cpu_setup || !info.cpu_usage) {
                get_cpu_count();
                cpu_setup = 1;
        }
 
-       if (stat_template == NULL) {
+       if (!stat_template) {
                stat_template = KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT ;
        }       
 
-       if (cpu == NULL) {
-               malloc_cpu_size = (info.cpu_count + 1) *  sizeof(struct cpu_info);
+       if (!cpu) {
+               malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
                cpu = malloc(malloc_cpu_size);
                memset(cpu, 0, malloc_cpu_size);
        }
 
-       if (stat_fp == NULL) {
-               stat_fp = open_file("/proc/stat", &rep);
-       } else {
-               fseek(stat_fp, 0, SEEK_SET);
-       }
-       if (stat_fp == NULL) {
-               return;
-       }
+  if (!(stat_fp = open_file("/proc/stat", &rep)))
+  {
+    info.run_procs=0;
+    if (info.cpu_usage)
+    {
+       memset(info.cpu_usage, 0, info.cpu_count * sizeof (float));
+    }
+    return;
+  }
+
        index = 0;
        while (!feof(stat_fp)) {
                if (fgets(buf, 255, stat_fp) == NULL)
@@ -461,7 +468,7 @@ inline static void update_stat()
                        info.mask |= (1 << INFO_CPU);
 
                        double delta = current_update_time - last_update_time;
-                       if (delta <= 0.001) return;     
+                       if (delta <= 0.001) break;      
 
                        cpu[index].cpu_val[0] = (cpu[index].cpu_active_total -  cpu[index].cpu_last_active_total) / 
                                                (float )(cpu[index].cpu_total - cpu[index].cpu_last_total); 
@@ -487,6 +494,7 @@ inline static void update_stat()
                }
 
        }
+  fclose (stat_fp);
 }
 
 void update_running_processes()
@@ -502,26 +510,29 @@ void update_cpu_usage()
 void update_load_average()
 {
 #ifdef HAVE_GETLOADAVG
-       double v[3];
-       getloadavg(v, 3);
-       info.loadavg[0] = (float) v[0];
-       info.loadavg[1] = (float) v[1];
-       info.loadavg[2] = (float) v[2];
-#else
-       static int rep;
-       FILE *fp;
-
-       fp = open_file("/proc/loadavg", &rep);
-       if (!fp) {
-               v[0] = v[1] = v[2] = 0.0;
-               return;
-       }
-
-       fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
-              &info.loadavg[2]);
-
-       fclose(fp);
+  if (!prefer_proc)
+  {
+         double v[3];
+         getloadavg(v, 3);
+         info.loadavg[0] = (float) v[0];
+         info.loadavg[1] = (float) v[1];
+         info.loadavg[2] = (float) v[2];
+  }
+  else
 #endif
+  {
+    static int rep = 0;
+    FILE *fp;
+
+    if (!(fp = open_file("/proc/loadavg", &rep)))
+    {
+      info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
+      return;
+    }
+    fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1], &info.loadavg[2]);
+    fclose(fp);
+  }
+  info.mask |= (1 << INFO_LOADAVG);
 }
 
 #define PROC_I8K "/proc/i8k"
@@ -610,7 +621,7 @@ open_i2c_sensor(const char *dev, const char *type, int n, int *div,
 
        /* if i2c device is NULL or *, get first */
        if (dev == NULL || strcmp(dev, "*") == 0) {
-               static int rep;
+               static int rep = 0;
                if (!get_first_file_in_a_directory(I2C_DIR, buf, &rep))
                        return -1;
                dev = buf;
@@ -728,7 +739,7 @@ double get_i2c_info(int *fd, int div, char *devtype, char *type)
 
 void get_adt746x_fan( char * p_client_buffer, size_t client_buffer_size )
 {
-       static int rep;
+       static int rep = 0;
        char adt746x_fan_state[64];
        FILE *fp;
 
@@ -761,7 +772,7 @@ void get_adt746x_fan( char * p_client_buffer, size_t client_buffer_size )
 
 void get_adt746x_cpu( char * p_client_buffer, size_t client_buffer_size )
 {
-       static int rep;
+       static int rep = 0;
        char adt746x_cpu_state[64];
        FILE *fp;
 
@@ -834,49 +845,66 @@ void get_freq_dynamic( char * p_client_buffer, size_t client_buffer_size, char *
        snprintf( p_client_buffer, client_buffer_size, p_format, (float)((cycles[1] - cycles[0]) / microseconds) / divisor );
        return;
 #else
-       get_freq( p_client_buffer, client_buffer_size, p_format, divisor );
+/* FIXME: hardwired: get freq for first cpu!
+   this whole function needs to be rethought and redone for
+   multi-cpu/multi-core/multi-threaded environments and 
+   arbitrary combinations thereof 
+*/
+       get_freq( p_client_buffer, client_buffer_size, p_format, divisor, 1 );
        return;
 #endif
 }
 
-#define CPUFREQ_CURRENT "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq"
+
+#define CPUFREQ_PREFIX "/sys/devices/system/cpu"
+#define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
 
 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
-void get_freq( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor )
+char get_freq( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor, unsigned int cpu )
 {
        FILE *f;
+  static int rep = 0;
        char frequency[32];
        char s[256];
        double freq = 0;
-
+       
        if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 )
-               return;
-
-       f = fopen(CPUFREQ_CURRENT, "r");
-       if (f) {
-               /* if there's a cpufreq /sys node, read the current frequency from this node;
-                * divide by 1000 to get Mhz. */
-               if (fgets(s, sizeof(s), f)) {
-                       s[strlen(s)-1] = '\0';
-                       freq = strtod(s, NULL);
-               }
-               fclose(f);
-               snprintf( p_client_buffer, client_buffer_size, p_format, (freq/1000)/divisor );
-               return;
+               return 0;
+       
+  if (!prefer_proc)
+  {
+    char current_freq_file[128];
+    snprintf(current_freq_file, 127, "%s/cpu%d/%s",CPUFREQ_PREFIX, cpu-1, CPUFREQ_POSTFIX);
+         f = fopen(current_freq_file, "r");
+         if (f) 
+    {
+                 /* if there's a cpufreq /sys node, read the current frequency from this node;
+                  * divide by 1000 to get Mhz. */
+                 if (fgets(s, sizeof(s), f)) {
+                           s[strlen(s)-1] = '\0';
+                           freq = strtod(s, NULL);
+                 }
+                 fclose(f);
+                 snprintf( p_client_buffer, client_buffer_size, p_format, (freq/1000)/divisor );
+                 return 1;
+    }
        }
        
-       f = fopen("/proc/cpuinfo", "r");                //open the CPU information file
-       if (!f)
-           return;
+       f = open_file("/proc/cpuinfo", &rep);           //open the CPU information file
+       if (!f) {
+               perror("Conky: Failed to access '/proc/cpuinfo' at get_freq()");
+               return 0;
+       }
 
        while (fgets(s, sizeof(s), f) != NULL){         //read the file
+
 #if defined(__i386) || defined(__x86_64)
-               if (strncmp(s, "cpu MHz", 7) == 0) {    //and search for the cpu mhz
+               if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {        //and search for the cpu mhz
 #else
 #if defined(__alpha)
-               if (strncmp(s, "cycle frequency [Hz]", 20) == 0) {              // different on alpha
+               if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {          // different on alpha
 #else
-               if (strncmp(s, "clock", 5) == 0) {      // this is different on ppc for some reason
+               if (strncmp(s, "clock", 5) == 0 && cpu == 0) {  // this is different on ppc for some reason
 #endif // defined(__alpha)
 #endif // defined(__i386) || defined(__x86_64)
 
@@ -890,19 +918,101 @@ void get_freq( char * p_client_buffer, size_t client_buffer_size, char * p_forma
 #endif
                break;
                }
+               if (strncmp(s, "processor", 9) == 0) {
+                   cpu--; 
+                   continue;
+               }
+               
        }
        
        fclose(f);
        snprintf( p_client_buffer, client_buffer_size, p_format, (float)freq/divisor );
-       return;
+       return 1;
 }
 
+#define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
+
+/* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
+char get_voltage( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor, unsigned int cpu )
+{
+/* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks 
+   something like this:
+# frequency voltage
+1800000 1340
+1600000 1292
+1400000 1100
+1200000 988
+1000000 1116
+800000 1004
+600000 988
+*/
+
+/* Peter Tarjan (ptarjan@citromail.hu) */
+       FILE *f;
+       char s[256];
+       int freq = 0;
+       int voltage = 0;
+       char current_freq_file[128];
+       int freq_comp = 0;
+       
+
+/* build the voltage file name */
+       cpu--;
+       snprintf(current_freq_file, 127, "%s/cpu%d/%s",
+                CPUFREQ_PREFIX, cpu, CPUFREQ_POSTFIX);
+
+       if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 )
+               return 0;
+
+       /* read the current cpu frequency from the /sys node */
+       f = fopen(current_freq_file, "r");
+       if (f) {
+           if (fgets(s, sizeof(s), f)) {
+               s[strlen(s)-1] = '\0';
+               freq = strtod(s, NULL);
+           }
+           fclose(f);
+       } else {
+               fprintf(stderr, "Conky: Failed to access '%s' at ", current_freq_file);
+               perror("get_voltage()");
+               if (f) {
+                       fclose(f);
+               }
+               return 0;
+           }
+
+       snprintf(current_freq_file, 127, "%s/cpu%d/%s",
+                CPUFREQ_PREFIX, cpu, CPUFREQ_VOLTAGE);
+
+/* use the current cpu frequency to find the corresponding voltage */
+       f = fopen(current_freq_file, "r");
+
+       if (f) {
+               while (!feof(f)) {
+                       char line[256];
+                       if (fgets(line, 255, f) == NULL) break;
+                       sscanf(line, "%d %d", &freq_comp, &voltage);
+                       if(freq_comp == freq) break;
+               }
+               fclose(f);
+       } else {
+               fprintf(stderr, "Conky: Failed to access '%s' at ", current_freq_file);
+               perror("get_voltage()");
+               if (f) {
+                       fclose(f);
+               }
+               return 0;
+       }
+       snprintf( p_client_buffer, client_buffer_size, p_format, (float)voltage/divisor );
+       return 1;
+
+}
 
 #define ACPI_FAN_DIR "/proc/acpi/fan/"
 
 void get_acpi_fan( char * p_client_buffer, size_t client_buffer_size )
 {
-       static int rep;
+       static int rep = 0;
        char buf[256];
        char buf2[256];
        FILE *fp;
@@ -937,7 +1047,7 @@ void get_acpi_fan( char * p_client_buffer, size_t client_buffer_size )
 
 void get_acpi_ac_adapter( char * p_client_buffer, size_t client_buffer_size )
 {
-       static int rep;
+       static int rep = 0;
        char buf[256];
        char buf2[256];
        FILE *fp;
@@ -993,7 +1103,7 @@ int open_acpi_temperature(const char *name)
        int fd;
 
        if (name == NULL || strcmp(name, "*") == 0) {
-               static int rep;
+               static int rep = 0;
                if (!get_first_file_in_a_directory
                    (ACPI_THERMAL_DIR, buf, &rep))
                        return -1;
@@ -1090,24 +1200,31 @@ static FILE *acpi_bat_fp;
 static FILE *apm_bat_fp;
 
 static int acpi_last_full;
+static int acpi_design_capacity;
 
-static char last_battery_str[64];
+static char last_battery_str[64];      /* e.g. "charging 75%" */
+static char last_battery_time_str[64]; /* e.g. "3h 15m" */
 
 static double last_battery_time;
 
-void get_battery_stuff(char *buf, unsigned int n, const char *bat)
+static int last_battery_perct;
+static double last_battery_perct_time;
+
+void get_battery_stuff(char *buf, unsigned int n, const char *bat, int item)
 {
-       static int rep, rep2;
+       static int rep = 0, rep2 = 0;
        char acpi_path[128];
        snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
 
        /* don't update battery too often */
-       if (current_update_time - last_battery_time < 29.5) {
-               snprintf(buf, n, "%s", last_battery_str);
-               return;
-       }
+       if (current_update_time - last_battery_time < 29.5) 
+               goto set_return_value;  
+
        last_battery_time = current_update_time;
 
+       memset (last_battery_str, 0, sizeof (last_battery_str));
+       memset (last_battery_time_str, 0, sizeof (last_battery_time_str));
+
        /* first try ACPI */
 
        if (acpi_bat_fp == NULL && apm_bat_fp == NULL)
@@ -1117,11 +1234,12 @@ void get_battery_stuff(char *buf, unsigned int n, const char *bat)
                int present_rate = -1;
                int remaining_capacity = -1;
                char charging_state[64];
+               char present[4];
 
                /* read last full capacity if it's zero */
                if (acpi_last_full == 0) {
-                       static int rep;
-                       char path[128];
+                       static int rep = 0;
+      char path[128];
                        FILE *fp;
                        snprintf(path, 127,
                                 ACPI_BATTERY_BASE_PATH "/%s/info", bat);
@@ -1150,61 +1268,71 @@ void get_battery_stuff(char *buf, unsigned int n, const char *bat)
                                break;
 
                        /* let's just hope units are ok */
-                       if (buf[0] == 'c')
-                               sscanf(buf, "charging state: %63s",
-                                      charging_state);
-                       else if (buf[0] == 'p')
-                               sscanf(buf, "present rate: %d",
-                                      &present_rate);
-                       else if (buf[0] == 'r')
-                               sscanf(buf, "remaining capacity: %d",
-                                      &remaining_capacity);
+                       if (strncmp (buf, "present:", 8) == 0) 
+                               sscanf(buf, "present: %4s", present);
+                       else if (strncmp (buf, "charging state:", 15) == 0)
+                               sscanf(buf, "charging state: %63s", charging_state);
+                       else if (strncmp (buf, "present rate:", 13) == 0)
+                               sscanf(buf, "present rate: %d", &present_rate);
+                       else if (strncmp(buf, "remaining capacity:", 19) == 0)
+                               sscanf(buf, "remaining capacity: %d", &remaining_capacity);
                }
 
+               /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
+               if (remaining_capacity > acpi_last_full)
+                       acpi_last_full = remaining_capacity;  /* normalize to 100% */
+
+               /* not present */
+               if (strcmp(present, "no") == 0) {
+                       strncpy(last_battery_str, "not present", 64);
+               }
                /* charging */
-               if (strcmp(charging_state, "charging") == 0) {
+               else if (strcmp(charging_state, "charging") == 0) {
                        if (acpi_last_full != 0 && present_rate > 0) {
-                               strcpy(last_battery_str, "charging ");
-                               format_seconds(last_battery_str + 9,
-                                              63 - 9,
-                                              (acpi_last_full -
-                                               remaining_capacity) * 60 *
-                                              60 / present_rate);
-                       } else if (acpi_last_full != 0
-                                  && present_rate <= 0) {
-                               sprintf(last_battery_str, "charging %d%%",
-                                       remaining_capacity * 100 /
-                                       acpi_last_full);
+                               /* e.g. charging 75% */
+                               snprintf(last_battery_str, sizeof(last_battery_str)-1, "charging %i%%", 
+                                       (int) ((remaining_capacity * 100) / acpi_last_full));
+                               /* e.g. 2h 37m */
+                               format_seconds(last_battery_time_str, sizeof(last_battery_time_str)-1,
+                                             (long) (((acpi_last_full - remaining_capacity) * 3600) / 
+                                                     present_rate));
+                       } else if (acpi_last_full != 0 && present_rate <= 0) {
+                               snprintf(last_battery_str, sizeof(last_battery_str)-1, "charging %d%%",
+                                       (int) ((remaining_capacity * 100) / acpi_last_full));
                        } else {
-                               strcpy(last_battery_str, "charging");
+                               strncpy(last_battery_str, "charging", sizeof(last_battery_str)-1);
                        }
                }
                /* discharging */
-               else if (strcmp(charging_state, "discharging") == 0) {
-                       if (present_rate > 0)
-                               format_seconds(last_battery_str, 63,
-                                              (remaining_capacity * 60 *
-                                               60) / present_rate);
-                       else
-                               sprintf(last_battery_str,
+               else if (strncmp(charging_state, "discharging", 64) == 0) {
+                       if (present_rate > 0) {
+                               /* e.g. discharging 35% */
+                               snprintf(last_battery_str, sizeof(last_battery_str)-1, "discharging %i%%",
+                                       (int) ((remaining_capacity * 100) / acpi_last_full));
+                               /* e.g. 1h 12m */
+                               format_seconds(last_battery_time_str, sizeof(last_battery_time_str)-1,
+                                             (long) ((remaining_capacity * 3600) / present_rate));
+                       } else if (present_rate == 0) { /* Thanks to Nexox for this one */
+                               snprintf(last_battery_str, sizeof(last_battery_str)-1, "full");
+                       } else {
+                               snprintf(last_battery_str, sizeof(last_battery_str)-1,
                                        "discharging %d%%",
-                                       remaining_capacity * 100 /
-                                       acpi_last_full);
+                                       (int) ((remaining_capacity * 100) / acpi_last_full));
+                       }
                }
                /* charged */
                /* thanks to Lukas Zapletal <lzap@seznam.cz> */
-               else if (strcmp(charging_state, "charged") == 0) {
+               else if (strncmp(charging_state, "charged", 64) == 0) {
                                strcpy(last_battery_str, "charged");
-               }
+               } 
                /* unknown, probably full / AC */
                else {
                        if (acpi_last_full != 0
                            && remaining_capacity != acpi_last_full)
-                               sprintf(last_battery_str, "unknown %d%%",
-                                       remaining_capacity * 100 /
-                                       acpi_last_full);
+                               snprintf(last_battery_str, 64, "unknown %d%%",
+                                       (int) ((remaining_capacity * 100) / acpi_last_full));
                        else
-                               strcpy(last_battery_str, "AC");
+                               strncpy(last_battery_str, "AC", 64);
                }
        } else {
                /* APM */
@@ -1236,7 +1364,196 @@ void get_battery_stuff(char *buf, unsigned int n, const char *bat)
                }
        }
 
-       snprintf(buf, n, "%s", last_battery_str);
+set_return_value:
+       switch (item) {
+        case BATTERY_STATUS:
+               {
+                       snprintf(buf, n, "%s", last_battery_str);
+                       break;
+               }
+        case BATTERY_TIME:
+               {
+                       snprintf(buf, n, "%s", last_battery_time_str);
+                       break;
+               }
+       default:
+                       break;
+        }              
+       return;
+}
+
+int get_battery_perct(const char *bat)
+{
+       static int rep;
+       char acpi_path[128];
+       snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
+
+       /* don't update battery too often */
+       if (current_update_time - last_battery_perct_time < 30) {
+               return last_battery_perct;
+       }
+       last_battery_perct_time = current_update_time;
+
+       /* Only check for ACPI */
+
+       if (acpi_bat_fp == NULL && apm_bat_fp == NULL)
+               acpi_bat_fp = open_file(acpi_path, &rep);
+
+       int remaining_capacity = -1;
+       if (acpi_bat_fp != NULL) {
+               /* read last full capacity if it's zero */
+               if (acpi_design_capacity == 0) {
+                       static int rep;
+                       char path[128];
+                       FILE *fp;
+                       snprintf(path, 127,
+                                ACPI_BATTERY_BASE_PATH "/%s/info", bat);
+                       fp = open_file(path, &rep);
+                       if (fp != NULL) {
+                               while (!feof(fp)) {
+                                       char b[256];
+                                       if (fgets(b, 256, fp) == NULL)
+                                               break;
+                                       if (sscanf(b, "design capacity: %d", &acpi_design_capacity) != 0) {
+                                               break;
+                                       }
+                               }
+                               fclose(fp);
+                       }
+               }
+
+               fseek(acpi_bat_fp, 0, SEEK_SET);
+
+               while (!feof(acpi_bat_fp)) {
+                       char buf[256];
+                       if (fgets(buf, 256, acpi_bat_fp) == NULL)
+                               break;
+
+                       if (buf[0] == 'r')
+                               sscanf(buf, "remaining capacity: %d",
+                                      &remaining_capacity);
+               }
+       }
+       if(remaining_capacity < 0)
+               return 0;
+       /* compute the battery percentage */
+       last_battery_perct = 
+               (int) (((float)remaining_capacity/acpi_design_capacity) * 100);
+       return last_battery_perct;
+}
+
+int get_battery_perct_bar(const char *bar)
+{
+       get_battery_perct(bar);
+       return (int) (last_battery_perct * 2.56 - 1);
+}
+
+
+
+/* On Apple powerbook and ibook:
+$ cat /proc/pmu/battery_0
+flags      : 00000013
+charge     : 3623
+max_charge : 3720
+current    : 388
+voltage    : 16787
+time rem.  : 900
+$ cat /proc/pmu/info
+PMU driver version     : 2
+PMU firmware version   : 0c
+AC Power               : 1
+Battery count          : 1
+*/
+
+/* defines as in <linux/pmu.h> */
+#define PMU_BATT_PRESENT        0x00000001
+#define PMU_BATT_CHARGING       0x00000002
+
+static FILE* pmu_battery_fp;
+static FILE* pmu_info_fp;
+static char pb_battery_info[3][32];
+static double pb_battery_info_update;
+#define PMU_PATH "/proc/pmu"
+void get_powerbook_batt_info(char *buf, size_t n, int i)
+{
+        static int rep = 0;
+        const char* batt_path = PMU_PATH "/battery_0";
+        const char* info_path = PMU_PATH "/info";
+        int flags, charge, max_charge, ac = -1;
+        long time = -1;
+
+        /* don't update battery too often */
+        if (current_update_time - pb_battery_info_update < 29.5) {
+                snprintf(buf, n, "%s", pb_battery_info[i]);
+                return;
+        }
+        pb_battery_info_update = current_update_time;
+
+        if (pmu_battery_fp == NULL)
+                pmu_battery_fp = open_file(batt_path, &rep);
+
+        if (pmu_battery_fp != NULL) {
+               rewind(pmu_battery_fp);
+                while (!feof(pmu_battery_fp)) {
+                        char buf[32];
+                        if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL)
+                                break;
+
+                        if (buf[0] == 'f')
+                                sscanf(buf, "flags      : %8x", &flags);
+                        else if (buf[0] == 'c' && buf[1] == 'h')
+                                sscanf(buf, "charge     : %d", &charge);
+                        else if (buf[0] == 'm')
+                                sscanf(buf, "max_charge : %d", &max_charge);
+                        else if (buf[0] == 't')
+                                sscanf(buf, "time rem.  : %ld", &time);
+                }
+        }
+        if (pmu_info_fp == NULL)
+                pmu_info_fp = open_file(info_path, &rep);
+
+        if (pmu_info_fp != NULL) {
+               rewind(pmu_info_fp);
+                while (!feof(pmu_info_fp)) {
+                        char buf[32];
+                        if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL)
+                                break;
+                        if (buf[0] == 'A')
+                                sscanf(buf, "AC Power               : %d", &ac);
+                }
+        }
+        /* update status string */
+        if ((ac && !(flags & PMU_BATT_PRESENT)))
+                strcpy(pb_battery_info[PB_BATT_STATUS], "AC");
+        else if (ac && (flags & PMU_BATT_PRESENT)
+                  && !(flags & PMU_BATT_CHARGING))
+                strcpy(pb_battery_info[PB_BATT_STATUS], "charged");
+        else if ((flags & PMU_BATT_PRESENT)
+                && (flags & PMU_BATT_CHARGING))
+                strcpy(pb_battery_info[PB_BATT_STATUS], "charging");
+        else
+                strcpy(pb_battery_info[PB_BATT_STATUS], "discharging");
+
+        /* update percentage string */
+        if (time == 0)
+                pb_battery_info[PB_BATT_PERCENT][0] = 0; 
+        else
+                snprintf(pb_battery_info[PB_BATT_PERCENT],
+                        sizeof(pb_battery_info[PB_BATT_PERCENT]),
+                        "%d%%", (charge * 100)/max_charge);
+
+        /* update time string */
+        if (time == 0) /* fully charged or battery not present */
+                pb_battery_info[PB_BATT_TIME][0] = 0; 
+        else if (time < 60*60) /* don't show secs */
+                format_seconds_short(pb_battery_info[PB_BATT_TIME],
+                        sizeof(pb_battery_info[PB_BATT_TIME]), time);
+        else
+                format_seconds(pb_battery_info[PB_BATT_TIME],
+                        sizeof(pb_battery_info[PB_BATT_TIME]), time);
+
+        snprintf(buf, n, "%s", pb_battery_info[i]);
 }
 
 void update_top()
@@ -1267,24 +1584,27 @@ void update_top()
 void update_diskio()
 {
        static unsigned int last = UINT_MAX;
-       static FILE* fp;
+       static unsigned int last_read = UINT_MAX;
+       static unsigned int last_write = UINT_MAX;
+       FILE* fp;
+       static int rep=0;
 
        char buf[512];
        int major, minor;
        unsigned int current = 0;
+       unsigned int current_read = 0;
+       unsigned int current_write = 0;
        unsigned int reads, writes = 0;
        int col_count = 0;
 
-       if (!fp) {
-               fp = fopen("/proc/diskstats", "r");
-       } else {
-               fseek(fp, 0, SEEK_SET);
+       if (!(fp =open_file("/proc/diskstats", &rep))) {
+               diskio_value=0;
+               return;
        }
 
        /* read reads and writes from all disks (minor = 0), including
         * cd-roms and floppies, and summ them up
         */
-       current = 0;
        while (!feof(fp)) {
                fgets(buf, 512, fp);
                col_count = sscanf(buf, "%u %u %*s %*u %*u %u %*u %*u %*u %u",
@@ -1298,6 +1618,8 @@ void update_diskio()
                    major != LVM_BLK_MAJOR && major != NBD_MAJOR &&
                    major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
                        current += reads + writes;
+                       current_read += reads;
+                       current_write += writes;
                }
        }
 
@@ -1306,6 +1628,14 @@ void update_diskio()
         * "sectors read", and we therefore have to divide by two to
         * get KB */
        int tot = ((double)(current-last)/2);
+       int tot_read = ((double)(current_read-last_read)/2);
+       int tot_write = ((double)(current_write-last_write)/2);
+       
+       if (last_read > current_read)
+           tot_read = 0;
+       if (last_write > current_write)
+           tot_write = 0;    
+       
        if (last > current) {
                /* we hit this either if it's the very first time we
                  * run this, or when /proc/diskstats overflows; while
@@ -1313,7 +1643,265 @@ void update_diskio()
                tot = 0;
        }
        last = current;
+       last_read = current_read;
+       last_write = current_write;
 
        diskio_value = tot;
+       diskio_read_value = tot_read;
+       diskio_write_value = tot_write;
+
+       fclose(fp);
 }
 
+/* Here come the IBM ACPI-specific things. For reference, see
+ http://ibm-acpi.sourceforge.net/README
+If IBM ACPI is installed, /proc/acpi/ibm contains the following files:
+bay
+beep
+bluetooth
+brightness
+cmos
+dock
+driver
+ecdump
+fan
+hotkey
+led
+light
+thermal
+video
+volume
+The content of these files is described in detail in the aforementioned
+README - some of them also in the following functions accessing them.
+Peter Tarjan (ptarjan@citromail.hu)
+*/
+
+#define IBM_ACPI_DIR "/proc/acpi/ibm"
+
+void get_ibm_acpi_fan( char * p_client_buffer, size_t client_buffer_size )
+{
+/* get fan speed on IBM/Lenovo laptops running the ibm acpi.
+   /proc/acpi/ibm/fan looks like this (3 lines):
+status:         disabled
+speed:          2944
+commands:       enable, disable
+Peter Tarjan (ptarjan@citromail.hu)
+*/
+
+    if ( !p_client_buffer || client_buffer_size <= 0 )
+       return;
+    
+    FILE *fp;
+    unsigned int speed=0;
+    char fan[128];
+    snprintf(fan, 127, "%s/fan",IBM_ACPI_DIR);    
+
+    fp = fopen(fan, "r");
+    if (fp != NULL)
+    {
+       while (!feof(fp))
+       {
+           char line[256];
+           if (fgets(line, 255, fp) == NULL) break;
+           if (sscanf(line, "speed: %d", &speed)) break;       
+       }
+    }
+    else 
+    {
+       CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove ibm* from your Conky config file.", fan, strerror(errno));
+    }
+
+    fclose(fp);
+    snprintf( p_client_buffer, client_buffer_size, "%d", speed );
+    return;
+    
+}    
+
+static double last_ibm_acpi_temp_time;
+void get_ibm_acpi_temps()
+{
+/* get the measured temperatures from the temperature sensors 
+   on IBM/Lenovo laptops running the ibm acpi.
+   There are 8 values in /proc/acpi/ibm/thermal, and according to 
+   http://ibm-acpi.sourceforge.net/README
+   these mean the following (at least on an IBM R51...)
+0:  CPU (also on the T series laptops)
+1:  Mini PCI Module (?)
+2:  HDD (?)
+3:  GPU (also on the T series laptops)
+4:  Battery (?)
+5:  N/A
+6:  Battery (?)
+7:  N/A 
+   I'm not too sure about those with the question mark, but the values I'm 
+   reading from *my* thermal file (on a T42p) look realistic for the 
+   hdd and the battery. 
+   #5 and #7 are always -128. 
+   /proc/acpi/ibm/thermal looks like this (1 line):
+temperatures:   41 43 31 46 33 -128 29 -128
+Peter Tarjan (ptarjan@citromail.hu)
+*/
+
+/*    don't update too often */
+    if (current_update_time - last_ibm_acpi_temp_time < 10.00) 
+    {
+       return;
+    }
+    last_ibm_acpi_temp_time = current_update_time; 
+    
+/*    if ( !p_client_buffer || client_buffer_size <= 0 )
+      return; */
+
+    FILE *fp;
+
+    char thermal[128];
+    snprintf(thermal, 127, "%s/thermal",IBM_ACPI_DIR);    
+    fp = fopen(thermal, "r");
+
+    if (fp != NULL)
+    {
+       while (!feof(fp))
+       {
+           char line[256];
+           if (fgets(line, 255, fp) == NULL) break;
+           if (sscanf(line, "temperatures: %d %d %d %d %d %d %d %d",
+                      &ibm_acpi.temps[0], &ibm_acpi.temps[1],
+                      &ibm_acpi.temps[2], &ibm_acpi.temps[3],
+                      &ibm_acpi.temps[4], &ibm_acpi.temps[5], 
+                      &ibm_acpi.temps[6], &ibm_acpi.temps[7])) break;
+       }
+    }
+    else 
+    {
+       CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove ibm* from your Conky config file.", thermal, strerror(errno));
+    }
+
+    fclose(fp);
+
+}             
+
+
+void get_ibm_acpi_volume( char * p_client_buffer, size_t client_buffer_size )
+{
+
+/* get volume (0-14) on IBM/Lenovo laptops running the ibm acpi.
+   "Volume" here is none of the mixer volumes, but a "master of masters"
+   volume adjusted by the IBM volume keys.
+   /proc/acpi/ibm/fan looks like this (4 lines):
+level:          4
+mute:           off
+commands:       up, down, mute
+commands:       level <level> (<level> is 0-15)
+Peter Tarjan (ptarjan@citromail.hu)
+*/
+    
+    if ( !p_client_buffer || client_buffer_size <= 0 )
+       return;
+    
+    FILE *fp;
+
+    char volume[128];
+    snprintf(volume, 127, "%s/volume",IBM_ACPI_DIR);    
+    unsigned int vol=-1;
+    char mute[3]="";
+
+    fp = fopen(volume, "r");
+    if (fp != NULL)
+    {
+       while (!feof(fp))
+       {
+           char line[256];
+           if (fgets(line, 255, fp) == NULL) break;
+           if (sscanf(line, "level: %d", &vol)) continue;
+           if (sscanf(line, "mute: %s", mute)) break;
+       }
+    }
+    else 
+    {
+       CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove ibm* from your Conky config file.", volume, strerror(errno));
+    }
+
+    fclose(fp);
+
+    if (strcmp(mute, "on")==0)
+    {
+       snprintf( p_client_buffer, client_buffer_size, "%s", "mute" );
+       return;
+    }
+    else
+    {
+       snprintf( p_client_buffer, client_buffer_size, "%d", vol );
+       return;
+    }
+
+}
+
+/*static FILE *fp=NULL;*/
+
+void get_ibm_acpi_brightness(char * p_client_buffer, size_t client_buffer_size)
+{
+/* get LCD brightness on IBM/Lenovo laptops running the ibm acpi.
+   /proc/acpi/ibm/brightness looks like this (3 lines):
+level:          7
+commands:       up, down
+commands:       level <level> (<level> is 0-7)
+Peter Tarjan (ptarjan@citromail.hu)
+*/
+
+    if ( !p_client_buffer || client_buffer_size <= 0 )
+       return;
+
+    FILE *fp;
+    unsigned int brightness=0;
+    char filename[128];
+    snprintf(filename, 127, "%s/brightness",IBM_ACPI_DIR);    
+
+    fp = fopen(filename, "r");
+    if (fp != NULL)
+    {
+       while (!feof(fp))
+       {
+           char line[256];
+           if (fgets(line, 255, fp) == NULL) break;
+           if (sscanf(line, "level: %d", &brightness)) break;  
+       }
+    }
+    else 
+    {
+       CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove ibm* from your Conky config file.", filename, strerror(errno));
+    }
+
+    fclose(fp);
+
+    snprintf( p_client_buffer, client_buffer_size, "%d", brightness );
+    return;
+    
+}    
+
+void update_entropy (void)
+{
+  static int rep = 0;
+  const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
+  const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
+  FILE *fp1, *fp2;
+
+  info.entropy.entropy_avail=0;
+  info.entropy.poolsize=0;
+
+  if ((fp1 = open_file (entropy_avail, &rep))==NULL)
+    return;
+
+  if ((fp2 = open_file (entropy_poolsize, &rep))==NULL)
+  {
+    fclose (fp1);
+    return;
+  }
+
+  fscanf (fp1, "%u", &info.entropy.entropy_avail);
+  fscanf (fp2, "%u", &info.entropy.poolsize);
+
+  fclose (fp1);
+  fclose (fp2);
+
+  info.mask |= (1 << INFO_ENTROPY);
+}