Make a description of ${cpu} variable not so confusing.
[monky] / src / linux.c
index b567bb5..aa07f98 100644 (file)
 #include <net/if.h>
 #include <math.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;
@@ -109,20 +113,20 @@ void update_meminfo()
                        break;
 
                if (strncmp(buf, "MemTotal:", 9) == 0) {
-                       sscanf(buf, "%*s %u", &info.memmax);
+                       sscanf(buf, "%*s %lu", &info.memmax);
                } else if (strncmp(buf, "MemFree:", 8) == 0) {
-                       sscanf(buf, "%*s %u", &info.mem);
+                       sscanf(buf, "%*s %lu", &info.mem);
                } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
-                       sscanf(buf, "%*s %u", &info.swapmax);
+                       sscanf(buf, "%*s %lu", &info.swapmax);
                } else if (strncmp(buf, "SwapFree:", 9) == 0) {
-                       sscanf(buf, "%*s %u", &info.swap);
+                       sscanf(buf, "%*s %lu", &info.swap);
                } else if (strncmp(buf, "Buffers:", 8) == 0) {
-                       sscanf(buf, "%*s %u", &info.buffers);
+                       sscanf(buf, "%*s %lu", &info.buffers);
                } else if (strncmp(buf, "Cached:", 7) == 0) {
-                       sscanf(buf, "%*s %u", &info.cached);
+                       sscanf(buf, "%*s %lu", &info.cached);
                }
        }
-
+       
        info.mem = info.memmax - info.mem;
        info.swap = info.swapmax - info.swap;
 
@@ -153,8 +157,9 @@ inline void update_net_stats()
                return;
 
        /* open file and ignore first two lines */
-       if (net_dev_fp == NULL)
+       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)
@@ -169,8 +174,9 @@ inline void update_net_stats()
                char *s, *p;
                long long r, t, last_recv, last_trans;
 
-               if (fgets(buf, 255, net_dev_fp) == NULL)
+               if (fgets(buf, 255, net_dev_fp) == NULL) {
                        break;
+               }
                p = buf;
                while (isspace((int) *p))
                        p++;
@@ -186,6 +192,7 @@ inline void update_net_stats()
 
                ns = get_net_stat(s);
                ns->up = 1;
+               memset(&(ns->addr.sa_data), 0, 14);
                last_recv = ns->recv;
                last_trans = ns->trans;
 
@@ -307,7 +314,8 @@ inline void update_wifi_stats()
 
                sscanf(p, "%*d   %d.  %d.  %d", &l, &m, &n);
 
-               ns->linkstatus = (int) (log(l) / log(92) * 100);
+               ns->linkstatus = (int) (log(MIN(MAX(l,1),92)) / log(92) * 100);
+
        }
 
        /*** end wireless patch ***/
@@ -320,13 +328,21 @@ void update_total_processes()
        update_sysinfo();
 }
 
+#define CPU_SAMPLE_COUNT 15
 struct cpu_info {
-       unsigned int cpu_user;
-       unsigned int cpu_system;
-       unsigned int cpu_nice;
-       double last_cpu_sum;
-       int clock_ticks;
-       double cpu_val[15];
+       unsigned long long cpu_user;
+       unsigned long long cpu_system;
+       unsigned long long cpu_nice;
+       unsigned long long cpu_idle;
+       unsigned long long cpu_iowait;
+       unsigned long long cpu_irq;
+       unsigned long long cpu_softirq;
+       unsigned long long cpu_steal;
+       unsigned long long cpu_total;
+       unsigned long long cpu_active_total;
+       unsigned long long cpu_last_total;
+       unsigned long long cpu_last_active_total;
+       double cpu_val[CPU_SAMPLE_COUNT];
 };
 static short cpu_setup = 0;
 static int rep;
@@ -334,8 +350,23 @@ static int rep;
 
 static FILE *stat_fp;
 
+/* 
+   determine if this kernel gives us "extended" statistics information in /proc/stat. 
+   Kernels around 2.5 and earlier only reported user, system, nice and idle values in proc stat. 
+   Kernels around 2.6 and greater report these PLUS iowait, irq, softirq, and steal 
+*/
+void determine_longstat(char * buf) { 
+       unsigned long long iowait=0;
+       KFLAG_SETOFF(KFLAG_IS_LONGSTAT);        
+       /* scanf will either return -1 or 1 because there is only 1 assignment  */
+       if (sscanf(buf, "%*s %*d %*d %*d %*d %llu",&iowait)>0) KFLAG_SETON(KFLAG_IS_LONGSTAT);
+}
+
 void get_cpu_count()
 {
+       if (info.cpu_usage) {
+               return;
+       }
        char buf[256];
        if (stat_fp == NULL)
                stat_fp = open_file("/proc/stat", &rep);
@@ -351,13 +382,17 @@ void get_cpu_count()
                        break;
 
                if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
+                       if (info.cpu_count == 0) {
+                               determine_longstat(buf);
+                       }
                        info.cpu_count++;
                }
        }
-       info.cpu_usage = malloc(info.cpu_count * sizeof(float));
-       printf("cpu count is %i\n", info.cpu_count);
+       info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
 }
 
+#define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
+#define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
 
 inline static void update_stat()
 {
@@ -366,13 +401,25 @@ inline static void update_stat()
        unsigned int i;
        unsigned int index;
        double curtmp;
+       char * stat_template=NULL; 
+       unsigned int malloc_cpu_size=0;
+       
+
        if (!cpu_setup) {
                get_cpu_count();
                cpu_setup = 1;
        }
+
+       if (stat_template == NULL) {
+               stat_template = KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT ;
+       }       
+
        if (cpu == NULL) {
-                       cpu = malloc(info.cpu_count * sizeof(struct cpu_info));
+               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 {
@@ -387,52 +434,61 @@ inline static void update_stat()
                        break;
 
                if (strncmp(buf, "procs_running ", 14) == 0) {
-                       sscanf(buf, "%*s %d", &info.run_procs);
+                       sscanf(buf, "%*s %hu", &info.run_procs);
                        info.mask |= (1 << INFO_RUN_PROCS);
-               } else if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3]) && index < info.cpu_count) {
-                       sscanf(buf, "%*s %u %u %u", &(cpu[index].cpu_user), &(cpu[index].cpu_nice),
-                              &(cpu[index].cpu_system));
-                       index++;
+               } else if (strncmp(buf, "cpu", 3) == 0) {
+                       index = isdigit(buf[3]) ? ((int)buf[3]) - 0x2F : 0;
+                       sscanf(buf, stat_template 
+                               , &(cpu[index].cpu_user)
+                               , &(cpu[index].cpu_nice)
+                               , &(cpu[index].cpu_system)
+                               , &(cpu[index].cpu_idle)
+                               , &(cpu[index].cpu_iowait)
+                               , &(cpu[index].cpu_irq)
+                               , &(cpu[index].cpu_softirq)
+                               , &(cpu[index].cpu_steal)
+                               );
+
+                       cpu[index].cpu_total = cpu[index].cpu_user 
+                                        + cpu[index].cpu_nice 
+                                        + cpu[index].cpu_system 
+                                        + cpu[index].cpu_idle 
+                                        + cpu[index].cpu_iowait 
+                                        + cpu[index].cpu_irq
+                                        + cpu[index].cpu_softirq
+                                        + cpu[index].cpu_steal 
+                                        ; 
+
+                       cpu[index].cpu_active_total = cpu[index].cpu_total - (cpu[index].cpu_idle + cpu[index].cpu_iowait);
                        info.mask |= (1 << INFO_CPU);
-               }
-       }
 
-       for (index = 0; index < info.cpu_count; index++) {
-               double delta;
-               delta = current_update_time - last_update_time;
-               if (delta <= 0.001) {
-                       return;
-               }
+                       double delta = current_update_time - last_update_time;
+                       if (delta <= 0.001) return;     
 
-               if (cpu[index].clock_ticks == 0) {
-                       cpu[index].clock_ticks = sysconf(_SC_CLK_TCK);
-               }
-               curtmp = 0;
-               cpu[index].cpu_val[0] =
-                               (cpu[index].cpu_user + cpu[index].cpu_nice + cpu[index].cpu_system -
-                               cpu[index].last_cpu_sum) / delta / (double) cpu[index].clock_ticks;
-               for (i = 0; i < info.cpu_avg_samples; i++) {
-                       curtmp += cpu[index].cpu_val[i];
+                       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); 
+                       curtmp = 0;
+                       for (i=0; i < info.cpu_avg_samples; i++ ) {
+                               curtmp += cpu[index].cpu_val[i];
+                       }
+                       /* TESTING -- I've removed this, because I don't think it is right. You shouldn't divide 
+                                     by the cpu count here ... removing for testing */
+                       /* if (index == 0) {
+                               info.cpu_usage[index] = curtmp / info.cpu_avg_samples / info.cpu_count; 
+                       } else {
+                               info.cpu_usage[index] = curtmp / info.cpu_avg_samples;
+                       }  */
+                       /* TESTING -- this line replaces the prev. "suspect" if/else */
+                       info.cpu_usage[index] = curtmp / info.cpu_avg_samples;
+
+                       cpu[index].cpu_last_total = cpu[index].cpu_total;
+                       cpu[index].cpu_last_active_total = cpu[index].cpu_active_total;
+                       for (i = info.cpu_avg_samples - 1; i > 0; i--) {
+                               cpu[index].cpu_val[i] = cpu[index].cpu_val[i - 1];
+                       }
                }
-               printf("setting usage to %f\n", curtmp / info.cpu_avg_samples);
-               info.cpu_usage[index] = curtmp / info.cpu_avg_samples;
-               cpu[index].last_cpu_sum = cpu[index].cpu_user + cpu[index].cpu_nice + cpu[index].cpu_system;
-               for (i = info.cpu_avg_samples; i > 1; i--)
-                       cpu[index].cpu_val[i - 1] = cpu[index].cpu_val[i - 2];
 
        }
-
-// test code
-// this is for getting proc shit
-// pee pee
-// poo
-       //
-
-
-
-
-
-
 }
 
 void update_running_processes()
@@ -550,7 +606,7 @@ open_i2c_sensor(const char *dev, const char *type, int n, int *div,
                char *devtype)
 {
        char path[256];
-       char buf[64];
+       char buf[256];
        int fd;
        int divfd;
 
@@ -567,12 +623,11 @@ open_i2c_sensor(const char *dev, const char *type, int n, int *div,
                type = "in";
 
        if (strcmp(type, "tempf") == 0) {
-               snprintf(path, 255, I2C_DIR "%s/%s%d_input", dev, "temp",
-                        n);
+               snprintf(path, 255, I2C_DIR "%s/%s%d_input", dev, "temp", n);
        } else {
                snprintf(path, 255, I2C_DIR "%s/%s%d_input", dev, type, n);
        }
-       strcpy(devtype, path);
+       strncpy(devtype, path, 255);
 
        /* open file */
        fd = open(path, O_RDONLY);
@@ -666,51 +721,68 @@ double get_i2c_info(int *fd, int div, char *devtype, char *type)
        }
 }
 
-#define ADT746X_FAN "/sys/devices/temperatures/cpu_fan_speed"
-
-static char *adt746x_fan_state;
+/* Prior to kernel version 2.6.12, the CPU fan speed was available
+ * in ADT746X_FAN_OLD, whereas later kernel versions provide this
+ * information in ADT746X_FAN.
+ */
+#define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
+#define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
 
-char *get_adt746x_fan()
+void get_adt746x_fan( char * p_client_buffer, size_t client_buffer_size )
 {
        static int rep;
+       char adt746x_fan_state[64];
        FILE *fp;
 
-       if (adt746x_fan_state == NULL) {
-               adt746x_fan_state = (char *) malloc(100);
-               assert(adt746x_fan_state != NULL);
-       }
+       if ( !p_client_buffer || client_buffer_size <= 0 )
+               return;
 
-       fp = open_file(ADT746X_FAN, &rep);
-       if (!fp) {
-               strcpy(adt746x_fan_state,
-                      "No fan found! Hey, you don't have one?");
-               return adt746x_fan_state;
+       if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
+                && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL)
+
+       {
+               sprintf(adt746x_fan_state, "adt746x not found");
+       }
+       else
+       {
+               fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
+               adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
+               fclose(fp);
        }
-       fscanf(fp, "%s", adt746x_fan_state);
-       fclose(fp);
 
-       return adt746x_fan_state;
+       snprintf( p_client_buffer, client_buffer_size, "%s", adt746x_fan_state );
+       return;
 }
 
-#define ADT746X_CPU "/sys/devices/temperatures/cpu_temperature"
-
-static char *adt746x_cpu_state;
+/* Prior to kernel version 2.6.12, the CPU temperature was found
+ * in ADT746X_CPU_OLD, whereas later kernel versions provide this
+ * information in ADT746X_CPU.
+ */
+#define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
+#define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
 
-char *get_adt746x_cpu()
+void get_adt746x_cpu( char * p_client_buffer, size_t client_buffer_size )
 {
        static int rep;
+       char adt746x_cpu_state[64];
        FILE *fp;
 
-       if (adt746x_cpu_state == NULL) {
-               adt746x_cpu_state = (char *) malloc(100);
-               assert(adt746x_cpu_state != NULL);
+       if ( !p_client_buffer || client_buffer_size <= 0 )
+               return;
+       
+       if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
+                && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL)
+       {
+               sprintf(adt746x_cpu_state, "adt746x not found");
+       }
+       else
+       {
+               fscanf(fp, "%2s", adt746x_cpu_state);
+               fclose(fp);
        }
 
-       fp = open_file(ADT746X_CPU, &rep);
-       fscanf(fp, "%2s", adt746x_cpu_state);
-       fclose(fp);
-
-       return adt746x_cpu_state;
+       snprintf( p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state ); 
+       return;
 }
 
 /* Thanks to "Walt Nelson" <wnelsonjr@comcast.net> */
@@ -733,19 +805,20 @@ __inline__ unsigned long long int rdtsc()
        __asm__ volatile (".byte 0x0f, 0x31":"=A" (x));
        return x;
 }
-static char *buffer = NULL;
 #endif
 
-float get_freq_dynamic()
+/* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
+void get_freq_dynamic( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor )
 {
 #if  defined(__i386) || defined(__x86_64)
-       if (buffer == NULL)
-               buffer = malloc(64);
        struct timezone tz;
        struct timeval tvstart, tvstop;
        unsigned long long cycles[2];   /* gotta be 64 bit */
        unsigned int microseconds;      /* total time taken */
 
+       if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 )
+            return;
+
        memset(&tz, 0, sizeof(tz));
 
        /* get this function in cached memory */
@@ -760,122 +833,240 @@ float get_freq_dynamic()
        microseconds = ((tvstop.tv_sec - tvstart.tv_sec) * 1000000) +
            (tvstop.tv_usec - tvstart.tv_usec);
 
-       return (cycles[1] - cycles[0]) / microseconds;
+       snprintf( p_client_buffer, client_buffer_size, p_format, (float)((cycles[1] - cycles[0]) / microseconds) / divisor );
+       return;
 #else
-       return get_freq();
+/* 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"
 
-static char *frequency;
-       
-float get_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) */
+char get_freq( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor, unsigned int cpu )
 {
        FILE *f;
-       char s[1000];
-       if (frequency == NULL) {
-               frequency = (char *) malloc(100);
-               assert(frequency != NULL);
-       }
-       f = fopen(CPUFREQ_CURRENT, "r");
+       char frequency[32];
+       char s[256];
+       double freq = 0;
+       char current_freq_file[128];
+       
+       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;
+       
+       f = fopen(current_freq_file, "r");
        if (f) {
-               /* if there's a cpufreq /sys node, read the current
-                * frequency there from this node; divice by 1000 to
-                * get MHz
-                */
-               double freq = 0;
-               if (fgets(s, 1000,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);
-               return (freq/1000);
+               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 0;
-       while (fgets(s, 1000, f) != NULL){      //read the file
+       cpu++;
+       f = fopen("/proc/cpuinfo", "r");                //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", 5) == 0) {    //and search for the cpu mhz
+               if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {        //and search for the cpu mhz
 #else
-               if (strncmp(s, "clock", 5) == 0) {      // this is different on ppc for some reason
-#endif
+#if defined(__alpha)
+               if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {          // different on alpha
+#else
+               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)
+
                strcpy(frequency, strchr(s, ':') + 2);  //copy just the number
-               frequency[strlen(frequency) - 1] = '\0';        // strip \n
+#if defined(__alpha)
+               frequency[strlen(frequency) - 6] = '\0';// strip " est.\n"
+               freq = strtod(frequency, NULL)/1000000; // kernel reports in Hz 
+#else
+               frequency[strlen(frequency) - 1] = '\0'; // strip \n
+               freq = strtod(frequency, NULL);
+#endif
                break;
                }
+               if (strncmp(s, "processor", 9) == 0) {
+                   cpu--; 
+                   continue;
+               }
+               
        }
-               fclose(f);
-               return strtod(frequency, (char **)NULL);
+       
+       fclose(f);
+       snprintf( p_client_buffer, client_buffer_size, p_format, (float)freq/divisor );
+       return 1;
 }
 
+#define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
 
-#define ACPI_FAN_DIR "/proc/acpi/fan/"
+/* 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;
 
-static char *acpi_fan_state;
+       /* 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;
+
+}
 
-char *get_acpi_fan()
+#define ACPI_FAN_DIR "/proc/acpi/fan/"
+
+void get_acpi_fan( char * p_client_buffer, size_t client_buffer_size )
 {
        static int rep;
        char buf[256];
        char buf2[256];
        FILE *fp;
 
-       if (acpi_fan_state == NULL) {
-               acpi_fan_state = (char *) malloc(100);
-               assert(acpi_fan_state != NULL);
-       }
+       if ( !p_client_buffer || client_buffer_size <= 0 )
+               return;
 
        /* yeah, slow... :/ */
        if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep))
-               return "no fans?";
+       {
+               snprintf( p_client_buffer, client_buffer_size, "no fans?" );
+               return;
+       }
 
-       snprintf(buf2, 256, "%s%s/state", ACPI_FAN_DIR, buf);
+       snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf );
 
        fp = open_file(buf2, &rep);
        if (!fp) {
-               strcpy(acpi_fan_state, "can't open fan's state file");
-               return acpi_fan_state;
+               snprintf( p_client_buffer, client_buffer_size, "can't open fan's state file" );
+               return;
        }
-       fscanf(fp, "%*s %99s", acpi_fan_state);
+       memset(buf,0,sizeof(buf));
+       fscanf(fp, "%*s %99s", buf);
+       fclose(fp);
+
+       snprintf( p_client_buffer, client_buffer_size, "%s", buf );
 
-       return acpi_fan_state;
+       return;
 }
 
 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
 
-static char *acpi_ac_adapter_state;
-
-char *get_acpi_ac_adapter()
+void get_acpi_ac_adapter( char * p_client_buffer, size_t client_buffer_size )
 {
        static int rep;
        char buf[256];
        char buf2[256];
        FILE *fp;
 
-       if (acpi_ac_adapter_state == NULL) {
-               acpi_ac_adapter_state = (char *) malloc(100);
-               assert(acpi_ac_adapter_state != NULL);
-       }
+       if ( !p_client_buffer || client_buffer_size <= 0 )
+               return;
 
        /* yeah, slow... :/ */
        if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep))
-               return "no ac_adapters?";
+       {
+               snprintf( p_client_buffer, client_buffer_size, "no ac_adapters?" );
+               return; 
+       }
 
-       snprintf(buf2, 256, "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
+       snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf );
+        
 
        fp = open_file(buf2, &rep);
        if (!fp) {
-               strcpy(acpi_ac_adapter_state,
-                      "No ac adapter found.... where is it?");
-               return acpi_ac_adapter_state;
+               snprintf( p_client_buffer, client_buffer_size, "No ac adapter found.... where is it?" );
+               return;
        }
-       fscanf(fp, "%*s %99s", acpi_ac_adapter_state);
+       memset(buf,0,sizeof(buf));
+       fscanf(fp, "%*s %99s", buf );
        fclose(fp);
 
-       return acpi_ac_adapter_state;
+       snprintf( p_client_buffer, client_buffer_size, "%s", buf );
+
+       return;
 }
 
 /*
@@ -898,7 +1089,7 @@ passive:                 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
 int open_acpi_temperature(const char *name)
 {
        char path[256];
-       char buf[64];
+       char buf[256];
        int fd;
 
        if (name == NULL || strcmp(name, "*") == 0) {
@@ -1026,6 +1217,7 @@ 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) {
@@ -1040,11 +1232,9 @@ void get_battery_stuff(char *buf, unsigned int n, const char *bat)
                                        char b[256];
                                        if (fgets(b, 256, fp) == NULL)
                                                break;
-
-                                       if (sscanf
-                                           (b, "last full capacity: %d",
-                                            &acpi_last_full) != 0)
+                                       if (sscanf(b, "last full capacity: %d", &acpi_last_full) != 0) {
                                                break;
+                                       }
                                }
 
                                fclose(fp);
@@ -1061,7 +1251,9 @@ void get_battery_stuff(char *buf, unsigned int n, const char *bat)
                                break;
 
                        /* let's just hope units are ok */
-                       if (buf[0] == 'c')
+                       if (buf[0] == 'p') {
+                               sscanf(buf, "present: %4s", present);
+                       } else if (buf[0] == 'c')
                                sscanf(buf, "charging state: %63s",
                                       charging_state);
                        else if (buf[0] == 'p')
@@ -1071,57 +1263,59 @@ void get_battery_stuff(char *buf, unsigned int n, const char *bat)
                                sscanf(buf, "remaining capacity: %d",
                                       &remaining_capacity);
                }
-
+               
+               /* 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,
+                               snprintf(last_battery_str, 63, "charging %i%% ", (int) (remaining_capacity / acpi_last_full) * 100);
+                               format_seconds(last_battery_str + 14,
+                                              63 - 14,
                                               (acpi_last_full -
                                                remaining_capacity) * 60 *
                                               60 / present_rate);
                        } else if (acpi_last_full != 0
                                   && present_rate <= 0) {
-                               sprintf(last_battery_str, "charging %d%%",
+                               snprintf(last_battery_str, 64, "charging %d%%",
                                        remaining_capacity * 100 /
                                        acpi_last_full);
                        } else {
-                               strcpy(last_battery_str, "charging");
+                               strncpy(last_battery_str, "charging", 63);
                        }
                }
                /* discharging */
-               else if (strcmp(charging_state, "discharging") == 0) {
-                       if (present_rate > 0)
-                               format_seconds(last_battery_str, 63,
+               else if (strncmp(charging_state, "discharging", 64) == 0) {
+                       if (present_rate > 0) {
+                               snprintf(last_battery_str, 63, "discharging %i%% ", (int)(remaining_capacity / acpi_last_full) * 100);
+                               format_seconds(last_battery_str + 17, 63 - 17,
                                               (remaining_capacity * 60 *
                                                60) / present_rate);
-                       else
-                               sprintf(last_battery_str,
+                       } else if (present_rate == 0) { /* Thanks to Nexox for this one */
+                               snprintf(last_battery_str, 64, "full");
+                       } else {
+                               snprintf(last_battery_str, 64,
                                        "discharging %d%%",
                                        remaining_capacity * 100 /
                                        acpi_last_full);
+                       }
                }
                /* charged */
                /* thanks to Lukas Zapletal <lzap@seznam.cz> */
-               else if (strcmp(charging_state, "charged") == 0) {
-                       if (acpi_last_full != 0
-                           && remaining_capacity != acpi_last_full)
-                               sprintf(last_battery_str, "charged %d%%",
-                                       remaining_capacity * 100 /
-                                       acpi_last_full);
-                       else
+               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%%",
+                               snprintf(last_battery_str, 64, "unknown %d%%",
                                        remaining_capacity * 100 /
                                        acpi_last_full);
                        else
-                               strcpy(last_battery_str, "AC");
+                               strncpy(last_battery_str, "AC", 64);
                }
        } else {
                /* APM */
@@ -1156,10 +1350,117 @@ void get_battery_stuff(char *buf, unsigned int n, const char *bat)
        snprintf(buf, n, "%s", last_battery_str);
 }
 
+/* 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;
+        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()
 {
        show_nice_processes = 1;
        process_find_top(info.cpu, info.memu);
+       info.first_process = get_first_process();
 }
 
 
@@ -1201,17 +1502,16 @@ void update_diskio()
         * cd-roms and floppies, and summ them up
         */
        current = 0;
-       strcmp(buf, "fasdf");
        while (!feof(fp)) {
                fgets(buf, 512, fp);
                col_count = sscanf(buf, "%u %u %*s %*u %*u %u %*u %*u %*u %u",
                                   &major, &minor, &reads, &writes);
-               /* ignore subdevices (they have only 7 entries in their line)
+               /* ignore subdevices (they have only 3 matching entries in their line)
                 * and virtual devices (LVM, network block devices, RAM disks, Loopback)
                 *
                 * XXX ignore devices which are part of a SW RAID (MD_MAJOR)
                 */
-               if (col_count > 7 &&
+               if (col_count > 3 &&
                    major != LVM_BLK_MAJOR && major != NBD_MAJOR &&
                    major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
                        current += reads + writes;
@@ -1233,3 +1533,228 @@ void update_diskio()
 
        diskio_value = tot;
 }
+
+/* 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;
+    
+}