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