aa976088198d39aa28e21f55e50ec37059c42b26
[monky] / src / freebsd.c
1 /* Conky, a system monitor, based on torsmo
2  *
3  * Any original torsmo code is licensed under the BSD license
4  *
5  * All code written since the fork of torsmo is licensed under the GPL
6  *
7  * Please see COPYING for details
8  *
9  * Copyright (c) 2005-2008 Brenden Matthews, Philip Kovacs, et. al.
10  *      (see AUTHORS)
11  * All rights reserved.
12  *
13  * This program is free software: you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation, either version 3 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  * You should have received a copy of the GNU General Public License
23  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24  *
25  */
26
27 #include <sys/dkstat.h>
28 #include <sys/param.h>
29 #include <sys/resource.h>
30 #include <sys/socket.h>
31 #include <sys/stat.h>
32 #include <sys/sysctl.h>
33 #include <sys/time.h>
34 #include <sys/types.h>
35 #include <sys/vmmeter.h>
36 #include <sys/user.h>
37 #include <sys/ioctl.h>
38
39 #include <net/if.h>
40 #include <net/if_mib.h>
41 #include <net/if_media.h>
42 #include <net/if_var.h>
43 #include <netinet/in.h>
44
45 #include <devstat.h>
46 #include <fcntl.h>
47 #include <ifaddrs.h>
48 #include <limits.h>
49 #include <unistd.h>
50
51 #include <dev/wi/if_wavelan_ieee.h>
52 #include <dev/acpica/acpiio.h>
53
54 #include "conky.h"
55 #include "freebsd.h"
56 #include "logging.h"
57 #include "top.h"
58 #include "diskio.h"
59
60 #define GETSYSCTL(name, var)    getsysctl(name, &(var), sizeof(var))
61 #define KELVTOC(x)                              ((x - 2732) / 10.0)
62 #define MAXSHOWDEVS                             16
63
64 #if 0
65 #define FREEBSD_DEBUG
66 #endif
67
68 inline void proc_find_top(struct process **cpu, struct process **mem);
69
70 static short cpu_setup = 0;
71 static struct diskio_stat stats = {
72         .next = NULL,
73         .current = 0,
74         .current_read = 0,
75         .current_write = 0,
76         .last = UINT_MAX,
77         .last_read = UINT_MAX,
78         .last_write = UINT_MAX,
79 };
80
81 static int getsysctl(char *name, void *ptr, size_t len)
82 {
83         size_t nlen = len;
84
85         if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) {
86                 return -1;
87         }
88
89         if (nlen != len && errno == ENOMEM) {
90                 return -1;
91         }
92
93         return 0;
94 }
95
96 struct ifmibdata *data = NULL;
97 size_t len = 0;
98
99 static int swapmode(unsigned long *retavail, unsigned long *retfree)
100 {
101         int n;
102         unsigned long pagesize = getpagesize();
103         struct kvm_swap swapary[1];
104
105         *retavail = 0;
106         *retfree = 0;
107
108 #define CONVERT(v)      ((quad_t)(v) * (pagesize / 1024))
109
110         n = kvm_getswapinfo(kd, swapary, 1, 0);
111         if (n < 0 || swapary[0].ksw_total == 0) {
112                 return 0;
113         }
114
115         *retavail = CONVERT(swapary[0].ksw_total);
116         *retfree = CONVERT(swapary[0].ksw_total - swapary[0].ksw_used);
117
118         n = (int) ((double) swapary[0].ksw_used * 100.0 /
119                 (double) swapary[0].ksw_total);
120
121         return n;
122 }
123
124 void prepare_update()
125 {
126 }
127
128 void update_uptime()
129 {
130         int mib[2] = { CTL_KERN, KERN_BOOTTIME };
131         struct timeval boottime;
132         time_t now;
133         size_t size = sizeof(boottime);
134
135         if ((sysctl(mib, 2, &boottime, &size, NULL, 0) != -1)
136                         && (boottime.tv_sec != 0)) {
137                 time(&now);
138                 info.uptime = now - boottime.tv_sec;
139         } else {
140                 fprintf(stderr, "Could not get uptime\n");
141                 info.uptime = 0;
142         }
143 }
144
145 int check_mount(char *s)
146 {
147         struct statfs *mntbuf;
148         int i, mntsize;
149
150         mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
151         for (i = mntsize - 1; i >= 0; i--) {
152                 if (strcmp(mntbuf[i].f_mntonname, s) == 0) {
153                         return 1;
154                 }
155         }
156
157         return 0;
158 }
159
160 void update_meminfo()
161 {
162         u_int total_pages, inactive_pages, free_pages;
163         unsigned long swap_avail, swap_free;
164
165         int pagesize = getpagesize();
166
167         if (GETSYSCTL("vm.stats.vm.v_page_count", total_pages)) {
168                 fprintf(stderr, "Cannot read sysctl \"vm.stats.vm.v_page_count\"\n");
169         }
170
171         if (GETSYSCTL("vm.stats.vm.v_free_count", free_pages)) {
172                 fprintf(stderr, "Cannot read sysctl \"vm.stats.vm.v_free_count\"\n");
173         }
174
175         if (GETSYSCTL("vm.stats.vm.v_inactive_count", inactive_pages)) {
176                 fprintf(stderr, "Cannot read sysctl \"vm.stats.vm.v_inactive_count\"\n");
177         }
178
179         info.memmax = total_pages * (pagesize >> 10);
180         info.mem = (total_pages - free_pages - inactive_pages) * (pagesize >> 10);
181         info.memeasyfree = info.memfree = info.memmax - info.mem;
182
183         if ((swapmode(&swap_avail, &swap_free)) >= 0) {
184                 info.swapmax = swap_avail;
185                 info.swap = (swap_avail - swap_free);
186         } else {
187                 info.swapmax = 0;
188                 info.swap = 0;
189         }
190 }
191
192 void update_net_stats()
193 {
194         struct net_stat *ns;
195         double delta;
196         long long r, t, last_recv, last_trans;
197         struct ifaddrs *ifap, *ifa;
198         struct if_data *ifd;
199
200         /* get delta */
201         delta = current_update_time - last_update_time;
202         if (delta <= 0.0001) {
203                 return;
204         }
205
206         if (getifaddrs(&ifap) < 0) {
207                 return;
208         }
209
210         for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
211                 ns = get_net_stat((const char *) ifa->ifa_name);
212
213                 if (ifa->ifa_flags & IFF_UP) {
214                         struct ifaddrs *iftmp;
215
216                         ns->up = 1;
217                         last_recv = ns->recv;
218                         last_trans = ns->trans;
219
220                         if (ifa->ifa_addr->sa_family != AF_LINK) {
221                                 continue;
222                         }
223
224                         for (iftmp = ifa->ifa_next;
225                                         iftmp != NULL && strcmp(ifa->ifa_name, iftmp->ifa_name) == 0;
226                                         iftmp = iftmp->ifa_next) {
227                                 if (iftmp->ifa_addr->sa_family == AF_INET) {
228                                         memcpy(&(ns->addr), iftmp->ifa_addr,
229                                                 iftmp->ifa_addr->sa_len);
230                                 }
231                         }
232
233                         ifd = (struct if_data *) ifa->ifa_data;
234                         r = ifd->ifi_ibytes;
235                         t = ifd->ifi_obytes;
236
237                         if (r < ns->last_read_recv) {
238                                 ns->recv += ((long long) 4294967295U - ns->last_read_recv) + r;
239                         } else {
240                                 ns->recv += (r - ns->last_read_recv);
241                         }
242
243                         ns->last_read_recv = r;
244
245                         if (t < ns->last_read_trans) {
246                                 ns->trans += ((long long) 4294967295U -
247                                         ns->last_read_trans) + t;
248                         } else {
249                                 ns->trans += (t - ns->last_read_trans);
250                         }
251
252                         ns->last_read_trans = t;
253
254                         /* calculate speeds */
255                         ns->recv_speed = (ns->recv - last_recv) / delta;
256                         ns->trans_speed = (ns->trans - last_trans) / delta;
257                 } else {
258                         ns->up = 0;
259                 }
260         }
261
262         freeifaddrs(ifap);
263 }
264
265 void update_total_processes()
266 {
267         int n_processes;
268
269         kvm_getprocs(kd, KERN_PROC_ALL, 0, &n_processes);
270
271         info.procs = n_processes;
272 }
273
274 void update_running_processes()
275 {
276         struct kinfo_proc *p;
277         int n_processes;
278         int i, cnt = 0;
279
280         p = kvm_getprocs(kd, KERN_PROC_ALL, 0, &n_processes);
281         for (i = 0; i < n_processes; i++) {
282 #if (__FreeBSD__ < 5) && (__FreeBSD_kernel__ < 5)
283                 if (p[i].kp_proc.p_stat == SRUN) {
284 #else
285                 if (p[i].ki_stat == SRUN) {
286 #endif
287                         cnt++;
288                 }
289         }
290
291         info.run_procs = cnt;
292 }
293
294 struct cpu_load_struct {
295         unsigned long load[5];
296 };
297
298 struct cpu_load_struct fresh = { {0, 0, 0, 0, 0} };
299 long cpu_used, oldtotal, oldused;
300
301 void get_cpu_count()
302 {
303         /* int cpu_count = 0; */
304
305         /* XXX: FreeBSD doesn't allow to get per CPU load stats on SMP machines.
306          * It's possible to get a CPU count, but as we fulfill only
307          * info.cpu_usage[0], it's better to report there's only one CPU.
308          * It should fix some bugs (e.g. cpugraph) */
309 #if 0
310         if (GETSYSCTL("hw.ncpu", cpu_count) == 0) {
311                 info.cpu_count = cpu_count;
312         }
313 #endif
314         info.cpu_count = 1;
315
316         info.cpu_usage = malloc(info.cpu_count * sizeof(float));
317         if (info.cpu_usage == NULL) {
318                 CRIT_ERR("malloc");
319         }
320 }
321
322 /* XXX: SMP support */
323 void update_cpu_usage()
324 {
325         long used, total;
326         long cp_time[CPUSTATES];
327         size_t len = sizeof(cp_time);
328
329         /* add check for !info.cpu_usage since that mem is freed on a SIGUSR1 */
330         if ((cpu_setup == 0) || (!info.cpu_usage)) {
331                 get_cpu_count();
332                 cpu_setup = 1;
333         }
334
335         if (sysctlbyname("kern.cp_time", &cp_time, &len, NULL, 0) < 0) {
336                 fprintf(stderr, "Cannot get kern.cp_time");
337         }
338
339         fresh.load[0] = cp_time[CP_USER];
340         fresh.load[1] = cp_time[CP_NICE];
341         fresh.load[2] = cp_time[CP_SYS];
342         fresh.load[3] = cp_time[CP_IDLE];
343         fresh.load[4] = cp_time[CP_IDLE];
344
345         used = fresh.load[0] + fresh.load[1] + fresh.load[2];
346         total = fresh.load[0] + fresh.load[1] + fresh.load[2] + fresh.load[3];
347
348         if ((total - oldtotal) != 0) {
349                 info.cpu_usage[0] = ((double) (used - oldused)) /
350                         (double) (total - oldtotal);
351         } else {
352                 info.cpu_usage[0] = 0;
353         }
354
355         oldused = used;
356         oldtotal = total;
357 }
358
359 void update_load_average()
360 {
361         double v[3];
362
363         getloadavg(v, 3);
364
365         info.loadavg[0] = (double) v[0];
366         info.loadavg[1] = (double) v[1];
367         info.loadavg[2] = (double) v[2];
368 }
369
370 double get_acpi_temperature(int fd)
371 {
372         int temp;
373
374         if (GETSYSCTL("hw.acpi.thermal.tz0.temperature", temp)) {
375                 fprintf(stderr,
376                         "Cannot read sysctl \"hw.acpi.thermal.tz0.temperature\"\n");
377                 return 0.0;
378         }
379
380         return KELVTOC(temp);
381 }
382
383 static void get_battery_stats(int *battime, int *batcapacity, int *batstate, int *ac) {
384         if (battime && GETSYSCTL("hw.acpi.battery.time", *battime)) {
385                 fprintf(stderr, "Cannot read sysctl \"hw.acpi.battery.time\"\n");
386         }
387         if (batcapacity && GETSYSCTL("hw.acpi.battery.life", *batcapacity)) {
388                 fprintf(stderr, "Cannot read sysctl \"hw.acpi.battery.life\"\n");
389         }
390         if (batstate && GETSYSCTL("hw.acpi.battery.state", *batstate)) {
391                 fprintf(stderr, "Cannot read sysctl \"hw.acpi.battery.state\"\n");
392         }
393         if (ac && GETSYSCTL("hw.acpi.acline", *ac)) {
394                 fprintf(stderr, "Cannot read sysctl \"hw.acpi.acline\"\n");
395         }
396 }
397
398 void get_battery_stuff(char *buf, unsigned int n, const char *bat, int item)
399 {
400         int battime, batcapacity, batstate, ac;
401         char battery_status[64];
402         char battery_time[64];
403
404         get_battery_stats(&battime, &batcapacity, &batstate, &ac);
405
406         if (batstate != 1 && batstate != 2 && batstate != 0 && batstate != 7)
407                 fprintf(stderr, "Unknown battery state %d!\n", batstate);
408         else if (batstate != 1 && ac == 0)
409                 fprintf(stderr, "Battery charging while not on AC!\n");
410         else if (batstate == 1 && ac == 1)
411                 fprintf(stderr, "Battery discharing while on AC!\n");
412
413         switch (item) {
414                 case BATTERY_TIME:
415                         if (batstate == 1 && battime != -1)
416                                 snprintf(buf, n, "%d:%2.2d", battime / 60, battime % 60);
417                         break;
418                 case BATTERY_STATUS:
419                         if (batstate == 1) // Discharging
420                                 snprintf(buf, n, "remaining %d%%", batcapacity);
421                         else
422                                 snprintf(buf, n, batstate == 2 ? "charging (%d%%)" :
423                                                 (batstate == 7 ? "absent/on AC" : "charged (%d%%)"),
424                                                 batcapacity);
425                         break;
426                 default:
427                         fprintf(stderr, "Unknown requested battery stat %d\n", item);
428         }
429 }
430
431 static int check_bat(const char *bat)
432 {
433         int batnum, numbatts;
434         char *endptr;
435         if (GETSYSCTL("hw.acpi.battery.units", numbatts)) {
436                 fprintf(stderr, "Cannot read sysctl \"hw.acpi.battery.units\"\n");
437                 return -1;
438         }
439         if (numbatts <= 0) {
440                 fprintf(stderr, "No battery unit detected\n");
441                 return -1;
442         }
443         if (!bat || (batnum = strtol(bat, &endptr, 10)) < 0 ||
444                         bat == endptr || batnum > numbatts) {
445                 fprintf(stderr, "Wrong battery unit requested\n", bat);
446                 return -1;
447         }
448         return batnum;
449 }
450
451 int get_battery_perct(const char *bat)
452 {
453         union acpi_battery_ioctl_arg battio;
454         int batnum, numbatts, acpifd;
455         int designcap, lastfulcap, batperct;
456
457         if ((battio.unit = batnum = check_bat(bat)) < 0)
458                 return 0;
459         if ((acpifd = open("/dev/acpi", O_RDONLY)) < 0) {
460                 fprintf(stderr, "Can't open ACPI device\n");
461                 return 0;
462         }
463         if (ioctl(acpifd, ACPIIO_BATT_GET_BIF, &battio) == -1) {
464                 fprintf(stderr, "Unable to get info for battery unit %d\n", batnum);
465                 return 0;
466         }
467         close(acpifd);
468         designcap = battio.bif.dcap;
469         lastfulcap = battio.bif.lfcap;
470         batperct = (designcap > 0 && lastfulcap > 0) ?
471                 (int) (((float) lastfulcap / designcap) * 100) : 0;
472         return batperct > 100 ? 100 : batperct;
473 }
474
475 int get_battery_perct_bar(const char *bar)
476 {
477         int batperct = get_battery_perct(bar);
478         return (int)(batperct * 2.56 - 1);
479 }
480
481 int open_acpi_temperature(const char *name)
482 {
483         /* Not applicable for FreeBSD. */
484         return 0;
485 }
486
487 void get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
488 {
489         int state;
490
491         if (!p_client_buffer || client_buffer_size <= 0) {
492                 return;
493         }
494
495         if (GETSYSCTL("hw.acpi.acline", state)) {
496                 fprintf(stderr, "Cannot read sysctl \"hw.acpi.acline\"\n");
497                 return;
498         }
499
500         if (state) {
501                 strncpy(p_client_buffer, "Running on AC Power", client_buffer_size);
502         } else {
503                 strncpy(p_client_buffer, "Running on battery", client_buffer_size);
504         }
505 }
506
507 void get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
508 {
509         /* not implemented */
510         if (p_client_buffer && client_buffer_size > 0) {
511                 memset(p_client_buffer, 0, client_buffer_size);
512         }
513 }
514
515 void get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
516 {
517         /* not implemented */
518         if (p_client_buffer && client_buffer_size > 0) {
519                 memset(p_client_buffer, 0, client_buffer_size);
520         }
521 }
522
523 void get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
524 {
525         /* not implemented */
526         if (p_client_buffer && client_buffer_size > 0) {
527                 memset(p_client_buffer, 0, client_buffer_size);
528         }
529 }
530
531 /* rdtsc() and get_freq_dynamic() copied from linux.c */
532
533 #if  defined(__i386) || defined(__x86_64)
534 __inline__ unsigned long long int rdtsc()
535 {
536         unsigned long long int x;
537
538         __asm__ volatile(".byte 0x0f, 0x31":"=A" (x));
539         return x;
540 }
541 #endif
542
543 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
544 void get_freq_dynamic(char *p_client_buffer, size_t client_buffer_size,
545                 const char *p_format, int divisor)
546 {
547 #if  defined(__i386) || defined(__x86_64)
548         struct timezone tz;
549         struct timeval tvstart, tvstop;
550         unsigned long long cycles[2];   /* gotta be 64 bit */
551         unsigned int microseconds;      /* total time taken */
552
553         memset(&tz, 0, sizeof(tz));
554
555         /* get this function in cached memory */
556         gettimeofday(&tvstart, &tz);
557         cycles[0] = rdtsc();
558         gettimeofday(&tvstart, &tz);
559
560         /* we don't trust that this is any specific length of time */
561         usleep(100);
562         cycles[1] = rdtsc();
563         gettimeofday(&tvstop, &tz);
564         microseconds = ((tvstop.tv_sec - tvstart.tv_sec) * 1000000) +
565                 (tvstop.tv_usec - tvstart.tv_usec);
566
567         snprintf(p_client_buffer, client_buffer_size, p_format,
568                 (float) ((cycles[1] - cycles[0]) / microseconds) / divisor);
569 #else
570         get_freq(p_client_buffer, client_buffer_size, p_format, divisor, 1);
571 #endif
572 }
573
574 /* void */
575 char get_freq(char *p_client_buffer, size_t client_buffer_size, const char *p_format,
576                 int divisor, unsigned int cpu)
577 {
578         int freq;
579         char *freq_sysctl;
580
581         freq_sysctl = (char *) calloc(16, sizeof(char));
582         if (freq_sysctl == NULL) {
583                 exit(-1);
584         }
585
586         snprintf(freq_sysctl, 16, "dev.cpu.%d.freq", (cpu - 1));
587
588         if (!p_client_buffer || client_buffer_size <= 0 || !p_format
589                         || divisor <= 0) {
590                 return 0;
591         }
592
593         if (GETSYSCTL(freq_sysctl, freq) == 0) {
594                 snprintf(p_client_buffer, client_buffer_size, p_format,
595                         (float) freq / divisor);
596         } else {
597                 snprintf(p_client_buffer, client_buffer_size, p_format, 0.0f);
598         }
599
600         free(freq_sysctl);
601         return 1;
602 }
603
604 void update_top()
605 {
606         proc_find_top(info.cpu, info.memu);
607 }
608
609 #if 0
610 void update_wifi_stats()
611 {
612         struct ifreq ifr;               /* interface stats */
613         struct wi_req wireq;
614         struct net_stat *ns;
615         struct ifaddrs *ifap, *ifa;
616         struct ifmediareq ifmr;
617         int s;
618
619         /* Get iface table */
620         if (getifaddrs(&ifap) < 0) {
621                 return;
622         }
623
624         for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
625                 ns = get_net_stat((const char *) ifa->ifa_name);
626
627                 s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
628
629                 /* Get media type */
630                 bzero(&ifmr, sizeof(ifmr));
631                 strlcpy(ifmr.ifm_name, ifa->ifa_name, IFNAMSIZ);
632                 if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) {
633                         close(s);
634                         return;
635                 }
636
637                 /* We can monitor only wireless interfaces
638                  * which are not in hostap mode */
639                 if ((ifmr.ifm_active & IFM_IEEE80211)
640                                 && !(ifmr.ifm_active & IFM_IEEE80211_HOSTAP)) {
641                         /* Get wi status */
642                         bzero(&ifr, sizeof(ifr));
643                         strlcpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ);
644                         wireq.wi_type = WI_RID_COMMS_QUALITY;
645                         wireq.wi_len = WI_MAX_DATALEN;
646                         ifr.ifr_data = (void *) &wireq;
647
648                         if (ioctl(s, SIOCGWAVELAN, (caddr_t) &ifr) < 0) {
649                                 perror("ioctl (getting wi status)");
650                                 exit(1);
651                         }
652
653                         /* wi_val[0] = quality
654                          * wi_val[1] = signal
655                          * wi_val[2] = noise */
656                         ns->linkstatus = (int) wireq.wi_val[1];
657                 }
658 cleanup:
659                 close(s);
660         }
661 }
662 #endif
663
664 void update_diskio()
665 {
666         int devs_count, num_selected, num_selections;
667         struct device_selection *dev_select = NULL;
668         long select_generation;
669         int dn;
670         static struct statinfo statinfo_cur;
671         struct diskio_stat *cur;
672
673         bzero(&statinfo_cur, sizeof(statinfo_cur));
674         statinfo_cur.dinfo = (struct devinfo *)calloc(1, sizeof(struct devinfo));
675         stats.current = stats.current_read = stats.current_write = 0;
676
677         if (devstat_getdevs(NULL, &statinfo_cur) < 0)
678                 return;
679
680         devs_count = statinfo_cur.dinfo->numdevs;
681         if (devstat_selectdevs(&dev_select, &num_selected, &num_selections,
682                         &select_generation, statinfo_cur.dinfo->generation,
683                         statinfo_cur.dinfo->devices, devs_count, NULL, 0, NULL, 0,
684                         DS_SELECT_ONLY, MAXSHOWDEVS, 1) >= 0) {
685                 for (dn = 0; dn < devs_count; ++dn) {
686                         int di;
687                         struct devstat *dev;
688
689                         di = dev_select[dn].position;
690                         dev = &statinfo_cur.dinfo->devices[di];
691
692                         for (cur = stats.next; cur; cur = cur->next) {
693                                 if (cur->dev && !strcmp(dev_select[dn].device_name, cur->dev)) {
694                                         cur->current = (dev->bytes[DEVSTAT_READ] +
695                                                         dev->bytes[DEVSTAT_WRITE] - cur->last) / 1024;
696                                         cur->current_read = (dev->bytes[DEVSTAT_READ] -
697                                                         cur->last_read) / 1024;
698                                         cur->current_write = (dev->bytes[DEVSTAT_WRITE] -
699                                                         cur->last_write) / 1024;
700                                         if (dev->bytes[DEVSTAT_READ] + dev->bytes[DEVSTAT_WRITE] <
701                                                         cur->last) {
702                                                 cur->current = 0;
703                                         }
704                                         if (dev->bytes[DEVSTAT_READ] < cur->last_read) {
705                                                 cur->current_read = 0;
706                                                 cur->current = cur->current_write;
707                                         }
708                                         if (dev->bytes[DEVSTAT_WRITE] < cur->last_write) {
709                                                 cur->current_write = 0;
710                                                 cur->current = cur->current_read;
711                                         }
712                                         cur->last = dev->bytes[DEVSTAT_READ] +
713                                                 dev->bytes[DEVSTAT_WRITE];
714                                         cur->last_read = dev->bytes[DEVSTAT_READ];
715                                         cur->last_write = dev->bytes[DEVSTAT_WRITE];
716                                 }
717                         }
718                 }
719
720                 free(dev_select);
721         }
722
723         free(statinfo_cur.dinfo);
724 }
725
726 void clear_diskio_stats()
727 {
728         struct diskio_stat *cur;
729         while (stats.next) {
730                 cur = stats.next;
731                 stats.next = stats.next->next;
732                 free(cur);
733         }
734 }
735
736 struct diskio_stat *prepare_diskio_stat(const char *s)
737 {
738         struct diskio_stat *new = 0;
739         struct stat sb;
740         int found = 0;
741         char device[text_buffer_size], fbuf[text_buffer_size];
742         static int rep = 0;
743         /* lookup existing or get new */
744         struct diskio_stat *cur = &stats;
745
746         if (!s)
747                 return cur;
748
749         while (cur->next) {
750                 cur = cur->next;
751                 if (!strcmp(cur->dev, s))
752                         return cur;
753         }
754
755         /* new dev */
756         if (!(cur->next = calloc(1, sizeof(struct diskio_stat)))) {
757                 ERR("out of memory allocating new disk stats struct");
758                 return NULL;
759         }
760         cur = cur->next;
761         cur->last = cur->last_read = cur->last_write = UINT_MAX;
762         if (strncmp(s, "/dev/", 5) == 0) {
763                 // supplied a /dev/device arg, so cut off the /dev part
764                 cur->dev = strndup(s + 5, text_buffer_size);
765         } else {
766                 cur->dev = strndup(s, text_buffer_size);
767         }
768         /*
769          * check that device actually exists
770          */
771         snprintf(device, text_buffer_size, "/dev/%s", new->dev);
772
773         if (stat(device, &sb)) {
774                 ERR("diskio device '%s' does not exist", s);
775                 return 0;
776         }
777         return cur;
778 }
779
780 /* While topless is obviously better, top is also not bad. */
781
782 int comparecpu(const void *a, const void *b)
783 {
784         if (((struct process *)a)->amount > ((struct process *)b)->amount) {
785                 return -1;
786         } else if (((struct process *)a)->amount < ((struct process *)b)->amount) {
787                 return 1;
788         } else {
789                 return 0;
790         }
791 }
792
793 int comparemem(const void *a, const void *b)
794 {
795         if (((struct process *)a)->totalmem > ((struct process *)b)->totalmem) {
796                 return -1;
797         } else if (((struct process *)a)->totalmem < ((struct process *)b)->totalmem) {
798                 return 1;
799         } else {
800                 return 0;
801         }
802 }
803
804 inline void proc_find_top(struct process **cpu, struct process **mem)
805 {
806         struct kinfo_proc *p;
807         int n_processes;
808         int i, j = 0;
809         struct process *processes;
810
811         int total_pages;
812
813         /* we get total pages count again to be sure it is up to date */
814         if (GETSYSCTL("vm.stats.vm.v_page_count", total_pages) != 0) {
815                 CRIT_ERR("Cannot read sysctl \"vm.stats.vm.v_page_count\"");
816         }
817
818         p = kvm_getprocs(kd, KERN_PROC_PROC, 0, &n_processes);
819         processes = malloc(n_processes * sizeof(struct process));
820
821         for (i = 0; i < n_processes; i++) {
822                 if (!((p[i].ki_flag & P_SYSTEM)) && p[i].ki_comm != NULL) {
823                         processes[j].pid = p[i].ki_pid;
824                         processes[j].name = strndup(p[i].ki_comm, text_buffer_size);
825                         processes[j].amount = 100.0 * p[i].ki_pctcpu / FSCALE;
826                         processes[j].totalmem = (float) (p[i].ki_rssize /
827                                 (float) total_pages) * 100.0;
828                         processes[j].vsize = p[i].ki_size;
829                         processes[j].rss = (p[i].ki_rssize * getpagesize());
830                         j++;
831                 }
832         }
833
834         qsort(processes, j - 1, sizeof(struct process), comparemem);
835         for (i = 0; i < 10 && i < n_processes; i++) {
836                 struct process *tmp, *ttmp;
837
838                 tmp = malloc(sizeof(struct process));
839                 tmp->pid = processes[i].pid;
840                 tmp->amount = processes[i].amount;
841                 tmp->totalmem = processes[i].totalmem;
842                 tmp->name = strndup(processes[i].name, text_buffer_size);
843                 tmp->rss = processes[i].rss;
844                 tmp->vsize = processes[i].vsize;
845
846                 ttmp = mem[i];
847                 mem[i] = tmp;
848                 if (ttmp != NULL) {
849                         free(ttmp->name);
850                         free(ttmp);
851                 }
852         }
853
854         qsort(processes, j - 1, sizeof(struct process), comparecpu);
855         for (i = 0; i < 10 && i < n_processes; i++) {
856                 struct process *tmp, *ttmp;
857
858                 tmp = malloc(sizeof(struct process));
859                 tmp->pid = processes[i].pid;
860                 tmp->amount = processes[i].amount;
861                 tmp->totalmem = processes[i].totalmem;
862                 tmp->name = strndup(processes[i].name, text_buffer_size);
863                 tmp->rss = processes[i].rss;
864                 tmp->vsize = processes[i].vsize;
865
866                 ttmp = cpu[i];
867                 cpu[i] = tmp;
868                 if (ttmp != NULL) {
869                         free(ttmp->name);
870                         free(ttmp);
871                 }
872         }
873
874 #if defined(FREEBSD_DEBUG)
875         printf("=====\nmem\n");
876         for (i = 0; i < 10; i++) {
877                 printf("%d: %s(%d) %.2f %ld %ld\n", i, mem[i]->name,
878                                 mem[i]->pid, mem[i]->totalmem, mem[i]->vsize, mem[i]->rss);
879         }
880 #endif
881
882         for (i = 0; i < j; i++) {
883                 free(processes[i].name);
884         }
885         free(processes);
886 }
887
888 #if     defined(i386) || defined(__i386__)
889 #define APMDEV          "/dev/apm"
890 #define APM_UNKNOWN     255
891
892 int apm_getinfo(int fd, apm_info_t aip)
893 {
894         if (ioctl(fd, APMIO_GETINFO, aip) == -1) {
895                 return -1;
896         }
897
898         return 0;
899 }
900
901 char *get_apm_adapter()
902 {
903         int fd;
904         struct apm_info info;
905         char *out;
906
907         out = (char *) calloc(16, sizeof(char));
908
909         fd = open(APMDEV, O_RDONLY);
910         if (fd < 0) {
911                 strncpy(out, "ERR", 16);
912                 return out;
913         }
914
915         if (apm_getinfo(fd, &info) != 0) {
916                 close(fd);
917                 strncpy(out, "ERR", 16);
918                 return out;
919         }
920         close(fd);
921
922         switch (info.ai_acline) {
923                 case 0:
924                         strncpy(out, "off-line", 16);
925                         return out;
926                         break;
927                 case 1:
928                         if (info.ai_batt_stat == 3) {
929                                 strncpy(out, "charging", 16);
930                                 return out;
931                         } else {
932                                 strncpy(out, "on-line", 16);
933                                 return out;
934                         }
935                         break;
936                 default:
937                         strncpy(out, "unknown", 16);
938                         return out;
939                         break;
940         }
941 }
942
943 char *get_apm_battery_life()
944 {
945         int fd;
946         u_int batt_life;
947         struct apm_info info;
948         char *out;
949
950         out = (char *) calloc(16, sizeof(char));
951
952         fd = open(APMDEV, O_RDONLY);
953         if (fd < 0) {
954                 strncpy(out, "ERR", 16);
955                 return out;
956         }
957
958         if (apm_getinfo(fd, &info) != 0) {
959                 close(fd);
960                 strncpy(out, "ERR", 16);
961                 return out;
962         }
963         close(fd);
964
965         batt_life = info.ai_batt_life;
966         if (batt_life == APM_UNKNOWN) {
967                 strncpy(out, "unknown", 16);
968         } else if (batt_life <= 100) {
969                 snprintf(out, 16, "%d%%", batt_life);
970                 return out;
971         } else {
972                 strncpy(out, "ERR", 16);
973         }
974
975         return out;
976 }
977
978 char *get_apm_battery_time()
979 {
980         int fd;
981         int batt_time;
982         int h, m, s;
983         struct apm_info info;
984         char *out;
985
986         out = (char *) calloc(16, sizeof(char));
987
988         fd = open(APMDEV, O_RDONLY);
989         if (fd < 0) {
990                 strncpy(out, "ERR", 16);
991                 return out;
992         }
993
994         if (apm_getinfo(fd, &info) != 0) {
995                 close(fd);
996                 strncpy(out, "ERR", 16);
997                 return out;
998         }
999         close(fd);
1000
1001         batt_time = info.ai_batt_time;
1002
1003         if (batt_time == -1) {
1004                 strncpy(out, "unknown", 16);
1005         } else {
1006                 h = batt_time;
1007                 s = h % 60;
1008                 h /= 60;
1009                 m = h % 60;
1010                 h /= 60;
1011                 snprintf(out, 16, "%2d:%02d:%02d", h, m, s);
1012         }
1013
1014         return out;
1015 }
1016
1017 #endif
1018
1019 void get_battery_short_status(char *buffer, unsigned int n, const char *bat)
1020 {
1021         get_battery_stuff(buffer, n, bat, BATTERY_STATUS);
1022         if (0 == strncmp("charging", buffer, 8)) {
1023                 buffer[0] = 'C';
1024                 memmove(buffer + 1, buffer + 8, n - 8);
1025         } else if (0 == strncmp("discharging", buffer, 11)) {
1026                 buffer[0] = 'D';
1027                 memmove(buffer + 1, buffer + 11, n - 11);
1028         } else if (0 == strncmp("absent/on AC", buffer, 12)) {
1029                 buffer[0] = 'A';
1030                 memmove(buffer + 1, buffer + 12, n - 12);
1031         }
1032 }
1033
1034 void update_entropy(void)
1035 {
1036         /* Not applicable for FreeBSD as it uses the yarrow prng. */
1037 }
1038
1039 /* empty stub so conky links */
1040 void free_all_processes(void)
1041 {
1042 }