Make a description of ${cpu} variable not so confusing.
[monky] / src / freebsd.c
1 /*
2  * freebsd.c
3  * Contains FreeBSD specific stuff
4  *
5  * $Id$
6  */
7
8 #include <sys/dkstat.h>
9 #include <sys/param.h>
10 #include <sys/resource.h>
11 #include <sys/socket.h>
12 #include <sys/sysctl.h>
13 #include <sys/time.h>
14 #include <sys/types.h>
15 #include <sys/vmmeter.h>
16 #include <sys/user.h>
17
18 #include <net/if.h>
19 #include <net/if_mib.h>
20
21 #include <devstat.h>
22 #include <fcntl.h>
23 #include <ifaddrs.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29
30 #include "conky.h"
31
32 #define GETSYSCTL(name, var)    getsysctl(name, &(var), sizeof (var))
33 #define KELVTOC(x)              ((x - 2732) / 10.0)
34 #define MAXSHOWDEVS             16
35
36 #if 0
37 #define FREEBSD_DEBUG
38 #endif
39
40 inline void proc_find_top(struct process **cpu, struct process **mem);
41
42 u_int64_t diskio_prev = 0;
43 static short cpu_setup = 0;
44 static short diskio_setup = 0;
45
46 static int getsysctl(char *name, void *ptr, size_t len)
47 {
48         size_t nlen = len;
49         if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) {
50                 return (-1);
51         }
52
53         if (nlen != len) {
54                 return (-1);
55         }
56
57         return (0);
58 }
59
60 struct ifmibdata *data = NULL;
61 size_t len = 0;
62
63 static int swapmode(int *retavail, int *retfree)
64 {
65         int n;
66         int pagesize = getpagesize();
67         struct kvm_swap swapary[1];
68
69         *retavail = 0;
70         *retfree = 0;
71
72 #define CONVERT(v)      ((quad_t)(v) * pagesize / 1024)
73
74         n = kvm_getswapinfo(kd, swapary, 1, 0);
75         if (n < 0 || swapary[0].ksw_total == 0)
76                 return (0);
77
78         *retavail = CONVERT(swapary[0].ksw_total);
79         *retfree = CONVERT(swapary[0].ksw_total - swapary[0].ksw_used);
80
81         n = (int) ((double) swapary[0].ksw_used * 100.0 /
82                 (double) swapary[0].ksw_total);
83
84         return (n);
85 }
86
87 void
88 prepare_update()
89 {
90 }
91
92 void
93 update_uptime()
94 {
95         int mib[2] = { CTL_KERN, KERN_BOOTTIME };
96         struct timeval boottime;
97         time_t now;
98         size_t size = sizeof (boottime);
99
100         if ((sysctl(mib, 2, &boottime, &size, NULL, 0) != -1) &&
101                         (boottime.tv_sec != 0)) {
102                 time(&now);
103                 info.uptime = now - boottime.tv_sec;
104         } else {
105                 fprintf(stderr, "Could not get uptime\n");
106                 info.uptime = 0;
107         }
108 }
109
110 void
111 update_meminfo()
112 {
113         int total_pages, inactive_pages, free_pages;
114         int swap_avail, swap_free;
115
116         int pagesize = getpagesize();
117
118         if (GETSYSCTL("vm.stats.vm.v_page_count", total_pages))
119                 fprintf(stderr,
120                         "Cannot read sysctl \"vm.stats.vm.v_page_count\"");
121
122         if (GETSYSCTL("vm.stats.vm.v_free_count", free_pages))
123                 fprintf(stderr,
124                         "Cannot read sysctl \"vm.stats.vm.v_free_count\"");
125
126         if (GETSYSCTL("vm.stats.vm.v_inactive_count", inactive_pages))
127                 fprintf(stderr,
128                         "Cannot read sysctl \"vm.stats.vm.v_inactive_count\"");
129
130         info.memmax = (total_pages * pagesize) >> 10;
131         info.mem =
132             ((total_pages - free_pages - inactive_pages) * pagesize) >> 10;
133
134
135         if ((swapmode(&swap_avail, &swap_free)) >= 0) {
136                 info.swapmax = swap_avail;
137                 info.swap = (swap_avail - swap_free);
138         } else {
139                 info.swapmax = 0;
140                 info.swap = 0;
141         }
142 }
143
144 void
145 update_net_stats()
146 {
147         struct net_stat *ns;
148         double delta;
149         long long r, t, last_recv, last_trans;
150         struct ifaddrs *ifap, *ifa;
151         struct if_data *ifd;
152
153
154         /* get delta */
155         delta = current_update_time - last_update_time;
156         if (delta <= 0.0001)
157                 return;
158
159         if (getifaddrs(&ifap) < 0)
160                 return;
161
162         for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
163                 ns = get_net_stat((const char *) ifa->ifa_name);
164
165                 if (ifa->ifa_flags & IFF_UP) {
166                         struct ifaddrs *iftmp;
167
168                         ns->up = 1;
169                         ns->linkstatus = 1;
170                         last_recv = ns->recv;
171                         last_trans = ns->trans;
172
173                         if (ifa->ifa_addr->sa_family != AF_LINK)
174                                 continue;
175
176                         for (iftmp = ifa->ifa_next; iftmp != NULL &&
177                                 strcmp(ifa->ifa_name, iftmp->ifa_name) == 0;
178                                 iftmp = iftmp->ifa_next)
179                                 if (iftmp->ifa_addr->sa_family == AF_INET)
180                                         memcpy(&(ns->addr), iftmp->ifa_addr,
181                                                 iftmp->ifa_addr->sa_len);
182
183                         ifd = (struct if_data *) ifa->ifa_data;
184                         r = ifd->ifi_ibytes;
185                         t = ifd->ifi_obytes;
186
187                         if (r < ns->last_read_recv)
188                                 ns->recv +=
189                                     ((long long) 4294967295U -
190                                         ns->last_read_recv) + r;
191                         else
192                                 ns->recv += (r - ns->last_read_recv);
193
194                         ns->last_read_recv = r;
195
196                         if (t < ns->last_read_trans)
197                                 ns->trans +=
198                                     ((long long) 4294967295U -
199                                         ns->last_read_trans) + t;
200                         else
201                                 ns->trans += (t - ns->last_read_trans);
202
203                         ns->last_read_trans = t;
204
205                         /* calculate speeds */
206                         ns->recv_speed = (ns->recv - last_recv) / delta;
207                         ns->trans_speed = (ns->trans - last_trans) / delta;
208                 } else {
209                         ns->up = 0;
210                         ns->linkstatus = 0;
211                 }
212         }
213
214         freeifaddrs(ifap);
215 }
216
217 void
218 update_total_processes()
219 {
220         int n_processes;
221
222         kvm_getprocs(kd, KERN_PROC_ALL, 0, &n_processes);
223
224         info.procs = n_processes;
225 }
226
227 void
228 update_running_processes()
229 {
230         struct kinfo_proc *p;
231         int n_processes;
232         int i, cnt = 0;
233
234         p = kvm_getprocs(kd, KERN_PROC_ALL, 0, &n_processes);
235         for (i = 0; i < n_processes; i++) {
236 #if __FreeBSD__ < 5
237                 if (p[i].kp_proc.p_stat == SRUN)
238 #else
239                 if (p[i].ki_stat == SRUN)
240 #endif
241                         cnt++;
242         }
243
244         info.run_procs = cnt;
245 }
246
247 struct cpu_load_struct {
248         unsigned long load[5];
249 };
250
251 struct cpu_load_struct fresh = { {0, 0, 0, 0, 0} };
252 long cpu_used, oldtotal, oldused;
253
254 void
255 get_cpu_count()
256 {
257         /* int cpu_count = 0; */
258
259         /*
260          * XXX
261          * FreeBSD doesn't allow to get per CPU load stats
262          * on SMP machines. It's possible to get a CPU count,
263          * but as we fulfil only info.cpu_usage[0], it's better
264          * to report there's only one CPU. It should fix some bugs
265          * (e.g. cpugraph)
266          */
267 #if 0
268         if (GETSYSCTL("hw.ncpu", cpu_count) == 0)
269                 info.cpu_count = cpu_count;
270 #endif
271         info.cpu_count = 1;
272
273         info.cpu_usage = malloc(info.cpu_count * sizeof (float));
274         if (info.cpu_usage == NULL)
275                 CRIT_ERR("malloc");
276 }
277
278 /* XXX: SMP support */
279 void
280 update_cpu_usage()
281 {
282         long used, total;
283         long cp_time[CPUSTATES];
284         size_t len = sizeof (cp_time);
285
286         if (cpu_setup == 0) {
287                 get_cpu_count();
288                 cpu_setup = 1;
289         }
290
291         if (sysctlbyname("kern.cp_time", &cp_time, &len, NULL, 0) < 0) {
292                 (void) fprintf(stderr, "Cannot get kern.cp_time");
293         }
294
295         fresh.load[0] = cp_time[CP_USER];
296         fresh.load[1] = cp_time[CP_NICE];
297         fresh.load[2] = cp_time[CP_SYS];
298         fresh.load[3] = cp_time[CP_IDLE];
299         fresh.load[4] = cp_time[CP_IDLE];
300
301         used = fresh.load[0] + fresh.load[1] + fresh.load[2];
302         total =
303             fresh.load[0] + fresh.load[1] + fresh.load[2] + fresh.load[3];
304
305         if ((total - oldtotal) != 0) {
306                 info.cpu_usage[0] = ((double) (used - oldused)) /
307                         (double) (total - oldtotal);
308         } else {
309                 info.cpu_usage[0] = 0;
310         }
311
312         oldused = used;
313         oldtotal = total;
314 }
315
316 double
317 get_i2c_info(int *fd, int arg, char *devtype, char *type)
318 {
319         return (0);
320 }
321
322 void
323 update_load_average()
324 {
325         double v[3];
326         getloadavg(v, 3);
327
328         info.loadavg[0] = (float) v[0];
329         info.loadavg[1] = (float) v[1];
330         info.loadavg[2] = (float) v[2];
331 }
332
333 double
334 get_acpi_temperature(int fd)
335 {
336         int temp;
337
338         if (GETSYSCTL("hw.acpi.thermal.tz0.temperature", temp)) {
339                 fprintf(stderr,
340                 "Cannot read sysctl \"hw.acpi.thermal.tz0.temperature\"\n");
341                 return (0.0);
342         }
343
344         return (KELVTOC(temp));
345 }
346
347 void
348 get_battery_stuff(char *buf, unsigned int n, const char *bat)
349 {
350         int battime, batcapacity, batstate, ac;
351
352         if (GETSYSCTL("hw.acpi.battery.time", battime))
353                 (void) fprintf(stderr,
354                         "Cannot read sysctl \"hw.acpi.battery.time\"\n");
355         if (GETSYSCTL("hw.acpi.battery.life", batcapacity))
356                 (void) fprintf(stderr,
357                                            "Cannot read sysctl \"hw.acpi.battery.life\"\n");
358
359         if (GETSYSCTL("hw.acpi.battery.state", batstate))
360                 (void) fprintf(stderr,
361                                            "Cannot read sysctl \"hw.acpi.battery.state\"\n");
362
363         if (GETSYSCTL("hw.acpi.acline", ac))
364                 (void) fprintf(stderr,
365                                            "Cannot read sysctl \"hw.acpi.acline\"\n");
366
367         if (batstate == 1) {
368                 if (battime != -1)
369                         snprintf(buf, n, "remaining %d%% (%d:%2.2d)",
370                                         batcapacity, battime / 60, battime % 60);
371                 else
372                         /* no time estimate available yet */
373                         snprintf(buf, n, "remaining %d%%",
374                                         batcapacity);
375                 if (ac == 1)
376                         (void) fprintf(stderr, "Discharging while on AC!\n");
377         } else {
378                 snprintf(buf, n, batstate == 2 ? "charging (%d%%)" : "charged (%d%%)", batcapacity);
379                 if (batstate != 2 && batstate != 0)
380                         (void) fprintf(stderr, "Unknow battery state %d!\n", batstate);
381                 if (ac == 0)
382                         (void) fprintf(stderr, "Charging while not on AC!\n");
383         }
384
385 }
386
387 int
388 open_i2c_sensor(const char *dev, const char *type, int n, int *div,
389                 char *devtype)
390 {
391         return (0);
392 }
393
394 int
395 open_acpi_temperature(const char *name)
396 {
397         return (0);
398 }
399
400 void
401 get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
402 {
403         int state;
404
405         if (!p_client_buffer || client_buffer_size <= 0)
406                 return;
407
408         if (GETSYSCTL("hw.acpi.acline", state)) {
409                 fprintf(stderr,
410                         "Cannot read sysctl \"hw.acpi.acline\"\n");
411                 return;
412         }
413
414
415         if (state)
416                 strncpy(p_client_buffer, "Running on AC Power",
417                                 client_buffer_size);
418         else
419                 strncpy(p_client_buffer, "Running on battery",
420                                 client_buffer_size);
421
422 }
423
424 void
425 get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
426 {
427         if (!p_client_buffer || client_buffer_size <= 0)
428                 return;
429
430         /* not implemented */
431         memset(p_client_buffer, 0, client_buffer_size);
432 }
433
434 void
435 get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
436 {
437         if (!p_client_buffer || client_buffer_size <= 0)
438                 return;
439
440         /* not implemented */
441         memset(p_client_buffer, 0, client_buffer_size);
442 }
443
444 void
445 get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
446 {
447         if (!p_client_buffer || client_buffer_size <= 0)
448                 return;
449
450         /* not implemented */
451         memset(p_client_buffer, 0, client_buffer_size);
452 }
453
454 /* rdtsc() and get_freq_dynamic() copied from linux.c */
455
456 #if  defined(__i386) || defined(__x86_64)
457 __inline__ unsigned long long int
458 rdtsc()
459 {
460         unsigned long long int x;
461         __asm__ volatile(".byte 0x0f, 0x31":"=A" (x));
462         return (x);
463 }
464 #endif
465
466 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
467 void
468 get_freq_dynamic(char *p_client_buffer, size_t client_buffer_size,
469                 char *p_format, int divisor)
470 {
471 #if  defined(__i386) || defined(__x86_64)
472         struct timezone tz;
473         struct timeval tvstart, tvstop;
474         unsigned long long cycles[2];   /* gotta be 64 bit */
475         unsigned int microseconds;      /* total time taken */
476
477         memset(&tz, 0, sizeof (tz));
478
479         /* get this function in cached memory */
480         gettimeofday(&tvstart, &tz);
481         cycles[0] = rdtsc();
482         gettimeofday(&tvstart, &tz);
483
484         /* we don't trust that this is any specific length of time */
485         usleep(100);
486         cycles[1] = rdtsc();
487         gettimeofday(&tvstop, &tz);
488         microseconds = ((tvstop.tv_sec - tvstart.tv_sec) * 1000000) +
489                 (tvstop.tv_usec - tvstart.tv_usec);
490
491         snprintf(p_client_buffer, client_buffer_size, p_format,
492                 (float)((cycles[1] - cycles[0]) / microseconds) / divisor);
493 #else
494         get_freq(p_client_buffer, client_buffer_size, p_format, divisor);
495 #endif
496 }
497
498 /*void*/
499 char
500 get_freq(char *p_client_buffer, size_t client_buffer_size,
501                 char *p_format, int divisor, unsigned int cpu)
502 {
503         int freq;
504         char *freq_sysctl;
505
506         freq_sysctl = (char *)calloc(16, sizeof(char));
507         if (freq_sysctl == NULL)
508                 exit(-1);
509
510         snprintf(freq_sysctl, 16, "dev.cpu.%d.freq", cpu);
511         
512         if (!p_client_buffer || client_buffer_size <= 0 ||
513                         !p_format || divisor <= 0)
514                 return;
515
516         if (GETSYSCTL(freq_sysctl, freq) == 0)
517                 snprintf(p_client_buffer, client_buffer_size,
518                                 p_format, (float)freq/divisor);
519         else
520                 snprintf(p_client_buffer, client_buffer_size, p_format, 0.0f);
521
522         free(freq_sysctl);
523         return 1;
524 }
525
526 void
527 update_top()
528 {
529         proc_find_top(info.cpu, info.memu);
530 }
531
532 void
533 update_wifi_stats()
534 {
535         /* XXX */
536 }
537 void
538 update_diskio()
539 {
540         int devs_count,
541             num_selected,
542             num_selections;
543         struct device_selection *dev_select = NULL;
544         long select_generation;
545         int dn;
546         static struct statinfo  statinfo_cur;
547         u_int64_t diskio_current = 0;
548
549         bzero(&statinfo_cur, sizeof (statinfo_cur));
550         statinfo_cur.dinfo = (struct devinfo *)malloc(sizeof (struct devinfo));
551         bzero(statinfo_cur.dinfo, sizeof (struct devinfo));
552
553         if (devstat_getdevs(NULL, &statinfo_cur) < 0)
554                 return;
555
556         devs_count = statinfo_cur.dinfo->numdevs;
557         if (devstat_selectdevs(&dev_select, &num_selected, &num_selections,
558                         &select_generation, statinfo_cur.dinfo->generation,
559                         statinfo_cur.dinfo->devices, devs_count, NULL, 0,
560                         NULL, 0, DS_SELECT_ONLY, MAXSHOWDEVS, 1) >= 0) {
561                 for (dn = 0; dn < devs_count; ++dn) {
562                         int di;
563                         struct devstat  *dev;
564
565                         di = dev_select[dn].position;
566                         dev = &statinfo_cur.dinfo->devices[di];
567
568                         diskio_current += dev->bytes[DEVSTAT_READ] +
569                                 dev->bytes[DEVSTAT_WRITE];
570                 }
571
572                 free(dev_select);
573         }
574
575         /*
576          * Since we return (diskio_total_current - diskio_total_old), first
577          * frame will be way too high (it will be equal to
578          * diskio_total_current, i.e. all disk I/O since boot). That's why
579          * it is better to return 0 first time;
580          */
581         if (diskio_setup == 0) {
582                 diskio_setup = 1;
583                 diskio_value = 0;
584         } else
585                 diskio_value = (unsigned int)((diskio_current - diskio_prev)/
586                                 1024);
587         diskio_prev = diskio_current;
588
589         free(statinfo_cur.dinfo);
590 }
591
592 /*
593  * While topless is obviously better, top is also not bad.
594  */
595
596 int
597 comparecpu(const void *a, const void *b)
598 {
599         if (((struct process *)a)->amount > ((struct process *)b)->amount)
600                 return (-1);
601
602         if (((struct process *)a)->amount < ((struct process *)b)->amount)
603                 return (1);
604
605         return (0);
606 }
607
608 int
609 comparemem(const void *a, const void *b)
610 {
611         if (((struct process *)a)->totalmem > ((struct process *)b)->totalmem)
612                 return (-1);
613
614         if (((struct process *)a)->totalmem < ((struct process *)b)->totalmem)
615                 return (1);
616
617         return (0);
618 }
619
620 inline void
621 proc_find_top(struct process **cpu, struct process **mem)
622 {
623         struct kinfo_proc *p;
624         int n_processes;
625         int i, j = 0;
626         struct process *processes;
627
628         int total_pages;
629
630         /* we get total pages count again to be sure it is up to date */
631         if (GETSYSCTL("vm.stats.vm.v_page_count", total_pages) != 0)
632                 CRIT_ERR("Cannot read sysctl"
633                         "\"vm.stats.vm.v_page_count\"");
634
635         p = kvm_getprocs(kd, KERN_PROC_PROC, 0, &n_processes);
636         processes = malloc(n_processes * sizeof (struct process));
637
638         for (i = 0; i < n_processes; i++) {
639                 if (!((p[i].ki_flag & P_SYSTEM)) &&
640                                 p[i].ki_comm != NULL) {
641                         processes[j].pid = p[i].ki_pid;
642                         processes[j].name =  strdup(p[i].ki_comm);
643                         processes[j].amount = 100.0 *
644                                 p[i].ki_pctcpu / FSCALE;
645                         processes[j].totalmem = (float)(p[i].ki_rssize /
646                                         (float)total_pages) * 100.0;
647                         j++;
648                 }
649         }
650
651         qsort(processes, j - 1, sizeof (struct process), comparemem);
652         for (i = 0; i < 10; i++) {
653                 struct process *tmp, *ttmp;
654
655                 tmp = malloc(sizeof (struct process));
656                 tmp->pid = processes[i].pid;
657                 tmp->amount = processes[i].amount;
658                 tmp->totalmem = processes[i].totalmem;
659                 tmp->name = strdup(processes[i].name);
660
661                 ttmp = mem[i];
662                 mem[i] = tmp;
663                 if (ttmp != NULL) {
664                         free(ttmp->name);
665                         free(ttmp);
666                 }
667         }
668
669         qsort(processes, j - 1, sizeof (struct process), comparecpu);
670         for (i = 0; i < 10; i++) {
671                 struct process *tmp, *ttmp;
672
673                 tmp = malloc(sizeof (struct process));
674                 tmp->pid = processes[i].pid;
675                 tmp->amount = processes[i].amount;
676                 tmp->totalmem = processes[i].totalmem;
677                 tmp->name = strdup(processes[i].name);
678
679                 ttmp = cpu[i];
680                 cpu[i] = tmp;
681                 if (ttmp != NULL) {
682                         free(ttmp->name);
683                         free(ttmp);
684                 }
685         }
686
687 #if defined(FREEBSD_DEBUG)
688         printf("=====\nmem\n");
689         for (i = 0; i < 10; i++) {
690                 printf("%d: %s(%d) %.2f\n", i, mem[i]->name,
691                                 mem[i]->pid, mem[i]->totalmem);
692         }
693 #endif
694
695         for (i = 0; i < j; free(processes[i++].name));
696         free(processes);
697 }
698
699 #if     defined(i386) || defined(__i386__)
700 #define APMDEV          "/dev/apm"
701 #define APM_UNKNOWN     255
702
703 int
704 apm_getinfo(int fd, apm_info_t aip)
705 {
706         if (ioctl(fd, APMIO_GETINFO, aip) == -1)
707                 return (-1);
708
709         return (0);
710 }
711
712 char
713 *get_apm_adapter()
714 {
715         int fd;
716         struct apm_info info;
717
718         fd = open(APMDEV, O_RDONLY);
719         if (fd < 0)
720                 return ("ERR");
721
722         if (apm_getinfo(fd, &info) != 0) {
723                 close(fd);
724                 return ("ERR");
725         }
726         close(fd);
727
728         switch (info.ai_acline) {
729                 case 0:
730                         return ("off-line");
731                         break;
732                 case 1:
733                         if (info.ai_batt_stat == 3)
734                                 return ("charging");
735                         else
736                                 return ("on-line");
737                         break;
738                 default:
739                         return ("unknown");
740                         break;
741         }
742 }
743
744 char
745 *get_apm_battery_life()
746 {
747         int fd;
748         u_int batt_life;
749         struct apm_info info;
750         char *out;
751
752         out = (char *)calloc(16, sizeof (char));
753
754         fd = open(APMDEV, O_RDONLY);
755         if (fd < 0) {
756                 strncpy(out, "ERR", 16);
757                 return (out);
758         }
759
760         if (apm_getinfo(fd, &info) != 0) {
761                 close(fd);
762                 strncpy(out, "ERR", 16);
763                 return (out);
764         }
765         close(fd);
766
767         batt_life = info.ai_batt_life;
768         if (batt_life == APM_UNKNOWN)
769                 strncpy(out, "unknown", 16);
770         else if (batt_life <= 100) {
771                 snprintf(out, 16, "%d%%", batt_life);
772                 return (out);
773         } else
774                 strncpy(out, "ERR", 16);
775
776         return (out);
777 }
778
779 char
780 *get_apm_battery_time()
781 {
782         int fd;
783         int batt_time;
784         int h, m, s;
785         struct apm_info info;
786         char *out;
787
788         out = (char *)calloc(16, sizeof (char));
789
790         fd = open(APMDEV, O_RDONLY);
791         if (fd < 0) {
792                 strncpy(out, "ERR", 16);
793                 return (out);
794         }
795
796         if (apm_getinfo(fd, &info) != 0) {
797                 close(fd);
798                 strncpy(out, "ERR", 16);
799                 return (out);
800         }
801         close(fd);
802
803         batt_time = info.ai_batt_time;
804
805         if (batt_time == -1)
806                 strncpy(out, "unknown", 16);
807         else {
808                 h = batt_time;
809                 s = h % 60;
810                 h /= 60;
811                 m = h % 60;
812                 h /= 60;
813                 snprintf(out, 16, "%2d:%02d:%02d", h, m, s);
814         }
815
816         return (out);
817 }
818
819 #endif
820
821 /* empty stub so conky links */
822 void
823 free_all_processes(void)
824 {
825 }