Conky 1.5.0 -- client/server prototype
[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 #define SHORTSTAT_TEMPL "%*s %llu %llu %llu"
33 #define LONGSTAT_TEMPL "%*s %llu %llu %llu "
34
35 #ifdef HAVE_LIBDEXTER
36 /* need the procraw service from the libdexter dxt-sysinfo plugin */
37 #include <dxt-sysinfo/procraw-public.h>
38 #define PROCRAW_SERVICE_UUID "ce975a10-0e52-458a-a4b9-253734760436"
39 /* timed sampler that delivers the procraw data */
40 static DexterTimedSampler *procraw_sampler = NULL;
41 /* procraw structure used as /proc surrogate */
42 static DxtSysinfoProcrawData procraw_data;
43 /* data selector mask for the service */
44 gint procraw_mask;
45 #endif
46
47 static int show_nice_processes;
48
49 /* this flags tells the linux routines to use the /proc system
50  * where possible, even if other api's are available, e.g. sysinfo() 
51  * or getloadavg(). the reason for this is to allow for /proc-based 
52  * distributed monitoring. using a flag in this manner creates less
53  * confusing code.
54  */
55 static int prefer_proc = 0;
56
57 void prepare_update()
58 {
59 }
60
61 void update_uptime()
62 {
63 #ifdef HAVE_SYSINFO
64   if (!prefer_proc)
65   {
66     struct sysinfo s_info;
67     sysinfo(&s_info);
68     info.uptime = (double) s_info.uptime;
69   }
70   else
71 #endif
72   {
73           static int rep = 0;
74           FILE *fp;
75   
76     if (!(fp = open_file("/proc/uptime", &rep)))
77     {
78       return;
79     }
80           fscanf(fp, "%lf", &info.uptime);
81           fclose(fp);
82   }
83   info.mask |= (1 << INFO_UPTIME);
84 }
85
86 /* these things are also in sysinfo except Buffers:, that's why I'm reading
87 * them from proc */
88
89 void update_meminfo()
90 {
91   FILE *meminfo_fp;
92         static int rep = 0;
93         /*  unsigned int a; */
94         char buf[256];
95
96         info.mem = info.memmax = info.swap = info.swapmax = info.bufmem =
97             info.buffers = info.cached = 0;
98
99   if (!(meminfo_fp = open_file("/proc/meminfo", &rep)))
100       return;
101
102         while (!feof(meminfo_fp)) {
103                 if (fgets(buf, 255, meminfo_fp) == NULL)
104                         break;
105
106                 if (strncmp(buf, "MemTotal:", 9) == 0) {
107                         sscanf(buf, "%*s %Lu", &info.memmax);
108                 } else if (strncmp(buf, "MemFree:", 8) == 0) {
109                         sscanf(buf, "%*s %Lu", &info.mem);
110                 } else if (strncmp(buf, "SwapTotal:", 10) == 0) {
111                         sscanf(buf, "%*s %Lu", &info.swapmax);
112                 } else if (strncmp(buf, "SwapFree:", 9) == 0) {
113                         sscanf(buf, "%*s %Lu", &info.swap);
114                 } else if (strncmp(buf, "Buffers:", 8) == 0) {
115                         sscanf(buf, "%*s %Lu", &info.buffers);
116                 } else if (strncmp(buf, "Cached:", 7) == 0) {
117                         sscanf(buf, "%*s %Lu", &info.cached);
118                 }
119         }
120         
121         info.mem = info.memmax - info.mem;
122         info.swap = info.swapmax - info.swap;
123
124         info.bufmem = info.cached + info.buffers;
125
126         info.mask |= (1 << INFO_MEM) | (1 << INFO_BUFFERS);
127
128   fclose (meminfo_fp);
129 }
130
131 static FILE *net_wireless_fp;
132
133 inline void update_net_stats()
134 {
135   FILE *net_dev_fp;
136         static int rep = 0;
137         // FIXME: arbitrary size chosen to keep code simple.
138         int i, i2;
139         unsigned int curtmp1, curtmp2;
140         unsigned int k;
141         struct ifconf conf;
142         char buf[256];
143         double delta;
144
145         /* get delta */
146         delta = current_update_time - last_update_time;
147         if (delta <= 0.0001)
148                 return;
149
150         /* open file and ignore first two lines */
151   if (!(net_dev_fp = open_file("/proc/net/dev", &rep)))
152     return;
153
154         fgets(buf, 255, net_dev_fp);    /* garbage */
155         fgets(buf, 255, net_dev_fp);    /* garbage (field names) */
156
157         /* read each interface */
158         for (i2 = 0; i2 < 16; i2++) {
159                 struct net_stat *ns;
160                 char *s, *p;
161                 long long r, t, last_recv, last_trans;
162
163                 if (fgets(buf, 255, net_dev_fp) == NULL) {
164                         break;
165                 }
166                 p = buf;
167                 while (isspace((int) *p))
168                         p++;
169
170                 s = p;
171
172                 while (*p && *p != ':')
173                         p++;
174                 if (*p == '\0')
175                         continue;
176                 *p = '\0';
177                 p++;
178
179                 ns = get_net_stat(s);
180                 ns->up = 1;
181                 memset(&(ns->addr.sa_data), 0, 14);
182                 last_recv = ns->recv;
183                 last_trans = ns->trans;
184
185                 sscanf(p,
186                        /* bytes packets errs drop fifo frame compressed multicast|bytes ... */
187                        "%Ld  %*d     %*d  %*d  %*d  %*d   %*d        %*d       %Ld",
188                        &r, &t);
189
190                 /* if recv or trans is less than last time, an overflow happened */
191
192                 if (r < ns->last_read_recv)
193                         ns->recv +=
194                             ((long long) 4294967295U -
195                              ns->last_read_recv) + r;
196                 else
197                         ns->recv += (r - ns->last_read_recv);
198                 ns->last_read_recv = r;
199
200                 if (t < ns->last_read_trans)
201                         ns->trans +=
202                             ((long long) 4294967295U -
203                              ns->last_read_trans) + t;
204                 else
205                         ns->trans += (t - ns->last_read_trans);
206                 ns->last_read_trans = t;
207
208                 /*** ip addr patch ***/
209                 i = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
210
211                 conf.ifc_buf = malloc(sizeof(struct ifreq) * 16);
212
213                 conf.ifc_len = sizeof(struct ifreq) * 16;
214
215                 ioctl((long) i, SIOCGIFCONF, &conf);
216
217                 for (k = 0; k < conf.ifc_len / sizeof(struct ifreq); k++) {
218                         struct net_stat *ns;
219                         ns = get_net_stat(((struct ifreq *) conf.
220                                            ifc_buf)[k].ifr_ifrn.ifrn_name);
221                         ns->addr =
222                             ((struct ifreq *) conf.ifc_buf)[k].ifr_ifru.
223                             ifru_addr;
224                 }
225
226                 close((long) i);
227
228                 free(conf.ifc_buf);
229
230
231                 /*** end ip addr patch ***/
232
233
234                 /* calculate speeds */
235                 ns->net_rec[0] = (ns->recv - last_recv) / delta;
236                 ns->net_trans[0] = (ns->trans - last_trans) / delta;
237                 curtmp1 = 0;
238                 curtmp2 = 0;
239                 // get an average
240                 for (i = 0; (unsigned) i < info.net_avg_samples; i++) {
241                         curtmp1 += ns->net_rec[i];
242                         curtmp2 += ns->net_trans[i];
243                 }
244                 ns->recv_speed = curtmp1 / (double) info.net_avg_samples;
245                 ns->trans_speed = curtmp2 / (double) info.net_avg_samples;
246                 if (info.net_avg_samples > 1) {
247                         for (i = info.net_avg_samples; i > 1; i--) {
248                                 ns->net_rec[i - 1] = ns->net_rec[i - 2];
249                                 ns->net_trans[i - 1] =
250                                     ns->net_trans[i - 2];
251                         }
252                 }
253
254         }
255
256         fclose(net_dev_fp);
257
258   info.mask |= (1 << INFO_NET);
259 }
260
261 inline void update_wifi_stats()
262 {
263         /** wireless stats patch by Bobby Beckmann **/
264         static int rep = 0;
265         int i;
266         char buf[256];
267         /*open file and ignore first two lines       sorry, this code sucks ass right now, i'll clean it up later */
268         if (net_wireless_fp == NULL)
269                 net_wireless_fp = open_file("/proc/net/wireless", &rep);
270         else
271                 fseek(net_wireless_fp, 0, SEEK_SET);
272         if (net_wireless_fp == NULL)
273                 return;
274
275         fgets(buf, 255, net_wireless_fp);       /* garbage */
276         fgets(buf, 255, net_wireless_fp);       /* garbage (field names) */
277
278         /* read each interface */
279         for (i = 0; i < 16; i++) {
280                 struct net_stat *ns;
281                 char *s, *p;
282                 int l, m, n;
283
284                 if (fgets(buf, 255, net_wireless_fp) == NULL)
285                         break;
286                 p = buf;
287                 while (isspace((int) *p))
288                         p++;
289
290                 s = p;
291
292                 while (*p && *p != ':')
293                         p++;
294                 if (*p == '\0')
295                         continue;
296                 *p = '\0';
297                 p++;
298
299                 ns = get_net_stat(s);
300
301                 sscanf(p, "%*d   %d.  %d.  %d", &l, &m, &n);
302
303                 ns->linkstatus = (int) (log(MIN(MAX(l,1),92)) / log(92) * 100);
304
305         }
306
307         /*** end wireless patch ***/
308 }
309
310 int result;
311
312 void update_total_processes()
313 {
314 #ifdef HAVE_SYSINFO
315   if (!prefer_proc)
316   {
317     struct sysinfo s_info;
318     sysinfo(&s_info);
319     info.procs = s_info.procs;
320   }
321   else
322 #endif
323   {
324     static int rep = 0;
325     FILE *fp;
326
327     if (!(fp = open_file("/proc/loadavg", &rep)))
328     {
329       return;
330     }
331     fscanf(fp, "%*f %*f %*f %*d/%hd", &info.procs );
332     fclose(fp);
333   }
334   info.mask |= (1 << INFO_PROCS);
335 }
336
337 #define CPU_SAMPLE_COUNT 15
338 struct cpu_info {
339         unsigned long long cpu_user;
340         unsigned long long cpu_system;
341         unsigned long long cpu_nice;
342         unsigned long long cpu_idle;
343         unsigned long long cpu_iowait;
344         unsigned long long cpu_irq;
345         unsigned long long cpu_softirq;
346         unsigned long long cpu_steal;
347         unsigned long long cpu_total;
348         unsigned long long cpu_active_total;
349         unsigned long long cpu_last_total;
350         unsigned long long cpu_last_active_total;
351         double cpu_val[CPU_SAMPLE_COUNT];
352 };
353 static short cpu_setup = 0;
354
355 /* 
356    determine if this kernel gives us "extended" statistics information in /proc/stat. 
357    Kernels around 2.5 and earlier only reported user, system, nice and idle values in proc stat. 
358    Kernels around 2.6 and greater report these PLUS iowait, irq, softirq, and steal 
359 */
360 void determine_longstat(char * buf) { 
361         unsigned long long iowait=0;
362         KFLAG_SETOFF(KFLAG_IS_LONGSTAT);        
363         /* scanf will either return -1 or 1 because there is only 1 assignment  */
364         if (sscanf(buf, "%*s %*d %*d %*d %*d %llu",&iowait)>0) KFLAG_SETON(KFLAG_IS_LONGSTAT);
365 }
366
367 void get_cpu_count()
368 {
369   FILE *stat_fp;
370   static int rep = 0;
371
372         if (info.cpu_usage) {
373                 return;
374         }
375         char buf[256];
376
377   if (!(stat_fp = open_file("/proc/stat", &rep)))
378     return;
379
380         info.cpu_count = 0;
381
382         while (!feof(stat_fp)) {
383                 if (fgets(buf, 255, stat_fp) == NULL)
384                         break;
385
386                 if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) {
387                         if (info.cpu_count == 0) {
388                                 determine_longstat(buf);
389                         }
390                         info.cpu_count++;
391                 }
392         }
393         info.cpu_usage = malloc((info.cpu_count + 1) * sizeof(float));
394
395   fclose (stat_fp);
396 }
397
398 #define TMPL_LONGSTAT "%*s %llu %llu %llu %llu %llu %llu %llu %llu"
399 #define TMPL_SHORTSTAT "%*s %llu %llu %llu %llu"
400
401 inline static void update_stat()
402 {
403   FILE *stat_fp;
404   static int rep = 0;
405         static struct cpu_info *cpu = NULL;
406         char buf[256];
407         unsigned int i;
408         unsigned int index;
409         double curtmp;
410         char * stat_template=NULL; 
411         unsigned int malloc_cpu_size=0;
412         
413
414         /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
415         if (!cpu_setup || !info.cpu_usage) {
416                 get_cpu_count();
417                 cpu_setup = 1;
418         }
419
420         if (!stat_template) {
421                 stat_template = KFLAG_ISSET(KFLAG_IS_LONGSTAT) ? TMPL_LONGSTAT : TMPL_SHORTSTAT ;
422         }       
423
424         if (!cpu) {
425                 malloc_cpu_size = (info.cpu_count + 1) * sizeof(struct cpu_info);
426                 cpu = malloc(malloc_cpu_size);
427                 memset(cpu, 0, malloc_cpu_size);
428         }
429
430   if (!(stat_fp = open_file("/proc/stat", &rep)))
431     return;
432
433         index = 0;
434         while (!feof(stat_fp)) {
435                 if (fgets(buf, 255, stat_fp) == NULL)
436                         break;
437
438                 if (strncmp(buf, "procs_running ", 14) == 0) {
439                         sscanf(buf, "%*s %hu", &info.run_procs);
440                         info.mask |= (1 << INFO_RUN_PROCS);
441                 } else if (strncmp(buf, "cpu", 3) == 0) {
442                         index = isdigit(buf[3]) ? ((int)buf[3]) - 0x2F : 0;
443                         sscanf(buf, stat_template 
444                                 , &(cpu[index].cpu_user)
445                                 , &(cpu[index].cpu_nice)
446                                 , &(cpu[index].cpu_system)
447                                 , &(cpu[index].cpu_idle)
448                                 , &(cpu[index].cpu_iowait)
449                                 , &(cpu[index].cpu_irq)
450                                 , &(cpu[index].cpu_softirq)
451                                 , &(cpu[index].cpu_steal)
452                                 );
453
454                         cpu[index].cpu_total = cpu[index].cpu_user 
455                                          + cpu[index].cpu_nice 
456                                          + cpu[index].cpu_system 
457                                          + cpu[index].cpu_idle 
458                                          + cpu[index].cpu_iowait 
459                                          + cpu[index].cpu_irq
460                                          + cpu[index].cpu_softirq
461                                          + cpu[index].cpu_steal 
462                                          ; 
463
464                         cpu[index].cpu_active_total = cpu[index].cpu_total - (cpu[index].cpu_idle + cpu[index].cpu_iowait);
465                         info.mask |= (1 << INFO_CPU);
466
467                         double delta = current_update_time - last_update_time;
468                         if (delta <= 0.001) break;      
469
470                         cpu[index].cpu_val[0] = (cpu[index].cpu_active_total -  cpu[index].cpu_last_active_total) / 
471                                                 (float )(cpu[index].cpu_total - cpu[index].cpu_last_total); 
472                         curtmp = 0;
473                         for (i=0; i < info.cpu_avg_samples; i++ ) {
474                                 curtmp += cpu[index].cpu_val[i];
475                         }
476                         /* TESTING -- I've removed this, because I don't think it is right. You shouldn't divide 
477                                       by the cpu count here ... removing for testing */
478                         /* if (index == 0) {
479                                 info.cpu_usage[index] = curtmp / info.cpu_avg_samples / info.cpu_count; 
480                         } else {
481                                 info.cpu_usage[index] = curtmp / info.cpu_avg_samples;
482                         }  */
483                         /* TESTING -- this line replaces the prev. "suspect" if/else */
484                         info.cpu_usage[index] = curtmp / info.cpu_avg_samples;
485
486                         cpu[index].cpu_last_total = cpu[index].cpu_total;
487                         cpu[index].cpu_last_active_total = cpu[index].cpu_active_total;
488                         for (i = info.cpu_avg_samples - 1; i > 0; i--) {
489                                 cpu[index].cpu_val[i] = cpu[index].cpu_val[i - 1];
490                         }
491                 }
492
493         }
494   fclose (stat_fp);
495 }
496
497 void update_running_processes()
498 {
499         update_stat();
500 }
501
502 void update_cpu_usage()
503 {
504         update_stat();
505 }
506
507 void update_load_average()
508 {
509 #ifdef HAVE_GETLOADAVG
510   if (!prefer_proc)
511   {
512           double v[3];
513           getloadavg(v, 3);
514           info.loadavg[0] = (float) v[0];
515           info.loadavg[1] = (float) v[1];
516           info.loadavg[2] = (float) v[2];
517   }
518   else
519 #endif
520   {
521     static int rep = 0;
522     FILE *fp;
523
524     if (!(fp = open_file("/proc/loadavg", &rep)))
525     {
526       info.loadavg[0] = info.loadavg[1] = info.loadavg[2] = 0.0;
527       return;
528     }
529     fscanf(fp, "%f %f %f", &info.loadavg[0], &info.loadavg[1], &info.loadavg[2]);
530     fclose(fp);
531   }
532   info.mask |= (1 << INFO_LOADAVG);
533 }
534
535 #define PROC_I8K "/proc/i8k"
536 #define I8K_DELIM " "
537 static char *i8k_procbuf = NULL;
538 void update_i8k()
539 {
540         FILE *fp;
541         if (!i8k_procbuf) {
542                 i8k_procbuf = (char*)malloc(128*sizeof(char));
543         }
544         if ((fp = fopen(PROC_I8K,"r")) == NULL) {
545                 CRIT_ERR("/proc/i8k doesn't exist! use insmod to make sure the kernel driver is loaded...");
546         }
547
548         memset(&i8k_procbuf[0],0,128);
549         if (fread(&i8k_procbuf[0],sizeof(char),128,fp) == 0) {
550                 ERR("something wrong with /proc/i8k...");
551         }
552
553         fclose(fp);
554
555   i8k.version = strtok(&i8k_procbuf[0],I8K_DELIM);
556         i8k.bios = strtok(NULL,I8K_DELIM);
557         i8k.serial = strtok(NULL,I8K_DELIM);
558         i8k.cpu_temp = strtok(NULL,I8K_DELIM);
559         i8k.left_fan_status = strtok(NULL,I8K_DELIM);   
560         i8k.right_fan_status = strtok(NULL,I8K_DELIM);  
561         i8k.left_fan_rpm = strtok(NULL,I8K_DELIM);
562         i8k.right_fan_rpm = strtok(NULL,I8K_DELIM);
563         i8k.ac_status = strtok(NULL,I8K_DELIM);
564         i8k.buttons_status = strtok(NULL,I8K_DELIM);
565 }
566
567
568 /***********************************************************/
569 /***********************************************************/
570 /***********************************************************/
571
572 static int no_dots(const struct dirent *d)
573 {
574         if (d->d_name[0] == '.')
575                 return 0;
576         return 1;
577 }
578
579 static int
580 get_first_file_in_a_directory(const char *dir, char *s, int *rep)
581 {
582         struct dirent **namelist;
583         int i, n;
584
585         n = scandir(dir, &namelist, no_dots, alphasort);
586         if (n < 0) {
587                 if (!rep || !*rep) {
588                         ERR("scandir for %s: %s", dir, strerror(errno));
589                         if (rep)
590                                 *rep = 1;
591                 }
592                 return 0;
593         } else {
594                 if (n == 0)
595                         return 0;
596
597                 strncpy(s, namelist[0]->d_name, 255);
598                 s[255] = '\0';
599
600                 for (i = 0; i < n; i++)
601                         free(namelist[i]);
602                 free(namelist);
603
604                 return 1;
605         }
606 }
607
608 #define I2C_DIR "/sys/bus/i2c/devices/"
609
610 int
611 open_i2c_sensor(const char *dev, const char *type, int n, int *div,
612                 char *devtype)
613 {
614         char path[256];
615         char buf[256];
616         int fd;
617         int divfd;
618
619         /* if i2c device is NULL or *, get first */
620         if (dev == NULL || strcmp(dev, "*") == 0) {
621                 static int rep = 0;
622                 if (!get_first_file_in_a_directory(I2C_DIR, buf, &rep))
623                         return -1;
624                 dev = buf;
625         }
626
627         /* change vol to in */
628         if (strcmp(type, "vol") == 0)
629                 type = "in";
630
631         if (strcmp(type, "tempf") == 0) {
632                 snprintf(path, 255, I2C_DIR "%s/%s%d_input", dev, "temp", n);
633         } else {
634                 snprintf(path, 255, I2C_DIR "%s/%s%d_input", dev, type, n);
635         }
636         strncpy(devtype, path, 255);
637
638         /* open file */
639         fd = open(path, O_RDONLY);
640         if (fd < 0) {
641                 CRIT_ERR("can't open '%s': %s\nplease fix i2c or remove it from Conky", path, strerror(errno));
642         }
643
644         if (strcmp(type, "in") == 0 || strcmp(type, "temp") == 0
645             || strcmp(type, "tempf") == 0)
646                 *div = 1;
647         else
648                 *div = 0;
649         /* fan does not use *_div as a read divisor */
650         if (strcmp("fan", type) == 0)
651                 return fd;
652
653         /* test if *_div file exist, open it and use it as divisor */
654         if (strcmp(type, "tempf") == 0) {
655                 snprintf(path, 255, I2C_DIR "%s/%s%d_div", "one", "two",
656                          n);
657         } else {
658                 snprintf(path, 255, I2C_DIR "%s/%s%d_div", dev, type, n);
659         }
660
661         divfd = open(path, O_RDONLY);
662         if (divfd > 0) {
663                 /* read integer */
664                 char divbuf[64];
665                 unsigned int divn;
666                 divn = read(divfd, divbuf, 63);
667                 /* should read until n == 0 but I doubt that kernel will give these
668                  * in multiple pieces. :) */
669                 divbuf[divn] = '\0';
670                 *div = atoi(divbuf);
671         }
672
673         close(divfd);
674
675         return fd;
676 }
677
678 double get_i2c_info(int *fd, int div, char *devtype, char *type)
679 {
680         int val = 0;
681
682         if (*fd <= 0)
683                 return 0;
684
685         lseek(*fd, 0, SEEK_SET);
686
687         /* read integer */
688         {
689                 char buf[64];
690                 unsigned int n;
691                 n = read(*fd, buf, 63);
692                 /* should read until n == 0 but I doubt that kernel will give these
693                  * in multiple pieces. :) */
694                 buf[n] = '\0';
695                 val = atoi(buf);
696         }
697
698         close(*fd);
699         /* open file */
700         *fd = open(devtype, O_RDONLY);
701         if (*fd < 0)
702                 ERR("can't open '%s': %s", devtype, strerror(errno));
703
704         /* My dirty hack for computing CPU value 
705          * Filedil, from forums.gentoo.org
706          */
707 /*      if (strstr(devtype, "temp1_input") != NULL)
708         return -15.096+1.4893*(val / 1000.0); */
709
710
711         /* divide voltage and temperature by 1000 */
712         /* or if any other divisor is given, use that */
713         if (strcmp(type, "tempf") == 0) {
714                 if (div > 1)
715                         return ((val / div + 40) * 9.0 / 5) - 40;
716                 else if (div)
717                         return ((val / 1000.0 + 40) * 9.0 / 5) - 40;
718                 else
719                         return ((val + 40) * 9.0 / 5) - 40;
720         } else {
721                 if (div > 1)
722                         return val / div;
723                 else if (div)
724                         return val / 1000.0;
725                 else
726                         return val;
727         }
728 }
729
730 /* Prior to kernel version 2.6.12, the CPU fan speed was available
731  * in ADT746X_FAN_OLD, whereas later kernel versions provide this
732  * information in ADT746X_FAN.
733  */
734 #define ADT746X_FAN "/sys/devices/temperatures/sensor1_fan_speed"
735 #define ADT746X_FAN_OLD "/sys/devices/temperatures/cpu_fan_speed"
736
737 void get_adt746x_fan( char * p_client_buffer, size_t client_buffer_size )
738 {
739         static int rep = 0;
740         char adt746x_fan_state[64];
741         FILE *fp;
742
743         if ( !p_client_buffer || client_buffer_size <= 0 )
744                 return;
745
746         if ((fp = open_file(ADT746X_FAN, &rep)) == NULL
747                  && (fp = open_file(ADT746X_FAN_OLD, &rep)) == NULL)
748
749         {
750                 sprintf(adt746x_fan_state, "adt746x not found");
751         }
752         else
753         {
754                 fgets(adt746x_fan_state, sizeof(adt746x_fan_state), fp);
755                 adt746x_fan_state[strlen(adt746x_fan_state) - 1] = 0;
756                 fclose(fp);
757         }
758
759         snprintf( p_client_buffer, client_buffer_size, "%s", adt746x_fan_state );
760         return;
761 }
762
763 /* Prior to kernel version 2.6.12, the CPU temperature was found
764  * in ADT746X_CPU_OLD, whereas later kernel versions provide this
765  * information in ADT746X_CPU.
766  */
767 #define ADT746X_CPU "/sys/devices/temperatures/sensor1_temperature"
768 #define ADT746X_CPU_OLD "/sys/devices/temperatures/cpu_temperature"
769
770 void get_adt746x_cpu( char * p_client_buffer, size_t client_buffer_size )
771 {
772         static int rep = 0;
773         char adt746x_cpu_state[64];
774         FILE *fp;
775
776         if ( !p_client_buffer || client_buffer_size <= 0 )
777                 return;
778         
779         if ((fp = open_file(ADT746X_CPU, &rep)) == NULL
780                  && (fp = open_file(ADT746X_CPU_OLD, &rep)) == NULL)
781         {
782                 sprintf(adt746x_cpu_state, "adt746x not found");
783         }
784         else
785         {
786                 fscanf(fp, "%2s", adt746x_cpu_state);
787                 fclose(fp);
788         }
789
790         snprintf( p_client_buffer, client_buffer_size, "%s", adt746x_cpu_state ); 
791         return;
792 }
793
794 /* Thanks to "Walt Nelson" <wnelsonjr@comcast.net> */
795
796 /***********************************************************************/
797 /*
798  *  This file is part of x86info.
799  *  (C) 2001 Dave Jones.
800  *
801  *  Licensed under the terms of the GNU GPL License version 2.
802  *
803  * Estimate CPU MHz routine by Andrea Arcangeli <andrea@suse.de>
804  * Small changes by David Sterba <sterd9am@ss1000.ms.mff.cuni.cz>
805  *
806  */
807 #if  defined(__i386) || defined(__x86_64)
808 __inline__ unsigned long long int rdtsc()
809 {
810         unsigned long long int x;
811         __asm__ volatile (".byte 0x0f, 0x31":"=A" (x));
812         return x;
813 }
814 #endif
815
816 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
817 void get_freq_dynamic( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor )
818 {
819 #if  defined(__i386) || defined(__x86_64)
820         struct timezone tz;
821         struct timeval tvstart, tvstop;
822         unsigned long long cycles[2];   /* gotta be 64 bit */
823         unsigned int microseconds;      /* total time taken */
824
825         if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 )
826              return;
827
828         memset(&tz, 0, sizeof(tz));
829
830         /* get this function in cached memory */
831         gettimeofday(&tvstart, &tz);
832         cycles[0] = rdtsc();
833         gettimeofday(&tvstart, &tz);
834
835         /* we don't trust that this is any specific length of time */
836         usleep(100);
837         cycles[1] = rdtsc();
838         gettimeofday(&tvstop, &tz);
839         microseconds = ((tvstop.tv_sec - tvstart.tv_sec) * 1000000) +
840             (tvstop.tv_usec - tvstart.tv_usec);
841
842         snprintf( p_client_buffer, client_buffer_size, p_format, (float)((cycles[1] - cycles[0]) / microseconds) / divisor );
843         return;
844 #else
845 /* FIXME: hardwired: get freq for first cpu!
846    this whole function needs to be rethought and redone for
847    multi-cpu/multi-core/multi-threaded environments and 
848    arbitrary combinations thereof 
849 */
850         get_freq( p_client_buffer, client_buffer_size, p_format, divisor, 1 );
851         return;
852 #endif
853 }
854
855
856 #define CPUFREQ_PREFIX "/sys/devices/system/cpu"
857 #define CPUFREQ_POSTFIX "cpufreq/scaling_cur_freq"
858
859 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
860 char get_freq( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor, unsigned int cpu )
861 {
862         FILE *f;
863   static int rep = 0;
864         char frequency[32];
865         char s[256];
866         double freq = 0;
867         
868         if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 )
869                 return 0;
870         
871   if (!prefer_proc)
872   {
873     char current_freq_file[128];
874     snprintf(current_freq_file, 127, "%s/cpu%d/%s",CPUFREQ_PREFIX, cpu-1, CPUFREQ_POSTFIX);
875           f = fopen(current_freq_file, "r");
876           if (f) 
877     {
878                   /* if there's a cpufreq /sys node, read the current frequency from this node;
879                    * divide by 1000 to get Mhz. */
880                   if (fgets(s, sizeof(s), f)) {
881                             s[strlen(s)-1] = '\0';
882                             freq = strtod(s, NULL);
883                   }
884                   fclose(f);
885                   snprintf( p_client_buffer, client_buffer_size, p_format, (freq/1000)/divisor );
886                   return 1;
887     }
888         }
889         
890         f = open_file("/proc/cpuinfo", &rep);           //open the CPU information file
891         if (!f) {
892                 perror("Conky: Failed to access '/proc/cpuinfo' at get_freq()");
893                 return 0;
894         }
895
896         while (fgets(s, sizeof(s), f) != NULL){         //read the file
897
898 #if defined(__i386) || defined(__x86_64)
899                 if (strncmp(s, "cpu MHz", 7) == 0 && cpu == 0) {        //and search for the cpu mhz
900 #else
901 #if defined(__alpha)
902                 if (strncmp(s, "cycle frequency [Hz]", 20) == 0 && cpu == 0) {          // different on alpha
903 #else
904                 if (strncmp(s, "clock", 5) == 0 && cpu == 0) {  // this is different on ppc for some reason
905 #endif // defined(__alpha)
906 #endif // defined(__i386) || defined(__x86_64)
907
908                 strcpy(frequency, strchr(s, ':') + 2);  //copy just the number
909 #if defined(__alpha)
910                 frequency[strlen(frequency) - 6] = '\0';// strip " est.\n"
911                 freq = strtod(frequency, NULL)/1000000; // kernel reports in Hz 
912 #else
913                 frequency[strlen(frequency) - 1] = '\0'; // strip \n
914                 freq = strtod(frequency, NULL);
915 #endif
916                 break;
917                 }
918                 if (strncmp(s, "processor", 9) == 0) {
919                     cpu--; 
920                     continue;
921                 }
922                 
923         }
924         
925         fclose(f);
926         snprintf( p_client_buffer, client_buffer_size, p_format, (float)freq/divisor );
927         return 1;
928 }
929
930 #define CPUFREQ_VOLTAGE "cpufreq/scaling_voltages"
931
932 /* return cpu voltage in mV (use divisor=1) or V (use divisor=1000) */
933 char get_voltage( char * p_client_buffer, size_t client_buffer_size, char * p_format, int divisor, unsigned int cpu )
934 {
935 /* /sys/devices/system/cpu/cpu0/cpufreq/scaling_voltages looks 
936    something like this:
937 # frequency voltage
938 1800000 1340
939 1600000 1292
940 1400000 1100
941 1200000 988
942 1000000 1116
943 800000 1004
944 600000 988
945 */
946
947 /* Peter Tarjan (ptarjan@citromail.hu) */
948         FILE *f;
949         char s[256];
950         int freq = 0;
951         int voltage = 0;
952         char current_freq_file[128];
953         int freq_comp = 0;
954         
955
956 /* build the voltage file name */
957         cpu--;
958         snprintf(current_freq_file, 127, "%s/cpu%d/%s",
959                  CPUFREQ_PREFIX, cpu, CPUFREQ_POSTFIX);
960
961         if ( !p_client_buffer || client_buffer_size <= 0 || !p_format || divisor <= 0 )
962                 return 0;
963
964         /* read the current cpu frequency from the /sys node */
965         f = fopen(current_freq_file, "r");
966         if (f) {
967             if (fgets(s, sizeof(s), f)) {
968                 s[strlen(s)-1] = '\0';
969                 freq = strtod(s, NULL);
970             }
971             fclose(f);
972         } else {
973                 fprintf(stderr, "Conky: Failed to access '%s' at ", current_freq_file);
974                 perror("get_voltage()");
975                 if (f) {
976                         fclose(f);
977                 }
978                 return 0;
979             }
980
981         snprintf(current_freq_file, 127, "%s/cpu%d/%s",
982                  CPUFREQ_PREFIX, cpu, CPUFREQ_VOLTAGE);
983
984 /* use the current cpu frequency to find the corresponding voltage */
985         f = fopen(current_freq_file, "r");
986
987         if (f) {
988                 while (!feof(f)) {
989                         char line[256];
990                         if (fgets(line, 255, f) == NULL) break;
991                         sscanf(line, "%d %d", &freq_comp, &voltage);
992                         if(freq_comp == freq) break;
993                 }
994                 fclose(f);
995         } else {
996                 fprintf(stderr, "Conky: Failed to access '%s' at ", current_freq_file);
997                 perror("get_voltage()");
998                 if (f) {
999                         fclose(f);
1000                 }
1001                 return 0;
1002         }
1003         snprintf( p_client_buffer, client_buffer_size, p_format, (float)voltage/divisor );
1004         return 1;
1005
1006 }
1007
1008 #define ACPI_FAN_DIR "/proc/acpi/fan/"
1009
1010 void get_acpi_fan( char * p_client_buffer, size_t client_buffer_size )
1011 {
1012         static int rep = 0;
1013         char buf[256];
1014         char buf2[256];
1015         FILE *fp;
1016
1017         if ( !p_client_buffer || client_buffer_size <= 0 )
1018                 return;
1019
1020         /* yeah, slow... :/ */
1021         if (!get_first_file_in_a_directory(ACPI_FAN_DIR, buf, &rep))
1022         {
1023                 snprintf( p_client_buffer, client_buffer_size, "no fans?" );
1024                 return;
1025         }
1026
1027         snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_FAN_DIR, buf );
1028
1029         fp = open_file(buf2, &rep);
1030         if (!fp) {
1031                 snprintf( p_client_buffer, client_buffer_size, "can't open fan's state file" );
1032                 return;
1033         }
1034         memset(buf,0,sizeof(buf));
1035         fscanf(fp, "%*s %99s", buf);
1036         fclose(fp);
1037
1038         snprintf( p_client_buffer, client_buffer_size, "%s", buf );
1039
1040         return;
1041 }
1042
1043 #define ACPI_AC_ADAPTER_DIR "/proc/acpi/ac_adapter/"
1044
1045 void get_acpi_ac_adapter( char * p_client_buffer, size_t client_buffer_size )
1046 {
1047         static int rep = 0;
1048         char buf[256];
1049         char buf2[256];
1050         FILE *fp;
1051
1052         if ( !p_client_buffer || client_buffer_size <= 0 )
1053                 return;
1054
1055         /* yeah, slow... :/ */
1056         if (!get_first_file_in_a_directory(ACPI_AC_ADAPTER_DIR, buf, &rep))
1057         {
1058                 snprintf( p_client_buffer, client_buffer_size, "no ac_adapters?" );
1059                 return; 
1060         }
1061
1062         snprintf(buf2, sizeof(buf2), "%s%s/state", ACPI_AC_ADAPTER_DIR, buf );
1063          
1064
1065         fp = open_file(buf2, &rep);
1066         if (!fp) {
1067                 snprintf( p_client_buffer, client_buffer_size, "No ac adapter found.... where is it?" );
1068                 return;
1069         }
1070         memset(buf,0,sizeof(buf));
1071         fscanf(fp, "%*s %99s", buf );
1072         fclose(fp);
1073
1074         snprintf( p_client_buffer, client_buffer_size, "%s", buf );
1075
1076         return;
1077 }
1078
1079 /*
1080 /proc/acpi/thermal_zone/THRM/cooling_mode
1081 cooling mode:            active
1082 /proc/acpi/thermal_zone/THRM/polling_frequency
1083 <polling disabled>
1084 /proc/acpi/thermal_zone/THRM/state
1085 state:                   ok
1086 /proc/acpi/thermal_zone/THRM/temperature
1087 temperature:             45 C
1088 /proc/acpi/thermal_zone/THRM/trip_points
1089 critical (S5):           73 C
1090 passive:                 73 C: tc1=4 tc2=3 tsp=40 devices=0xcdf6e6c0
1091 */
1092
1093 #define ACPI_THERMAL_DIR "/proc/acpi/thermal_zone/"
1094 #define ACPI_THERMAL_FORMAT "/proc/acpi/thermal_zone/%s/temperature"
1095
1096 int open_acpi_temperature(const char *name)
1097 {
1098         char path[256];
1099         char buf[256];
1100         int fd;
1101
1102         if (name == NULL || strcmp(name, "*") == 0) {
1103                 static int rep = 0;
1104                 if (!get_first_file_in_a_directory
1105                     (ACPI_THERMAL_DIR, buf, &rep))
1106                         return -1;
1107                 name = buf;
1108         }
1109
1110         snprintf(path, 255, ACPI_THERMAL_FORMAT, name);
1111
1112         fd = open(path, O_RDONLY);
1113         if (fd < 0)
1114                 ERR("can't open '%s': %s", path, strerror(errno));
1115
1116         return fd;
1117 }
1118
1119 static double last_acpi_temp;
1120 static double last_acpi_temp_time;
1121
1122 double get_acpi_temperature(int fd)
1123 {
1124         if (fd <= 0)
1125                 return 0;
1126
1127         /* don't update acpi temperature too often */
1128         if (current_update_time - last_acpi_temp_time < 11.32) {
1129                 return last_acpi_temp;
1130         }
1131         last_acpi_temp_time = current_update_time;
1132
1133         /* seek to beginning */
1134         lseek(fd, 0, SEEK_SET);
1135
1136         /* read */
1137         {
1138                 char buf[256];
1139                 int n;
1140                 n = read(fd, buf, 255);
1141                 if (n < 0)
1142                         ERR("can't read fd %d: %s", fd, strerror(errno));
1143                 else {
1144                         buf[n] = '\0';
1145                         sscanf(buf, "temperature: %lf", &last_acpi_temp);
1146                 }
1147         }
1148
1149         return last_acpi_temp;
1150 }
1151
1152 /*
1153 hipo@lepakko hipo $ cat /proc/acpi/battery/BAT1/info 
1154 present:                 yes
1155 design capacity:         4400 mAh
1156 last full capacity:      4064 mAh
1157 battery technology:      rechargeable
1158 design voltage:          14800 mV
1159 design capacity warning: 300 mAh
1160 design capacity low:     200 mAh
1161 capacity granularity 1:  32 mAh
1162 capacity granularity 2:  32 mAh
1163 model number:            02KT
1164 serial number:           16922
1165 battery type:            LION
1166 OEM info:                SANYO
1167 */
1168
1169 /*
1170 hipo@lepakko conky $ cat /proc/acpi/battery/BAT1/state
1171 present:                 yes
1172 capacity state:          ok
1173 charging state:          unknown
1174 present rate:            0 mA
1175 remaining capacity:      4064 mAh
1176 present voltage:         16608 mV
1177 */
1178
1179 /*
1180 2213<@jupet kellari ö> jupet@lagi-unstable:~$ cat /proc/apm 
1181 2213<@jupet kellari ö> 1.16 1.2 0x03 0x01 0xff 0x10 -1% -1 ?
1182 2213<@jupet kellari ö> (-1 ollee ei akkua kiinni, koska akku on pöydällä)
1183 2214<@jupet kellari ö> jupet@lagi-unstable:~$ cat /proc/apm 
1184 2214<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x03 0x09 98% -1 ?
1185
1186 2238<@jupet kellari ö> 1.16 1.2 0x03 0x00 0x00 0x01 100% -1 ? ilman verkkovirtaa
1187 2239<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x00 0x01 99% -1 ? verkkovirralla
1188
1189 2240<@jupet kellari ö> 1.16 1.2 0x03 0x01 0x03 0x09 100% -1 ? verkkovirralla ja monitori päällä
1190 2241<@jupet kellari ö> 1.16 1.2 0x03 0x00 0x00 0x01 99% -1 ? monitori päällä mutta ilman verkkovirtaa
1191 */
1192
1193 #define ACPI_BATTERY_BASE_PATH "/proc/acpi/battery"
1194 #define APM_PATH "/proc/apm"
1195
1196 static FILE *acpi_bat_fp;
1197 static FILE *apm_bat_fp;
1198
1199 static int acpi_last_full;
1200
1201 static char last_battery_str[64];       /* e.g. "charging 75%" */
1202 static char last_battery_time_str[64];  /* e.g. "3h 15m" */
1203
1204 static double last_battery_time;
1205
1206 void get_battery_stuff(char *buf, unsigned int n, const char *bat, int item)
1207 {
1208         static int rep = 0, rep2 = 0;
1209         char acpi_path[128];
1210         snprintf(acpi_path, 127, ACPI_BATTERY_BASE_PATH "/%s/state", bat);
1211
1212         /* don't update battery too often */
1213         if (current_update_time - last_battery_time < 29.5) 
1214                 goto set_return_value;  
1215
1216         last_battery_time = current_update_time;
1217
1218         memset (last_battery_str, 0, sizeof (last_battery_str));
1219         memset (last_battery_time_str, 0, sizeof (last_battery_time_str));
1220
1221         /* first try ACPI */
1222
1223         if (acpi_bat_fp == NULL && apm_bat_fp == NULL)
1224                 acpi_bat_fp = open_file(acpi_path, &rep);
1225
1226         if (acpi_bat_fp != NULL) {
1227                 int present_rate = -1;
1228                 int remaining_capacity = -1;
1229                 char charging_state[64];
1230                 char present[4];
1231
1232                 /* read last full capacity if it's zero */
1233                 if (acpi_last_full == 0) {
1234                         static int rep = 0;
1235       char path[128];
1236                         FILE *fp;
1237                         snprintf(path, 127,
1238                                  ACPI_BATTERY_BASE_PATH "/%s/info", bat);
1239                         fp = open_file(path, &rep);
1240                         if (fp != NULL) {
1241                                 while (!feof(fp)) {
1242                                         char b[256];
1243                                         if (fgets(b, 256, fp) == NULL)
1244                                                 break;
1245                                         if (sscanf(b, "last full capacity: %d", &acpi_last_full) != 0) {
1246                                                 break;
1247                                         }
1248                                 }
1249
1250                                 fclose(fp);
1251                         }
1252                 }
1253
1254                 fseek(acpi_bat_fp, 0, SEEK_SET);
1255
1256                 strcpy(charging_state, "unknown");
1257
1258                 while (!feof(acpi_bat_fp)) {
1259                         char buf[256];
1260                         if (fgets(buf, 256, acpi_bat_fp) == NULL)
1261                                 break;
1262
1263                         /* let's just hope units are ok */
1264                         if (strncmp (buf, "present:", 8) == 0) 
1265                                 sscanf(buf, "present: %4s", present);
1266                         else if (strncmp (buf, "charging state:", 15) == 0)
1267                                 sscanf(buf, "charging state: %63s", charging_state);
1268                         else if (strncmp (buf, "present rate:", 13) == 0)
1269                                 sscanf(buf, "present rate: %d", &present_rate);
1270                         else if (strncmp(buf, "remaining capacity:", 19) == 0)
1271                                 sscanf(buf, "remaining capacity: %d", &remaining_capacity);
1272                 }
1273
1274                 /* Hellf[i]re notes that remaining capacity can exceed acpi_last_full */
1275                 if (remaining_capacity > acpi_last_full)
1276                         acpi_last_full = remaining_capacity;  /* normalize to 100% */
1277
1278                 /* not present */
1279                 if (strcmp(present, "no") == 0) {
1280                         strncpy(last_battery_str, "not present", 64);
1281                 }
1282                 /* charging */
1283                 else if (strcmp(charging_state, "charging") == 0) {
1284                         if (acpi_last_full != 0 && present_rate > 0) {
1285                                 /* e.g. charging 75% */
1286                                 snprintf(last_battery_str, sizeof(last_battery_str)-1, "charging %i%%", 
1287                                         (int) ((remaining_capacity * 100) / acpi_last_full));
1288                                 /* e.g. 2h 37m */
1289                                 format_seconds(last_battery_time_str, sizeof(last_battery_time_str)-1,
1290                                               (long) (((acpi_last_full - remaining_capacity) * 3600) / 
1291                                                       present_rate));
1292                         } else if (acpi_last_full != 0 && present_rate <= 0) {
1293                                 snprintf(last_battery_str, sizeof(last_battery_str)-1, "charging %d%%",
1294                                         (int) ((remaining_capacity * 100) / acpi_last_full));
1295                         } else {
1296                                 strncpy(last_battery_str, "charging", sizeof(last_battery_str)-1);
1297                         }
1298                 }
1299                 /* discharging */
1300                 else if (strncmp(charging_state, "discharging", 64) == 0) {
1301                         if (present_rate > 0) {
1302                                 /* e.g. discharging 35% */
1303                                 snprintf(last_battery_str, sizeof(last_battery_str)-1, "discharging %i%%",
1304                                         (int) ((remaining_capacity * 100) / acpi_last_full));
1305                                 /* e.g. 1h 12m */
1306                                 format_seconds(last_battery_time_str, sizeof(last_battery_time_str)-1,
1307                                               (long) ((remaining_capacity * 3600) / present_rate));
1308                         } else if (present_rate == 0) { /* Thanks to Nexox for this one */
1309                                 snprintf(last_battery_str, sizeof(last_battery_str)-1, "full");
1310                         } else {
1311                                 snprintf(last_battery_str, sizeof(last_battery_str)-1,
1312                                         "discharging %d%%",
1313                                         (int) ((remaining_capacity * 100) / acpi_last_full));
1314                         }
1315                 }
1316                 /* charged */
1317                 /* thanks to Lukas Zapletal <lzap@seznam.cz> */
1318                 else if (strncmp(charging_state, "charged", 64) == 0) {
1319                                 strcpy(last_battery_str, "charged");
1320                 } 
1321                 /* unknown, probably full / AC */
1322                 else {
1323                         if (acpi_last_full != 0
1324                             && remaining_capacity != acpi_last_full)
1325                                 snprintf(last_battery_str, 64, "unknown %d%%",
1326                                         (int) ((remaining_capacity * 100) / acpi_last_full));
1327                         else
1328                                 strncpy(last_battery_str, "AC", 64);
1329                 }
1330         } else {
1331                 /* APM */
1332                 if (apm_bat_fp == NULL)
1333                         apm_bat_fp = open_file(APM_PATH, &rep2);
1334
1335                 if (apm_bat_fp != NULL) {
1336                         int ac, status, flag, life;
1337
1338                         fscanf(apm_bat_fp,
1339                                "%*s %*s %*x %x   %x       %x     %d%%",
1340                                &ac, &status, &flag, &life);
1341
1342                         if (life == -1) {
1343                                 /* could check now that there is ac */
1344                                 snprintf(last_battery_str, 64, "AC");
1345                         } else if (ac && life != 100) { /* could check that status==3 here? */
1346                                 snprintf(last_battery_str, 64,
1347                                          "charging %d%%", life);
1348                         } else {
1349                                 snprintf(last_battery_str, 64, "%d%%",
1350                                          life);
1351                         }
1352
1353                         /* it seemed to buffer it so file must be closed (or could use syscalls
1354                          * directly but I don't feel like coding it now) */
1355                         fclose(apm_bat_fp);
1356                         apm_bat_fp = NULL;
1357                 }
1358         }
1359
1360 set_return_value:
1361         switch (item) {
1362         case BATTERY_STATUS:
1363                 {
1364                         snprintf(buf, n, "%s", last_battery_str);
1365                         break;
1366                 }
1367         case BATTERY_TIME:
1368                 {
1369                         snprintf(buf, n, "%s", last_battery_time_str);
1370                         break;
1371                 }
1372         default:
1373                         break;
1374         }              
1375         return;
1376 }
1377
1378 /* On Apple powerbook and ibook:
1379 $ cat /proc/pmu/battery_0
1380 flags      : 00000013
1381 charge     : 3623
1382 max_charge : 3720
1383 current    : 388
1384 voltage    : 16787
1385 time rem.  : 900
1386 $ cat /proc/pmu/info
1387 PMU driver version     : 2
1388 PMU firmware version   : 0c
1389 AC Power               : 1
1390 Battery count          : 1
1391 */
1392
1393 /* defines as in <linux/pmu.h> */
1394 #define PMU_BATT_PRESENT        0x00000001
1395 #define PMU_BATT_CHARGING       0x00000002
1396
1397 static FILE* pmu_battery_fp;
1398 static FILE* pmu_info_fp;
1399 static char pb_battery_info[3][32];
1400 static double pb_battery_info_update;
1401  
1402 #define PMU_PATH "/proc/pmu"
1403 void get_powerbook_batt_info(char *buf, size_t n, int i)
1404 {
1405         static int rep = 0;
1406         const char* batt_path = PMU_PATH "/battery_0";
1407         const char* info_path = PMU_PATH "/info";
1408         int flags, charge, max_charge, ac = -1;
1409         long time = -1;
1410
1411         /* don't update battery too often */
1412         if (current_update_time - pb_battery_info_update < 29.5) {
1413                 snprintf(buf, n, "%s", pb_battery_info[i]);
1414                 return;
1415         }
1416         pb_battery_info_update = current_update_time;
1417
1418         if (pmu_battery_fp == NULL)
1419                 pmu_battery_fp = open_file(batt_path, &rep);
1420
1421         if (pmu_battery_fp != NULL) {
1422                 rewind(pmu_battery_fp);
1423                 while (!feof(pmu_battery_fp)) {
1424                         char buf[32];
1425                         if (fgets(buf, sizeof(buf), pmu_battery_fp) == NULL)
1426                                 break;
1427
1428                         if (buf[0] == 'f')
1429                                 sscanf(buf, "flags      : %8x", &flags);
1430                         else if (buf[0] == 'c' && buf[1] == 'h')
1431                                 sscanf(buf, "charge     : %d", &charge);
1432                         else if (buf[0] == 'm')
1433                                 sscanf(buf, "max_charge : %d", &max_charge);
1434                         else if (buf[0] == 't')
1435                                 sscanf(buf, "time rem.  : %ld", &time);
1436                 }
1437         }
1438         if (pmu_info_fp == NULL)
1439                 pmu_info_fp = open_file(info_path, &rep);
1440
1441         if (pmu_info_fp != NULL) {
1442                 rewind(pmu_info_fp);
1443                 while (!feof(pmu_info_fp)) {
1444                         char buf[32];
1445                         if (fgets(buf, sizeof(buf), pmu_info_fp) == NULL)
1446                                 break;
1447                         if (buf[0] == 'A')
1448                                 sscanf(buf, "AC Power               : %d", &ac);
1449                 }
1450         }
1451         /* update status string */
1452         if ((ac && !(flags & PMU_BATT_PRESENT)))
1453                 strcpy(pb_battery_info[PB_BATT_STATUS], "AC");
1454         else if (ac && (flags & PMU_BATT_PRESENT)
1455                   && !(flags & PMU_BATT_CHARGING))
1456                 strcpy(pb_battery_info[PB_BATT_STATUS], "charged");
1457         else if ((flags & PMU_BATT_PRESENT)
1458                 && (flags & PMU_BATT_CHARGING))
1459                 strcpy(pb_battery_info[PB_BATT_STATUS], "charging");
1460         else
1461                 strcpy(pb_battery_info[PB_BATT_STATUS], "discharging");
1462
1463         /* update percentage string */
1464         if (time == 0)
1465                 pb_battery_info[PB_BATT_PERCENT][0] = 0; 
1466         else
1467                 snprintf(pb_battery_info[PB_BATT_PERCENT],
1468                         sizeof(pb_battery_info[PB_BATT_PERCENT]),
1469                         "%d%%", (charge * 100)/max_charge);
1470
1471         /* update time string */
1472         if (time == 0) /* fully charged or battery not present */
1473                 pb_battery_info[PB_BATT_TIME][0] = 0; 
1474         else if (time < 60*60) /* don't show secs */
1475                 format_seconds_short(pb_battery_info[PB_BATT_TIME],
1476                         sizeof(pb_battery_info[PB_BATT_TIME]), time);
1477         else
1478                 format_seconds(pb_battery_info[PB_BATT_TIME],
1479                         sizeof(pb_battery_info[PB_BATT_TIME]), time);
1480
1481         snprintf(buf, n, "%s", pb_battery_info[i]);
1482 }
1483
1484 void update_top()
1485 {
1486         show_nice_processes = 1;
1487         process_find_top(info.cpu, info.memu);
1488         info.first_process = get_first_process();
1489 }
1490
1491
1492 /*
1493  *  The following ifdefs were adapted from gkrellm
1494  */
1495 #include <linux/major.h>
1496
1497 #if ! defined (MD_MAJOR)
1498 #define MD_MAJOR 9
1499 #endif
1500
1501 #if !defined(LVM_BLK_MAJOR)
1502 #define LVM_BLK_MAJOR 58
1503 #endif
1504
1505 #if !defined(NBD_MAJOR)
1506 #define NBD_MAJOR 43
1507 #endif
1508
1509 void update_diskio()
1510 {
1511         static unsigned int last = UINT_MAX;
1512         FILE* fp;
1513   static int rep=0;
1514
1515         char buf[512];
1516         int major, minor;
1517         unsigned int current = 0;
1518         unsigned int reads, writes = 0;
1519         int col_count = 0;
1520
1521         if (!(fp =open_file("/proc/diskstats", &rep)))
1522       return;
1523
1524         /* read reads and writes from all disks (minor = 0), including
1525          * cd-roms and floppies, and summ them up
1526          */
1527         current = 0;
1528         while (!feof(fp)) {
1529                 fgets(buf, 512, fp);
1530                 col_count = sscanf(buf, "%u %u %*s %*u %*u %u %*u %*u %*u %u",
1531                                    &major, &minor, &reads, &writes);
1532                 /* ignore subdevices (they have only 3 matching entries in their line)
1533                  * and virtual devices (LVM, network block devices, RAM disks, Loopback)
1534                  *
1535                  * XXX ignore devices which are part of a SW RAID (MD_MAJOR)
1536                  */
1537                 if (col_count > 3 &&
1538                     major != LVM_BLK_MAJOR && major != NBD_MAJOR &&
1539                     major != RAMDISK_MAJOR && major != LOOP_MAJOR) {
1540                         current += reads + writes;
1541                 }
1542         }
1543
1544         /* since the values in /proc/diststats are absolute, we have
1545          * to substract our last reading. The numbers stand for
1546          * "sectors read", and we therefore have to divide by two to
1547          * get KB */
1548         int tot = ((double)(current-last)/2);
1549         if (last > current) {
1550                 /* we hit this either if it's the very first time we
1551                  * run this, or when /proc/diskstats overflows; while
1552                  * 0 is not correct, it's at least not way off */
1553                 tot = 0;
1554         }
1555         last = current;
1556
1557         diskio_value = tot;
1558
1559   fclose(fp);
1560 }
1561
1562 /* Here come the IBM ACPI-specific things. For reference, see
1563  http://ibm-acpi.sourceforge.net/README
1564 If IBM ACPI is installed, /proc/acpi/ibm contains the following files:
1565 bay
1566 beep
1567 bluetooth
1568 brightness
1569 cmos
1570 dock
1571 driver
1572 ecdump
1573 fan
1574 hotkey
1575 led
1576 light
1577 thermal
1578 video
1579 volume
1580 The content of these files is described in detail in the aforementioned
1581 README - some of them also in the following functions accessing them.
1582 Peter Tarjan (ptarjan@citromail.hu)
1583 */
1584
1585 #define IBM_ACPI_DIR "/proc/acpi/ibm"
1586
1587 void get_ibm_acpi_fan( char * p_client_buffer, size_t client_buffer_size )
1588 {
1589 /* get fan speed on IBM/Lenovo laptops running the ibm acpi.
1590    /proc/acpi/ibm/fan looks like this (3 lines):
1591 status:         disabled
1592 speed:          2944
1593 commands:       enable, disable
1594 Peter Tarjan (ptarjan@citromail.hu)
1595 */
1596
1597     if ( !p_client_buffer || client_buffer_size <= 0 )
1598         return;
1599     
1600     FILE *fp;
1601     unsigned int speed=0;
1602     char fan[128];
1603     snprintf(fan, 127, "%s/fan",IBM_ACPI_DIR);    
1604
1605     fp = fopen(fan, "r");
1606     if (fp != NULL)
1607     {
1608         while (!feof(fp))
1609         {
1610             char line[256];
1611             if (fgets(line, 255, fp) == NULL) break;
1612             if (sscanf(line, "speed: %d", &speed)) break;       
1613         }
1614     }
1615     else 
1616     {
1617         CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove ibm* from your Conky config file.", fan, strerror(errno));
1618     }
1619
1620     fclose(fp);
1621     snprintf( p_client_buffer, client_buffer_size, "%d", speed );
1622     return;
1623     
1624 }    
1625
1626 static double last_ibm_acpi_temp_time;
1627 void get_ibm_acpi_temps()
1628 {
1629 /* get the measured temperatures from the temperature sensors 
1630    on IBM/Lenovo laptops running the ibm acpi.
1631    There are 8 values in /proc/acpi/ibm/thermal, and according to 
1632    http://ibm-acpi.sourceforge.net/README
1633    these mean the following (at least on an IBM R51...)
1634 0:  CPU (also on the T series laptops)
1635 1:  Mini PCI Module (?)
1636 2:  HDD (?)
1637 3:  GPU (also on the T series laptops)
1638 4:  Battery (?)
1639 5:  N/A
1640 6:  Battery (?)
1641 7:  N/A 
1642    I'm not too sure about those with the question mark, but the values I'm 
1643    reading from *my* thermal file (on a T42p) look realistic for the 
1644    hdd and the battery. 
1645    #5 and #7 are always -128. 
1646    /proc/acpi/ibm/thermal looks like this (1 line):
1647 temperatures:   41 43 31 46 33 -128 29 -128
1648 Peter Tarjan (ptarjan@citromail.hu)
1649 */
1650
1651 /*    don't update too often */
1652     if (current_update_time - last_ibm_acpi_temp_time < 10.00) 
1653     {
1654         return;
1655     }
1656     last_ibm_acpi_temp_time = current_update_time; 
1657     
1658 /*    if ( !p_client_buffer || client_buffer_size <= 0 )
1659       return; */
1660
1661     FILE *fp;
1662
1663     char thermal[128];
1664     snprintf(thermal, 127, "%s/thermal",IBM_ACPI_DIR);    
1665     fp = fopen(thermal, "r");
1666
1667     if (fp != NULL)
1668     {
1669         while (!feof(fp))
1670         {
1671             char line[256];
1672             if (fgets(line, 255, fp) == NULL) break;
1673             if (sscanf(line, "temperatures: %d %d %d %d %d %d %d %d",
1674                        &ibm_acpi.temps[0], &ibm_acpi.temps[1],
1675                        &ibm_acpi.temps[2], &ibm_acpi.temps[3],
1676                        &ibm_acpi.temps[4], &ibm_acpi.temps[5], 
1677                        &ibm_acpi.temps[6], &ibm_acpi.temps[7])) break;
1678         }
1679     }
1680     else 
1681     {
1682         CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove ibm* from your Conky config file.", thermal, strerror(errno));
1683     }
1684
1685     fclose(fp);
1686
1687 }              
1688
1689
1690 void get_ibm_acpi_volume( char * p_client_buffer, size_t client_buffer_size )
1691 {
1692
1693 /* get volume (0-14) on IBM/Lenovo laptops running the ibm acpi.
1694    "Volume" here is none of the mixer volumes, but a "master of masters"
1695    volume adjusted by the IBM volume keys.
1696    /proc/acpi/ibm/fan looks like this (4 lines):
1697 level:          4
1698 mute:           off
1699 commands:       up, down, mute
1700 commands:       level <level> (<level> is 0-15)
1701 Peter Tarjan (ptarjan@citromail.hu)
1702 */
1703     
1704     if ( !p_client_buffer || client_buffer_size <= 0 )
1705         return;
1706     
1707     FILE *fp;
1708
1709     char volume[128];
1710     snprintf(volume, 127, "%s/volume",IBM_ACPI_DIR);    
1711     unsigned int vol=-1;
1712     char mute[3]="";
1713
1714     fp = fopen(volume, "r");
1715     if (fp != NULL)
1716     {
1717         while (!feof(fp))
1718         {
1719             char line[256];
1720             if (fgets(line, 255, fp) == NULL) break;
1721             if (sscanf(line, "level: %d", &vol)) continue;
1722             if (sscanf(line, "mute: %s", mute)) break;
1723         }
1724     }
1725     else 
1726     {
1727         CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove ibm* from your Conky config file.", volume, strerror(errno));
1728     }
1729
1730     fclose(fp);
1731
1732     if (strcmp(mute, "on")==0)
1733     {
1734         snprintf( p_client_buffer, client_buffer_size, "%s", "mute" );
1735         return;
1736     }
1737     else
1738     {
1739         snprintf( p_client_buffer, client_buffer_size, "%d", vol );
1740         return;
1741     }
1742
1743 }
1744
1745 /*static FILE *fp=NULL;*/
1746
1747 void get_ibm_acpi_brightness(char * p_client_buffer, size_t client_buffer_size)
1748 {
1749 /* get LCD brightness on IBM/Lenovo laptops running the ibm acpi.
1750    /proc/acpi/ibm/brightness looks like this (3 lines):
1751 level:          7
1752 commands:       up, down
1753 commands:       level <level> (<level> is 0-7)
1754 Peter Tarjan (ptarjan@citromail.hu)
1755 */
1756
1757     if ( !p_client_buffer || client_buffer_size <= 0 )
1758         return;
1759
1760     FILE *fp;
1761     unsigned int brightness=0;
1762     char filename[128];
1763     snprintf(filename, 127, "%s/brightness",IBM_ACPI_DIR);    
1764
1765     fp = fopen(filename, "r");
1766     if (fp != NULL)
1767     {
1768         while (!feof(fp))
1769         {
1770             char line[256];
1771             if (fgets(line, 255, fp) == NULL) break;
1772             if (sscanf(line, "level: %d", &brightness)) break;  
1773         }
1774     }
1775     else 
1776     {
1777         CRIT_ERR("can't open '%s': %s\nYou are not using the IBM ACPI. Remove ibm* from your Conky config file.", filename, strerror(errno));
1778     }
1779
1780     fclose(fp);
1781
1782     snprintf( p_client_buffer, client_buffer_size, "%d", brightness );
1783     return;
1784     
1785 }    
1786
1787 void update_entropy (void)
1788 {
1789   static int rep = 0;
1790   const char *entropy_avail = "/proc/sys/kernel/random/entropy_avail";
1791   const char *entropy_poolsize = "/proc/sys/kernel/random/poolsize";
1792   FILE *fp1, *fp2;
1793
1794   info.entropy.entropy_avail=0;
1795   info.entropy.poolsize=0;
1796
1797   if ((fp1 = open_file (entropy_avail, &rep))==NULL)
1798     return;
1799
1800   if ((fp2 = open_file (entropy_poolsize, &rep))==NULL)
1801   {
1802     fclose (fp1);
1803     return;
1804   }
1805
1806   fscanf (fp1, "%u", &info.entropy.entropy_avail);
1807   fscanf (fp2, "%u", &info.entropy.poolsize);
1808
1809   fclose (fp1);
1810   fclose (fp2);
1811
1812   info.mask |= (1 << INFO_ENTROPY);
1813 }
1814
1815 #ifdef HAVE_LIBDEXTER
1816 FILE *open_file(const char *file, int *reported)
1817 {
1818   /* this version of open_file() is the hook that ties the client/server code 
1819    * into conky. if conky wants to open a /proc file that the server is feeding 
1820    * us, we do not return an ordinary file stream pointer to the local /proc 
1821    * filesystem. instead, we return a string stream pointer to the proc surrogate, 
1822    * so conky parses remote data as if it were local.
1823    */
1824   FILE *fp;
1825
1826   if ((strcmp (file,"/proc/cpuinfo")==0) && (procraw_mask & PROCRAW_CPUINFO))
1827   {
1828     if (!procraw_sampler)
1829       return NULL;
1830     fp = fmemopen (procraw_data.cpuinfo, procraw_data.cpuinfo_sz, "r");
1831   }
1832   else if ((strcmp (file,"/proc/loadavg")==0) && (procraw_mask & PROCRAW_LOADAVG))
1833   {
1834     if (!procraw_sampler)
1835       return NULL;
1836     fp = fmemopen (procraw_data.loadavg, procraw_data.loadavg_sz, "r");
1837   }
1838   else if ((strcmp (file,"/proc/meminfo")==0) && (procraw_mask & PROCRAW_MEMINFO))
1839   {
1840     if (!procraw_sampler)
1841       return NULL;
1842     fp = fmemopen (procraw_data.meminfo, procraw_data.meminfo_sz, "r");
1843   }
1844   else if ((strcmp (file,"/proc/stat")==0) && (procraw_mask & PROCRAW_STAT))
1845   {
1846     if (!procraw_sampler)
1847       return NULL;
1848     fp = fmemopen (procraw_data.stat, procraw_data.stat_sz, "r");
1849   }
1850   else if ((strcmp (file,"/proc/uptime")==0) && (procraw_mask & PROCRAW_UPTIME))
1851   {
1852     if (!procraw_sampler)
1853       return NULL;
1854     fp = fmemopen (procraw_data.uptime, procraw_data.uptime_sz, "r");
1855   }
1856   else if ((strcmp (file,"/proc/net/dev")==0) && (procraw_mask & PROCRAW_NET_DEV))
1857   {
1858     if (!procraw_sampler)
1859       return NULL;
1860     fp = fmemopen (procraw_data.net_dev, procraw_data.net_dev_sz, "r");
1861   }
1862   else if ((strcmp (file,"/proc/diskstats")==0) && (procraw_mask & PROCRAW_DISKSTATS))
1863   {
1864     if (!procraw_sampler)
1865       return NULL;
1866     fp = fmemopen (procraw_data.diskstats, procraw_data.diskstats_sz, "r");
1867   }
1868   else
1869   {
1870     fp = fopen(file, "r");
1871   }
1872
1873   if (!fp)
1874   {
1875     if (!reported || *reported == 0)
1876     {
1877       ERR("can't open %s: %s", file, strerror(errno));
1878       if (reported)
1879         *reported = 1;
1880     }
1881     return 0;
1882   }
1883   return fp;
1884 }
1885
1886 void sampler_data_callback (gpointer sampler, gpointer sampler_data)
1887 {
1888   /* callback runs in a thread */
1889   if (!sampler)
1890     return;
1891
1892   if (sampler_data)
1893   {
1894     FILE *out;
1895     char *p;
1896     unsigned int i;
1897     DxtSysinfoProcrawData *data = (DxtSysinfoProcrawData *)sampler_data;
1898
1899     /* use GNU string streams and stdio locking to exchange data with main thread. */
1900
1901     /* update /proc/cpuinfo surrogate */
1902     for (;data->cpuinfo;)
1903     {
1904       if (!(out = open_memstream (&procraw_data.cpuinfo, &procraw_data.cpuinfo_sz)))
1905         break;
1906
1907       flockfile (out);
1908       if (procraw_data.cpuinfo)
1909       {
1910         free (procraw_data.cpuinfo); procraw_data.cpuinfo=NULL;
1911       }
1912       for (p=data->cpuinfo, i=0; i<data->cpuinfo_sz; i++, p++)
1913       {
1914         /* we have the FILE lock so use faster putc_unlocked() */
1915         if (fputc_unlocked (*p, out) == EOF)
1916           break;
1917       }
1918       fclose (out);
1919       funlockfile (out);
1920       break;
1921     }
1922
1923     /* update /proc/loadavg surrogate */
1924     for (;data->loadavg;)
1925     {
1926       if (!(out = open_memstream (&procraw_data.loadavg, &procraw_data.loadavg_sz)))
1927         break;
1928
1929       flockfile (out);
1930       if (procraw_data.loadavg)
1931       {
1932         free (procraw_data.loadavg); procraw_data.loadavg=NULL;
1933       }
1934       for (p=data->loadavg, i=0; i<data->loadavg_sz; i++, p++)
1935       {
1936         /* we have the FILE lock so use faster putc_unlocked() */
1937         if (fputc_unlocked (*p, out) == EOF)
1938           break;
1939       }
1940       fclose (out);
1941       funlockfile (out);
1942       break;
1943     }
1944
1945     /* update /proc/meminfo surrogate */
1946     for (;data->meminfo;)
1947     {
1948       if (!(out = open_memstream (&procraw_data.meminfo, &procraw_data.meminfo_sz)))
1949         break;
1950
1951       flockfile (out);
1952       if (procraw_data.meminfo)
1953       {
1954         free (procraw_data.meminfo); procraw_data.meminfo=NULL;
1955       }
1956       for (p=data->meminfo, i=0; i<data->meminfo_sz; i++, p++)
1957       {
1958         /* we have the FILE lock so use faster putc_unlocked() */
1959         if (fputc_unlocked (*p, out) == EOF)
1960           break;
1961       }
1962       fclose (out);
1963       funlockfile (out);
1964       break;
1965     }
1966
1967     /* update /proc/stat surrogate */
1968     for (;data->stat;)
1969     {
1970       if (!(out = open_memstream (&procraw_data.stat, &procraw_data.stat_sz)))
1971         break;
1972
1973       flockfile (out);
1974       if (procraw_data.stat)
1975       {
1976         free (procraw_data.stat); procraw_data.stat=NULL;
1977       }
1978       for (p=data->stat, i=0; i<data->stat_sz; i++, p++)
1979       {
1980         /* we have the FILE lock so use faster putc_unlocked() */
1981         if (fputc_unlocked (*p, out) == EOF)
1982           break;
1983       }
1984       fclose (out);
1985       funlockfile (out);
1986       break;
1987     }
1988
1989     /* update /proc/uptime surrogate */
1990     for (;data->uptime;)
1991     {
1992       if (!(out = open_memstream (&procraw_data.uptime, &procraw_data.uptime_sz)))
1993         break;
1994
1995       flockfile (out);
1996       if (procraw_data.uptime)
1997       {
1998         free (procraw_data.uptime); procraw_data.uptime=NULL;
1999       }
2000       for (p=data->uptime, i=0; i<data->uptime_sz; i++, p++)
2001       {
2002         /* we have the FILE lock so use faster putc_unlocked() */
2003         if (fputc_unlocked (*p, out) == EOF)
2004           break;
2005       }
2006       fclose (out);
2007       funlockfile (out);
2008       break;
2009     }
2010
2011     /* update /proc/net/dev surrogate */
2012     for (;data->net_dev;)
2013     {
2014       if (!(out = open_memstream (&procraw_data.net_dev, &procraw_data.net_dev_sz)))
2015         break;
2016
2017       flockfile (out);
2018       if (procraw_data.net_dev)
2019       {
2020         free (procraw_data.net_dev); procraw_data.net_dev=NULL;
2021       }
2022       for (p=data->net_dev, i=0; i<data->net_dev_sz; i++, p++)
2023       {
2024         /* we have the FILE lock so use faster putc_unlocked() */
2025         if (fputc_unlocked (*p, out) == EOF)
2026           break;
2027       }
2028       fclose (out);
2029       funlockfile (out);
2030       break;
2031     }
2032
2033     /* update /proc/diskstats surrogate */
2034     for (;data->diskstats;)
2035     {
2036       if (!(out = open_memstream (&procraw_data.diskstats, &procraw_data.diskstats_sz)))
2037         break;
2038
2039       flockfile (out);
2040       if (procraw_data.diskstats)
2041       {
2042         free (procraw_data.diskstats); procraw_data.diskstats=NULL;
2043       }
2044       for (p=data->diskstats, i=0; i<data->diskstats_sz; i++, p++)
2045       {
2046         /* we have the FILE lock so use faster putc_unlocked() */
2047         if (fputc_unlocked (*p, out) == EOF)
2048           break;
2049       }
2050       fclose (out);
2051       funlockfile (out);
2052       break;
2053     }
2054
2055   /* record data packet arrival time */
2056   g_mutex_lock (packet_mutex);
2057   clock_gettime (CLOCK_REALTIME, &packet_arrival_time);
2058   g_mutex_unlock (packet_mutex);
2059 #ifdef DEBUG
2060   fprintf(stderr, "Conky: data packet arrived\n");
2061 #endif
2062
2063   } /* if (sampler_data) ... */
2064 }
2065
2066 /* return 0 on success, -1 on failure */
2067 int dexter_client_init (void)
2068 {
2069   /* init libdexter for linux-specific client-side activity */
2070
2071   DexterServiceBroker       *broker;
2072   DexterPluginServiceGroup  *service_group;
2073   DexterPluginService       *procraw_service;
2074   DexterSamplerDataCallback *callbacks;
2075   GError                    *error = NULL;
2076
2077   /* create a service broker so we can query the server for its services */
2078   if (!(broker = dexter_service_broker_new (info.dexter.channel)))
2079   {
2080     ERR ("unable to create service broker");
2081     return (-1);
2082   }
2083
2084   /* fetch the services from the server */
2085   service_group = dexter_service_broker_get_services (broker, DEXTER_SERVICE_SAMPLER, &error);
2086   if (error)
2087   {
2088     ERR("%s", error->message);
2089     g_clear_error (&error);
2090     dexter_service_broker_free (broker);
2091     return (-1);
2092   }
2093
2094   /* dont need service broker any more */
2095   dexter_service_broker_free (broker);
2096   broker=NULL;
2097
2098   /* find the procraw service */
2099   procraw_service=NULL;
2100   if (!dexterplugin_service_group_find_uuid (&procraw_service, service_group, PROCRAW_SERVICE_UUID))
2101   {
2102     ERR ("server doesn't offer the procraw service: (%s)", PROCRAW_SERVICE_UUID);
2103     dexterplugin_service_group_free (service_group);
2104     return (-1);
2105   }
2106
2107   /* create null-terminated callback list with one callback on it */
2108   callbacks = g_new0 (DexterSamplerDataCallback, 2);
2109   callbacks[0] = sampler_data_callback;
2110
2111   /* create the procraw timed sampler, timed to match conky's update_interval */
2112   procraw_sampler = dexter_timedsampler_new (procraw_service, update_interval*G_USEC_PER_SEC,
2113                                              callbacks, info.dexter.channel, &error);
2114   if (error)
2115   {
2116     ERR("%s", error->message);
2117     g_clear_error (&error);
2118     dexterplugin_service_group_free (service_group);
2119     return (-1);
2120   }
2121
2122   /* free callbacks as libdexter makes internal copy */
2123   g_free (callbacks);
2124   callbacks=NULL;
2125
2126   /* initialize the timed sampler */
2127   procraw_mask = 0;
2128   if (need_mask & (1 << INFO_FREQ))
2129     procraw_mask |= PROCRAW_CPUINFO;
2130   if (need_mask & (1 << INFO_LOADAVG))
2131     procraw_mask |= PROCRAW_LOADAVG;
2132   if ((need_mask & (1 << INFO_MEM)) || (need_mask & (1 << INFO_BUFFERS)))
2133     procraw_mask |= PROCRAW_MEMINFO;
2134   if ((need_mask & (1 << INFO_CPU)) || (need_mask & (1 << INFO_PROCS)) || 
2135       (need_mask & (1 << INFO_RUN_PROCS)) || (need_mask & (1 << INFO_FREQ )))
2136     procraw_mask |= PROCRAW_STAT;
2137   if (need_mask & (1 << INFO_UPTIME))
2138     procraw_mask |= PROCRAW_UPTIME;
2139   if (need_mask & (1 << INFO_NET))
2140     procraw_mask |= PROCRAW_NET_DEV;
2141   if (need_mask & (1 << INFO_DISKIO))
2142     procraw_mask |= PROCRAW_DISKSTATS;
2143
2144   dexter_timedsampler_initialize (procraw_sampler, &procraw_mask, NULL, &error);
2145   if (error)
2146   {
2147     ERR("%s", error->message);
2148     g_clear_error (&error);
2149     dexter_timedsampler_free (procraw_sampler, NULL);
2150     dexterplugin_service_group_free (service_group);
2151     return (-1);
2152   }
2153
2154   /* start the timed sampler and begin receiving updates from server */
2155   dexter_timedsampler_start (procraw_sampler, &error);
2156   if (error)
2157   {
2158     ERR("%s", error->message);
2159     g_clear_error (&error);
2160     dexter_timedsampler_free (procraw_sampler, NULL);
2161     dexterplugin_service_group_free (service_group);
2162     return (-1);
2163   }
2164
2165   dexterplugin_service_group_free (service_group);
2166
2167   /* so far, so good.  tell the linux routines to read /proc, 
2168    * even if other api's are available. */
2169   prefer_proc = 1;
2170
2171   return 0;
2172 }
2173
2174 /* return 0 on success, -1 on failure */
2175 int dexter_client_exit (void)
2176 {
2177   /* de-init libdexter for linux-specific client-side activity */
2178
2179   if (procraw_sampler)
2180   {
2181     dexter_timedsampler_stop (procraw_sampler, NULL);
2182     dexter_timedsampler_free (procraw_sampler, NULL);
2183     procraw_sampler=NULL;
2184   }
2185   
2186   /* free left-over sampler data.  ok to do this without thread sync because 
2187    * this function runs in the same thread that conky issues open_file(). */
2188   if (procraw_data.cpuinfo)
2189   {
2190     free (procraw_data.cpuinfo); procraw_data.cpuinfo=NULL;
2191   }
2192   if (procraw_data.loadavg)
2193   {
2194     free (procraw_data.loadavg); procraw_data.loadavg=NULL;
2195   }
2196   if (procraw_data.meminfo)
2197   {
2198     free (procraw_data.meminfo); procraw_data.meminfo=NULL;
2199   }
2200   if (procraw_data.stat)
2201   {
2202     free (procraw_data.stat); procraw_data.stat=NULL;
2203   }
2204   if (procraw_data.uptime)
2205   {
2206     free (procraw_data.uptime); procraw_data.uptime=NULL;
2207   }
2208   if (procraw_data.net_dev)
2209   {
2210     free (procraw_data.net_dev); procraw_data.net_dev=NULL;
2211   }
2212   if (procraw_data.diskstats)
2213   {
2214     free (procraw_data.diskstats); procraw_data.diskstats=NULL;
2215   }
2216
2217   info.uptime=0.0;
2218   info.procs=0;
2219   info.mem = info.memmax = info.swap = info.swapmax = info.bufmem = info.buffers = info.cached = 0;
2220   info.run_procs=0;
2221   if (info.cpu_usage)
2222   {
2223     memset(info.cpu_usage, 0, info.cpu_count * sizeof (float));
2224   }
2225   clear_net_stats ();
2226   diskio_value=0;
2227   return 0;
2228 }
2229 #endif