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