Initial revision
[monky] / linux.c
1 #include "conky.h"
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <dirent.h>
6 #include <ctype.h>
7 #include <errno.h>
8 #include <limits.h>
9 #include <sys/types.h>
10 #include <sys/sysinfo.h>
11 #include <sys/stat.h>
12 #include <fcntl.h>
13 #include <unistd.h>
14 // #include <assert.h>
15 #include <time.h>
16 #include <proc/procps.h>
17 #include <proc/readproc.h>
18 #include "top.h"
19
20 #include <sys/ioctl.h>
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 #include <linux/sockios.h>
24 #include <net/if.h>
25 #include <math.h>
26
27
28 static struct sysinfo s_info;
29
30 #define TEXT_BUFFER_SIZE (1024*4)
31
32 static int show_nice_processes;
33
34 void prepare_update()
35 {
36 }
37
38 static void update_sysinfo()
39 {
40         sysinfo(&s_info);
41
42         info.uptime = (double) s_info.uptime;
43
44         /* there was some problem with these */
45 #if 0
46 //      info.loadavg[0] = s_info.loads[0] / 100000.0f;
47         info.loadavg[1] = s_info.loads[1] / 100000.0f;
48         info.loadavg[2] = s_info.loads[2] / 100000.0f;
49         gkrelltop_process_find_top_three info.mask |= 1 << INFO_LOADAVG;
50 #endif
51
52         info.procs = s_info.procs;
53
54         /* these aren't nice, no cache and should check kernel version for mem_unit */
55 #if 0
56         info.memmax = s_info.totalram;
57         info.mem = s_info.totalram - s_info.freeram;
58         info.swapmax = s_info.totalswap;
59         info.swap = s_info.totalswap - s_info.swap;
60         info.mask |= 1 << INFO_MEM;
61 #endif
62
63         info.mask |= (1 << INFO_UPTIME) | (1 << INFO_PROCS);
64 }
65
66 void update_uptime()
67 {
68         /* prefers sysinfo() for uptime, I don't really know which one is better
69         * (=faster?) */
70 #ifdef USE_PROC_UPTIME
71         static int rep;
72         FILE *fp = open_file("/proc/uptime", &rep);
73         if (!fp)
74                 return 0;
75         fscanf(fp, "%lf", &info.uptime);
76         fclose(fp);
77
78         info.mask |= (1 << INFO_UPTIME);
79 #else
80         update_sysinfo();
81 #endif
82 }
83
84 /* these things are also in sysinfo except Buffers:, that's why I'm reading
85 * them from proc */
86
87 static FILE *meminfo_fp;
88
89 void update_meminfo()
90 {
91         static int rep;
92         /*  unsigned int a; */
93         char buf[256];
94
95         info.mem = info.memmax = info.swap = info.swapmax = info.bufmem =
96                         info.buffers = info.cached = 0;
97
98         if (meminfo_fp == NULL)
99                 meminfo_fp = open_file("/proc/meminfo", &rep);
100         else
101                 fseek(meminfo_fp, 0, SEEK_SET);
102         if (meminfo_fp == NULL)
103                 return;
104
105         while (!feof(meminfo_fp)) {
106                 if (fgets(buf, 255, meminfo_fp) == NULL)
107                         break;
108
109                 if (strncmp(buf, "MemTotal:", 9) == 0) {
110                         sscanf(buf, "%*s %u", &info.memmax);
111                 } else if (strncmp(buf, "MemFree:", 8) == 0) {
112                         sscanf(buf, "%*s %u", &info.mem);
113                 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
114                         sscanf(buf, "%*s %u", &info.swapmax);
115                 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
116                         sscanf(buf, "%*s %u", &info.swap);
117                 } else if (strncmp(buf, "Buffers:", 8) == 0) {
118                         sscanf(buf, "%*s %u", &info.buffers);
119                 } else if (strncmp(buf, "Cached:", 7) == 0) {
120                         sscanf(buf, "%*s %u", &info.cached);
121                 }
122         }
123
124         info.mem = info.memmax - info.mem;
125         info.swap = info.swapmax - info.swap;
126
127         info.bufmem = info.cached + info.buffers;
128
129         info.mask |= (1 << INFO_MEM) | (1 << INFO_BUFFERS);
130 }
131
132 static FILE *net_dev_fp;
133 static FILE *net_wireless_fp;
134
135 inline void update_net_stats()
136 {
137         static int rep;
138         // FIXME: arbitrary size chosen to keep code simple.
139         int i, i2;
140         unsigned int curtmp1, curtmp2;
141         unsigned int k;
142         struct ifconf conf;
143
144
145         char buf[256];
146         double delta;
147
148         /* get delta */
149         delta = current_update_time - last_update_time;
150         if (delta <= 0.0001)
151                 return;
152
153         /* open file and ignore first two lines */
154         if (net_dev_fp == NULL)
155                 net_dev_fp = open_file("/proc/net/dev", &rep);
156         else
157                 fseek(net_dev_fp, 0, SEEK_SET);
158         if (!net_dev_fp)
159                 return;
160
161         fgets(buf, 255, net_dev_fp);    /* garbage */
162         fgets(buf, 255, net_dev_fp);    /* garbage (field names) */
163
164         /* read each interface */
165         for (i2 = 0; i2 < 16; i2++) {
166                 struct net_stat *ns;
167                 char *s, *p;
168                 long long r, t, last_recv, last_trans;
169
170                 if (fgets(buf, 255, net_dev_fp) == NULL)
171                         break;
172                 p = buf;
173                 while (isspace((int) *p))
174                         p++;
175
176                 s = p;
177
178                 while (*p && *p != ':')
179                         p++;
180                 if (*p == '\0')
181                         continue;
182                 *p = '\0';
183                 p++;
184
185                 ns = get_net_stat(s);
186                 ns->up = 1;
187                 last_recv = ns->recv;
188                 last_trans = ns->trans;
189
190                 sscanf(p,
191                        /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
192                        "%Ld  %*d     %*d  %*d  %*d  %*d   %*d        %*d       %Ld",
193                        &r, &t);
194
195                 /* if recv or trans is less than last time, an overflow happened */
196
197                 if (r < ns->last_read_recv)
198                         ns->recv +=
199                                         ((long long) 4294967295U -
200                                         ns->last_read_recv) + r;
201                 else
202                         ns->recv += (r - ns->last_read_recv);
203                 ns->last_read_recv = r;
204
205                 if (t < ns->last_read_trans)
206                         ns->trans +=
207                                         ((long long) 4294967295U -
208                                         ns->last_read_trans) + t;
209                 else
210                         ns->trans += (t - ns->last_read_trans);
211                 ns->last_read_trans = t;
212
213                 /*** ip addr patch ***/
214
215                 s = (char*)socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
216                 
217                 conf.ifc_buf = malloc(sizeof(struct ifreq) * 16);
218                 
219                 conf.ifc_len = sizeof(struct ifreq) * 16;
220
221                 ioctl((long)s, SIOCGIFCONF, &conf);
222
223                 for (k=0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
224                         struct net_stat *ns;    
225                         ns = get_net_stat(((struct ifreq*)conf.ifc_buf)[k].ifr_ifrn.ifrn_name);
226                         ns->addr = ((struct ifreq*)conf.ifc_buf)[k].ifr_ifru.ifru_addr;
227                 }
228
229                 close((long)s);
230                 
231                 free(conf.ifc_buf);
232
233
234                 /*** end ip addr patch ***/
235
236
237                 /* calculate speeds */
238                 ns->net_rec[0] = (ns->recv - last_recv) / delta;
239                 ns->net_trans[0] = (ns->trans - last_trans) / delta;
240                 curtmp1 = 0;
241                 curtmp2 = 0;
242                 // get an average
243                 for (i = 0; (unsigned) i < info.net_avg_samples; i++) {
244                         curtmp1 += ns->net_rec[i];
245                         curtmp2 += ns->net_trans[i];
246                 }
247                 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
248                 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
249                 if (info.net_avg_samples > 1) {
250                         for (i = info.net_avg_samples; i > 1; i--) {
251                                 ns->net_rec[i - 1] = ns->net_rec[i - 2];
252                                 ns->net_trans[i - 1] =
253                                                 ns->net_trans[i - 2];
254                         }
255                 }
256
257
258
259         }
260
261         /* fclose(net_dev_fp); net_dev_fp = NULL; */
262 }
263
264 inline void update_wifi_stats() {
265         /** wireless stats patch by Bobby Beckmann **/
266         static int rep;
267         int i;
268         char buf[256];
269         /*open file and ignore first two lines       sorry, this code sucks ass right now, i'll clean it up later*/
270         if (net_wireless_fp == NULL)
271                 net_wireless_fp = open_file("/proc/net/wireless", &rep);
272         else
273                 fseek(net_wireless_fp, 0, SEEK_SET);
274         if (!net_wireless_fp) return;
275
276         fgets(buf, 255, net_wireless_fp); /* garbage */
277         fgets(buf, 255, net_wireless_fp); /* garbage (field names) */
278
279         /* read each interface */
280         for (i=0; i<16; i++) {
281                 struct net_stat *ns;
282                 char *s, *p;
283                 int l, m, n;
284
285                 if (fgets(buf, 255, net_wireless_fp) == NULL) break;
286                 p = buf;
287                 while (isspace((int) *p)) p++;
288
289                 s = p;
290
291                 while (*p && *p != ':') p++;
292                 if (*p == '\0') continue;
293                 *p = '\0';
294                 p++;
295
296                 ns = get_net_stat(s);
297
298                 sscanf(p,
299                        "%*d   %d.  %d.  %d",
300                        &l,  &m,  &n);
301
302                 ns->linkstatus = (int)(log(l) / log(92) * 100);
303         }
304
305         /*** end wireless patch ***/
306 }
307
308 int result;
309
310 void update_total_processes()
311 {
312         update_sysinfo();
313 }
314
315 static unsigned int cpu_user, cpu_system, cpu_nice;
316 static double last_cpu_sum;
317 static int clock_ticks;
318
319 static FILE *stat_fp;
320
321 inline static void update_stat()
322 {
323         // FIXME: arbitrary size?
324         static double cpu_val[15];
325         static int rep;
326         char buf[256];
327         unsigned int i;
328         double curtmp;
329
330         if (stat_fp == NULL)
331                 stat_fp = open_file("/proc/stat", &rep);
332         else
333                 fseek(stat_fp, 0, SEEK_SET);
334         if (stat_fp == NULL)
335                 return;
336
337         info.cpu_count = 0;
338
339         while (!feof(stat_fp)) {
340                 if (fgets(buf, 255, stat_fp) == NULL)
341                         break;
342
343                 if (strncmp(buf, "procs_running ", 14) == 0) {
344                         sscanf(buf, "%*s %d", &info.run_procs);
345                         info.mask |= (1 << INFO_RUN_PROCS);
346                 } else if (strncmp(buf, "cpu ", 4) == 0) {
347                         sscanf(buf, "%*s %u %u %u", &cpu_user, &cpu_nice,
348                                &cpu_system);
349                         info.mask |= (1 << INFO_CPU);
350                 } else if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
351                         info.cpu_count++;
352                 }
353         }
354
355         {
356                 double delta;
357                 delta = current_update_time - last_update_time;
358                 if (delta <= 0.001)
359                         return;
360
361                 if (clock_ticks == 0)
362                         clock_ticks = sysconf(_SC_CLK_TCK);
363                 curtmp = 0;
364                 cpu_val[0] =
365                                 (cpu_user + cpu_nice + cpu_system -
366                                 last_cpu_sum) / delta / (double) clock_ticks /
367                                 info.cpu_count;
368                 for (i = 0; i < info.cpu_avg_samples; i++)
369                         curtmp += cpu_val[i];
370                 info.cpu_usage = curtmp / info.cpu_avg_samples;
371                 last_cpu_sum = cpu_user + cpu_nice + cpu_system;
372                 for (i = info.cpu_avg_samples; i > 1; i--)
373                         cpu_val[i - 1] = cpu_val[i - 2];
374
375         }
376
377 // test code
378 // this is for getting proc shit
379 // pee pee
380 // poo
381         //
382
383
384
385
386
387
388 }
389
390 void update_running_processes()
391 {
392         update_stat();
393 }
394
395 void update_cpu_usage()
396 {
397         update_stat();
398 }
399
400 void update_load_average()
401 {
402 #ifdef HAVE_GETLOADAVG
403         double v[3];
404         getloadavg(v, 3);
405         info.loadavg[0] = (float) v[0];
406         info.loadavg[1] = (float) v[1];
407         info.loadavg[2] = (float) v[2];
408 #else
409         static int rep;
410         FILE *fp;
411
412         fp = open_file("/proc/loadavg", &rep);
413         if (!fp) {
414                 v[0] = v[1] = v[2] = 0.0;
415                 return;
416         }
417
418         fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1],
419                &info.loadavg[2]);
420
421         fclose(fp);
422 #endif
423 }
424
425
426 static int no_dots(const struct dirent *d)
427 {
428         if (d->d_name[0] == '.')
429                 return 0;
430         return 1;
431 }
432
433 static int
434                 get_first_file_in_a_directory(const char *dir, char *s, int *rep)
435 {
436         struct dirent **namelist;
437         int i, n;
438
439         n = scandir(dir, &namelist, no_dots, alphasort);
440         if (n < 0) {
441                 if (!rep || !*rep) {
442                         ERR("scandir for %s: %s", dir, strerror(errno));
443                         if (rep)
444                                 *rep = 1;
445                 }
446                 return 0;
447         } else {
448                 if (n == 0)
449                         return 0;
450
451                 strncpy(s, namelist[0]->d_name, 255);
452                 s[255] = '\0';
453
454                 for (i = 0; i < n; i++)
455                         free(namelist[i]);
456                 free(namelist);
457
458                 return 1;
459         }
460 }
461
462 #define I2C_DIR "/sys/bus/i2c/devices/"
463
464 int
465 open_i2c_sensor(const char *dev, const char *type, int n, int *div,
466                 char *devtype)
467 {
468         char path[256];
469         char buf[64];
470         int fd;
471         int divfd;
472
473         /* if i2c device is NULL or *, get first */
474         if (dev == NULL || strcmp(dev, "*") == 0) {
475                 static int rep;
476                 if (!get_first_file_in_a_directory(I2C_DIR, buf, &rep))
477                         return -1;
478                 dev = buf;
479         }
480
481         /* change vol to in */
482         if (strcmp(type, "vol") == 0)
483                 type = "in";
484
485         if (strcmp(type, "tempf") == 0) {
486                 snprintf(path, 255, I2C_DIR "%s/%s%d_input", dev, "temp", n);
487         }
488         else {
489                 snprintf(path, 255, I2C_DIR "%s/%s%d_input", dev, type, n);
490         }
491         strcpy(devtype, path);
492
493         /* open file */
494         fd = open(path, O_RDONLY);
495         if (fd < 0)
496                 ERR("can't open '%s': %s", path, strerror(errno));
497
498         if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0 || strcmp(type, "tempf") == 0)
499                 *div = 1;
500         else
501                 *div = 0;
502         /* fan does not use *_div as a read divisor */
503         if (strcmp("fan", type) == 0)
504                 return fd;
505
506         /* test if *_div file exist, open it and use it as divisor */
507         if (strcmp(type, "tempf") == 0) {
508                 snprintf(path, 255, I2C_DIR "%s/%s%d_div", "one", "two", n);
509         }
510         else {
511                 snprintf(path, 255, I2C_DIR "%s/%s%d_div", dev, type, n);
512         }
513
514         divfd = open(path, O_RDONLY);
515         if (divfd > 0) {
516                 /* read integer */
517                 char divbuf[64];
518                 unsigned int divn;
519                 divn = read(divfd, divbuf, 63);
520                 /* should read until n == 0 but I doubt that kernel will give these
521                 * in multiple pieces. :) */
522                 divbuf[divn] = '\0';
523                 *div = atoi(divbuf);
524         }
525
526         close(divfd);
527
528         return fd;
529 }
530
531 double get_i2c_info(int *fd, int div, char *devtype, char *type)
532 {
533         int val = 0;
534
535         if (*fd <= 0)
536                 return 0;
537
538         lseek(*fd, 0, SEEK_SET);
539
540         /* read integer */
541         {
542                 char buf[64];
543                 unsigned int n;
544                 n = read(*fd, buf, 63);
545                 /* should read until n == 0 but I doubt that kernel will give these
546                 * in multiple pieces. :) */
547                 buf[n] = '\0';
548                 val = atoi(buf);
549         }
550
551         close(*fd);
552         /* open file */
553         *fd = open(devtype, O_RDONLY);
554         if (*fd < 0)
555                 ERR("can't open '%s': %s", devtype, strerror(errno));  
556         
557         /* My dirty hack for computing CPU value 
558         * Filedil, from forums.gentoo.org
559         */
560 /*      if (strstr(devtype, "temp1_input") != NULL)
561         return -15.096+1.4893*(val / 1000.0); */
562         
563         
564         /* divide voltage and temperature by 1000 */
565         /* or if any other divisor is given, use that */
566         if (strcmp(type, "tempf") == 0) {
567                 if (div > 1)
568                         return ((val / div + 40)*9.0/5)-40;
569                 else if (div)
570                         return ((val / 1000.0 + 40)*9.0/5)-40;
571                 else
572                         return ((val + 40)*9.0/5)-40;
573         }
574         else {
575                 if (div > 1)
576                         return val / div;
577                 else if (div)
578                         return val / 1000.0;
579                 else
580                         return val;
581         }
582 }
583
584 #define ADT746X_FAN "/sys/devices/temperatures/cpu_fan_speed"
585
586 static char *adt746x_fan_state;
587
588 char *get_adt746x_fan()
589 {
590         static int rep;
591         FILE *fp;
592
593         if (adt746x_fan_state == NULL) {
594                 adt746x_fan_state = (char *) malloc(100);
595                 assert(adt746x_fan_state != NULL);
596         }
597
598         fp = open_file(ADT746X_FAN, &rep);
599         if (!fp) {
600                 strcpy(adt746x_fan_state,
601                        "No fan found! Hey, you don't have one?");
602                 return adt746x_fan_state;
603         }
604         fscanf(fp, "%s", adt746x_fan_state);
605         fclose(fp);
606
607         return adt746x_fan_state;
608 }
609
610 #define ADT746X_CPU "/sys/devices/temperatures/cpu_temperature"
611
612 static char *adt746x_cpu_state;
613
614 char *get_adt746x_cpu()
615 {
616         static int rep;
617         FILE *fp;
618
619         if (adt746x_cpu_state == NULL) {
620                 adt746x_cpu_state = (char *) malloc(100);
621                 assert(adt746x_cpu_state != NULL);
622         }
623
624         fp = open_file(ADT746X_CPU, &rep);
625         fscanf(fp, "%2s", adt746x_cpu_state);
626         fclose(fp);
627
628         return adt746x_cpu_state;
629 }
630
631 static char *frequency;
632
633 char *get_freq()
634 {
635         FILE *f;
636         char s[1000];
637         if (frequency == NULL) {
638                 frequency = (char *) malloc(100);
639                 assert(frequency != NULL);
640         }
641         //char frequency[10];
642         f = fopen("/proc/cpuinfo", "r");        //open the CPU information file
643         //if (!f)
644         //    return;
645         while (fgets(s, 1000, f) != NULL)       //read the file
646                 if (strncmp(s, "cpu M", 5) == 0) {      //and search for the cpu mhz
647                         //printf("%s", strchr(s, ':')+2);
648                 strcpy(frequency, strchr(s, ':') + 2);  //copy just the number
649                 frequency[strlen(frequency) - 1] = '\0';        // strip \n
650                 break;
651                 }
652                 fclose(f);
653                 return frequency;
654 }
655
656 #define ACPI_FAN_DIR "/proc/acpi/fan/"
657
658 static char *acpi_fan_state;
659
660 char *get_acpi_fan()
661 {
662         static int rep;
663         char buf[256];
664         char buf2[256];
665         FILE *fp;
666
667         if (acpi_fan_state == NULL) {
668                 acpi_fan_state = (char *) malloc(100);
669                 assert(acpi_fan_state != NULL);
670         }
671
672         /* yeah, slow... :/ */
673         if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep))
674                 return "no fans?";
675
676         snprintf(buf2, 256, "%s%s/state", ACPI_FAN_DIR, buf);
677
678         fp = open_file(buf2, &rep);
679         if (!fp) {
680                 strcpy(acpi_fan_state, "can't open fan's state file");
681                 return acpi_fan_state;
682         }
683         fscanf(fp, "%*s %99s", acpi_fan_state);
684
685         return acpi_fan_state;
686 }
687
688 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
689
690 static char *acpi_ac_adapter_state;
691
692 char *get_acpi_ac_adapter()
693 {
694         static int rep;
695         char buf[256];
696         char buf2[256];
697         FILE *fp;
698
699         if (acpi_ac_adapter_state == NULL) {
700                 acpi_ac_adapter_state = (char *) malloc(100);
701                 assert(acpi_ac_adapter_state != NULL);
702         }
703
704         /* yeah, slow... :/ */
705         if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep))
706                 return "no ac_adapters?";
707
708         snprintf(buf2, 256, "%s%s/state", ACPI_AC_ADAPTER_DIR, buf);
709
710         fp = open_file(buf2, &rep);
711         if (!fp) {
712                 strcpy(acpi_ac_adapter_state,
713                        "No ac adapter found.... where is it?");
714                 return acpi_ac_adapter_state;
715         }
716         fscanf(fp, "%*s %99s", acpi_ac_adapter_state);
717         fclose(fp);
718
719         return acpi_ac_adapter_state;
720 }
721
722 /*
723 /proc/acpi/thermal_zone/THRM/cooling_mode
724 cooling mode:            active
725 /proc/acpi/thermal_zone/THRM/polling_frequency
726 <polling disabled>
727 /proc/acpi/thermal_zone/THRM/state
728 state:                   ok
729 /proc/acpi/thermal_zone/THRM/temperature
730 temperature:             45 C
731 /proc/acpi/thermal_zone/THRM/trip_points
732 critical (S5):           73 C
733 passive:                 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
734 */
735
736 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
737 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
738
739 int open_acpi_temperature(const char *name)
740 {
741         char path[256];
742         char buf[64];
743         int fd;
744
745         if (name == NULL || strcmp(name, "*") == 0) {
746                 static int rep;
747                 if (!get_first_file_in_a_directory
748                                    (ACPI_THERMAL_DIR, buf, &rep))
749                         return -1;
750                 name = buf;
751         }
752
753         snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
754
755         fd = open(path, O_RDONLY);
756         if (fd < 0)
757                 ERR("can't open '%s': %s", path, strerror(errno));
758
759         return fd;
760 }
761
762 static double last_acpi_temp;
763 static double last_acpi_temp_time;
764
765 double get_acpi_temperature(int fd)
766 {
767         if (fd <= 0)
768                 return 0;
769
770         /* don't update acpi temperature too often */
771         if (current_update_time - last_acpi_temp_time < 11.32) {
772                 return last_acpi_temp;
773         }
774         last_acpi_temp_time = current_update_time;
775
776         /* seek to beginning */
777         lseek(fd, 0, SEEK_SET);
778
779         /* read */
780         {
781                 char buf[256];
782                 int n;
783                 n = read(fd, buf, 255);
784                 if (n < 0)
785                         ERR("can't read fd %d: %s", fd, strerror(errno));
786                 else {
787                         buf[n] = '\0';
788                         sscanf(buf, "temperature: %lf", &last_acpi_temp);
789                 }
790         }
791
792         return last_acpi_temp;
793 }
794
795 /*
796 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info 
797 present:                 yes
798 design capacity:         4400 mAh
799 last full capacity:      4064 mAh
800 battery technology:      rechargeable
801 design voltage:          14800 mV
802 design capacity warning: 300 mAh
803 design capacity low:     200 mAh
804 capacity granularity 1:  32 mAh
805 capacity granularity 2:  32 mAh
806 model number:            02KT
807 serial number:           16922
808 battery type:            LION
809 OEM info:                SANYO
810 */
811
812 /*
813 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
814 present:                 yes
815 capacity state:          ok
816 charging state:          unknown
817 present rate:            0 mA
818 remaining capacity:      4064 mAh
819 present voltage:         16608 mV
820 */
821
822 /*
823 2213<@jupet kellari ö> jupet@lagi-unstable:~$ cat /proc/apm 
824 2213<@jupet kellari ö> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
825 2213<@jupet kellari ö> (-1 ollee ei akkua kiinni, koska akku on pöydällä)
826 2214<@jupet kellari ö> jupet@lagi-unstable:~$ cat /proc/apm 
827 2214<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
828
829 2238<@jupet kellari ö> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
830 2239<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
831
832 2240<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori päällä
833 2241<@jupet kellari ö> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori päällä mutta ilman verkkovirtaa
834 */
835
836 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
837 #define APM_PATH "/proc/apm"
838
839 static FILE *acpi_bat_fp;
840 static FILE *apm_bat_fp;
841
842 static int acpi_last_full;
843
844 static char last_battery_str[64];
845
846 static double last_battery_time;
847
848 void get_battery_stuff(char *buf, unsigned int n, const char *bat)
849 {
850         static int rep, rep2;
851         char acpi_path[128];
852         snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
853
854         /* don't update battery too often */
855         if (current_update_time - last_battery_time < 29.5) {
856                 snprintf(buf, n, "%s", last_battery_str);
857                 return;
858         }
859         last_battery_time = current_update_time;
860
861         /* first try ACPI */
862
863         if (acpi_bat_fp == NULL && apm_bat_fp == NULL)
864                 acpi_bat_fp = open_file(acpi_path, &rep);
865
866         if (acpi_bat_fp != NULL) {
867                 int present_rate = -1;
868                 int remaining_capacity = -1;
869                 char charging_state[64];
870
871                 /* read last full capacity if it's zero */
872                 if (acpi_last_full == 0) {
873                         static int rep;
874                         char path[128];
875                         FILE *fp;
876                         snprintf(path, 127,
877                                  ACPI_BATTERY_BASE_PATH "/%s/info", bat);
878                         fp = open_file(path, &rep);
879                         if (fp != NULL) {
880                                 while (!feof(fp)) {
881                                         char b[256];
882                                         if (fgets(b, 256, fp) == NULL)
883                                                 break;
884
885                                         if (sscanf
886                                                                                (b, "last full capacity: %d",
887                                                         &acpi_last_full) != 0)
888                                                 break;
889                                 }
890
891                                 fclose(fp);
892                         }
893                 }
894
895                 fseek(acpi_bat_fp, 0, SEEK_SET);
896
897                 strcpy(charging_state, "unknown");
898
899                 while (!feof(acpi_bat_fp)) {
900                         char buf[256];
901                         if (fgets(buf, 256, acpi_bat_fp) == NULL)
902                                 break;
903
904                         /* let's just hope units are ok */
905                         if (buf[0] == 'c')
906                                 sscanf(buf, "charging state: %63s",
907                                        charging_state);
908                         else if (buf[0] == 'p')
909                                 sscanf(buf, "present rate: %d",
910                                        &present_rate);
911                         else if (buf[0] == 'r')
912                                 sscanf(buf, "remaining capacity: %d",
913                                        &remaining_capacity);
914                 }
915
916                 /* charging */
917                 if (strcmp(charging_state, "charging") == 0) {
918                         if (acpi_last_full != 0 && present_rate > 0) {
919                                 strcpy(last_battery_str, "charging ");
920                                 format_seconds(last_battery_str + 9,
921                                                63 - 9,
922                                                (acpi_last_full -
923                                                                remaining_capacity) * 60 *
924                                                                60 / present_rate);
925                         } else if (acpi_last_full != 0
926                                                         && present_rate <= 0) {
927                                                                 sprintf(last_battery_str, "charging %d%%",
928                                                                                 remaining_capacity * 100 /
929                                                                                 acpi_last_full);
930                                                         } else {
931                                                                 strcpy(last_battery_str, "charging");
932                                                         }
933                 }
934                 /* discharging */
935                 else if (strcmp(charging_state, "discharging") == 0) {
936                         if (present_rate > 0)
937                                 format_seconds(last_battery_str, 63,
938                                                (remaining_capacity * 60 *
939                                                                60) / present_rate);
940                         else
941                                 sprintf(last_battery_str,
942                                         "discharging %d%%",
943                                         remaining_capacity * 100 /
944                                                         acpi_last_full);
945                 }
946                 /* charged */
947                 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
948                 else if (strcmp(charging_state, "charged") == 0) {
949                         if (acpi_last_full != 0 && remaining_capacity != acpi_last_full)
950                                 sprintf(last_battery_str, "charged %d%%",
951                                         remaining_capacity * 100 / acpi_last_full);
952                         else
953                                 strcpy(last_battery_str, "charged");
954                 }
955                 /* unknown, probably full / AC */
956                 else {
957                         if (acpi_last_full != 0
958                                                  && remaining_capacity != acpi_last_full)
959                                 sprintf(last_battery_str, "unknown %d%%",
960                                         remaining_capacity * 100 /
961                                                         acpi_last_full);
962                         else
963                                 strcpy(last_battery_str, "AC");
964                 }
965         } else {
966                 /* APM */
967                 if (apm_bat_fp == NULL)
968                         apm_bat_fp = open_file(APM_PATH, &rep2);
969
970                 if (apm_bat_fp != NULL) {
971                         int ac, status, flag, life;
972
973                         fscanf(apm_bat_fp,
974                                "%*s %*s %*x %x   %x       %x     %d%%",
975                                &ac, &status, &flag, &life);
976
977                         if (life == -1) {
978                                 /* could check now that there is ac */
979                                 snprintf(last_battery_str, 64, "AC");
980                         } else if (ac && life != 100) { /* could check that status==3 here? */
981                                 snprintf(last_battery_str, 64,
982                                          "charging %d%%", life);
983                         } else {
984                                 snprintf(last_battery_str, 64, "%d%%",
985                                          life);
986                         }
987
988                         /* it seemed to buffer it so file must be closed (or could use syscalls
989                         * directly but I don't feel like coding it now) */
990                         fclose(apm_bat_fp);
991                         apm_bat_fp = NULL;
992                 }
993         }
994
995         snprintf(buf, n, "%s", last_battery_str);
996 }
997
998 void update_top()
999 {
1000         show_nice_processes = 1;
1001         process_find_top(info.tops);
1002 }