- Minor change to ${top} stuff to fix a possible
[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                         last_recv = ns->recv;
167                         last_trans = ns->trans;
168
169                         if (ifa->ifa_addr->sa_family != AF_LINK)
170                                 continue;
171
172                         ifd = (struct if_data *) ifa->ifa_data;
173                         r = ifd->ifi_ibytes;
174                         t = ifd->ifi_obytes;
175
176                         if (r < ns->last_read_recv)
177                                 ns->recv +=
178                                     ((long long) 4294967295U -
179                                         ns->last_read_recv) + r;
180                         else
181                                 ns->recv += (r - ns->last_read_recv);
182
183                         ns->last_read_recv = r;
184
185                         if (t < ns->last_read_trans)
186                                 ns->trans +=
187                                     ((long long) 4294967295U -
188                                         ns->last_read_trans) + t;
189                         else
190                                 ns->trans += (t - ns->last_read_trans);
191
192                         ns->last_read_trans = t;
193
194
195                         /* calculate speeds */
196                         ns->recv_speed = (ns->recv - last_recv) / delta;
197                         ns->trans_speed = (ns->trans - last_trans) / delta;
198                 }
199         }
200
201         freeifaddrs(ifap);
202 }
203
204 void
205 update_total_processes()
206 {
207         int n_processes;
208
209         kvm_getprocs(kd, KERN_PROC_ALL, 0, &n_processes);
210
211         info.procs = n_processes;
212 }
213
214 void
215 update_running_processes()
216 {
217         struct kinfo_proc *p;
218         int n_processes;
219         int i, cnt = 0;
220
221         p = kvm_getprocs(kd, KERN_PROC_ALL, 0, &n_processes);
222         for (i = 0; i < n_processes; i++) {
223 #if __FreeBSD__ < 5
224                 if (p[i].kp_proc.p_stat == SRUN)
225 #else
226                 if (p[i].ki_stat == SRUN)
227 #endif
228                         cnt++;
229         }
230
231         info.run_procs = cnt;
232 }
233
234 struct cpu_load_struct {
235         unsigned long load[5];
236 };
237
238 struct cpu_load_struct fresh = { {0, 0, 0, 0, 0} };
239 long cpu_used, oldtotal, oldused;
240
241 void
242 get_cpu_count()
243 {
244         /* int cpu_count = 0; */
245
246         /*
247          * XXX
248          * FreeBSD doesn't allow to get per CPU load stats
249          * on SMP machines. It's possible to get a CPU count,
250          * but as we fulfil only info.cpu_usage[0], it's better
251          * to report there's only one CPU. It should fix some bugs
252          * (e.g. cpugraph)
253          */
254 #if 0
255         if (GETSYSCTL("hw.ncpu", cpu_count) == 0)
256                 info.cpu_count = cpu_count;
257 #endif
258         info.cpu_count = 1;
259
260         info.cpu_usage = malloc(info.cpu_count * sizeof (float));
261         if (info.cpu_usage == NULL)
262                 CRIT_ERR("malloc");
263 }
264
265 /* XXX: SMP support */
266 void
267 update_cpu_usage()
268 {
269         long used, total;
270         long cp_time[CPUSTATES];
271         size_t len = sizeof (cp_time);
272
273         if (cpu_setup == 0) {
274                 get_cpu_count();
275                 cpu_setup = 1;
276         }
277
278         if (sysctlbyname("kern.cp_time", &cp_time, &len, NULL, 0) < 0) {
279                 (void) fprintf(stderr, "Cannot get kern.cp_time");
280         }
281
282         fresh.load[0] = cp_time[CP_USER];
283         fresh.load[1] = cp_time[CP_NICE];
284         fresh.load[2] = cp_time[CP_SYS];
285         fresh.load[3] = cp_time[CP_IDLE];
286         fresh.load[4] = cp_time[CP_IDLE];
287
288         used = fresh.load[0] + fresh.load[1] + fresh.load[2];
289         total =
290             fresh.load[0] + fresh.load[1] + fresh.load[2] + fresh.load[3];
291
292         if ((total - oldtotal) != 0) {
293                 info.cpu_usage[0] = ((double) (used - oldused)) /
294                         (double) (total - oldtotal);
295         } else {
296                 info.cpu_usage[0] = 0;
297         }
298
299         oldused = used;
300         oldtotal = total;
301 }
302
303 double
304 get_i2c_info(int *fd, int arg, char *devtype, char *type)
305 {
306         return (0);
307 }
308
309 void
310 update_load_average()
311 {
312         double v[3];
313         getloadavg(v, 3);
314
315         info.loadavg[0] = (float) v[0];
316         info.loadavg[1] = (float) v[1];
317         info.loadavg[2] = (float) v[2];
318 }
319
320 double
321 get_acpi_temperature(int fd)
322 {
323         int temp;
324
325         if (GETSYSCTL("hw.acpi.thermal.tz0.temperature", temp)) {
326                 fprintf(stderr,
327                 "Cannot read sysctl \"hw.acpi.thermal.tz0.temperature\"\n");
328                 return (0.0);
329         }
330
331         return (KELVTOC(temp));
332 }
333
334 void
335 get_battery_stuff(char *buf, unsigned int n, const char *bat)
336 {
337         int battime;
338
339         if (GETSYSCTL("hw.acpi.battery.time", battime))
340                 (void) fprintf(stderr,
341                         "Cannot read sysctl \"hw.acpi.battery.time\"\n");
342
343         if (battime != -1)
344                 snprintf(buf, n, "Discharging, remaining %d:%2.2d",
345                         battime / 60, battime % 60);
346         else
347                 snprintf(buf, n, "Battery is charging");
348 }
349
350 int
351 open_i2c_sensor(const char *dev, const char *type, int n, int *div,
352                 char *devtype)
353 {
354         return (0);
355 }
356
357 int
358 open_acpi_temperature(const char *name)
359 {
360         return (0);
361 }
362
363 void
364 get_acpi_ac_adapter(char *p_client_buffer, size_t client_buffer_size)
365 {
366         int state;
367
368         if (!p_client_buffer || client_buffer_size <= 0)
369                 return;
370
371         if (GETSYSCTL("hw.acpi.acline", state)) {
372                 fprintf(stderr,
373                         "Cannot read sysctl \"hw.acpi.acline\"\n");
374                 return;
375         }
376
377
378         if (state)
379                 strncpy(p_client_buffer, "Running on AC Power",
380                                 client_buffer_size);
381         else
382                 strncpy(p_client_buffer, "Running on battery",
383                                 client_buffer_size);
384
385 }
386
387 void
388 get_acpi_fan(char *p_client_buffer, size_t client_buffer_size)
389 {
390         if (!p_client_buffer || client_buffer_size <= 0)
391                 return;
392
393         /* not implemented */
394         memset(p_client_buffer, 0, client_buffer_size);
395 }
396
397 void
398 get_adt746x_cpu(char *p_client_buffer, size_t client_buffer_size)
399 {
400         if (!p_client_buffer || client_buffer_size <= 0)
401                 return;
402
403         /* not implemented */
404         memset(p_client_buffer, 0, client_buffer_size);
405 }
406
407 void
408 get_adt746x_fan(char *p_client_buffer, size_t client_buffer_size)
409 {
410         if (!p_client_buffer || client_buffer_size <= 0)
411                 return;
412
413         /* not implemented */
414         memset(p_client_buffer, 0, client_buffer_size);
415 }
416
417 /* rdtsc() and get_freq_dynamic() copied from linux.c */
418
419 #if  defined(__i386) || defined(__x86_64)
420 __inline__ unsigned long long int
421 rdtsc()
422 {
423         unsigned long long int x;
424         __asm__ volatile(".byte 0x0f, 0x31":"=A" (x));
425         return (x);
426 }
427 #endif
428
429 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
430 void
431 get_freq_dynamic(char *p_client_buffer, size_t client_buffer_size,
432                 char *p_format, int divisor)
433 {
434 #if  defined(__i386) || defined(__x86_64)
435         struct timezone tz;
436         struct timeval tvstart, tvstop;
437         unsigned long long cycles[2];   /* gotta be 64 bit */
438         unsigned int microseconds;      /* total time taken */
439
440         memset(&tz, 0, sizeof (tz));
441
442         /* get this function in cached memory */
443         gettimeofday(&tvstart, &tz);
444         cycles[0] = rdtsc();
445         gettimeofday(&tvstart, &tz);
446
447         /* we don't trust that this is any specific length of time */
448         usleep(100);
449         cycles[1] = rdtsc();
450         gettimeofday(&tvstop, &tz);
451         microseconds = ((tvstop.tv_sec - tvstart.tv_sec) * 1000000) +
452                 (tvstop.tv_usec - tvstart.tv_usec);
453
454         snprintf(p_client_buffer, client_buffer_size, p_format,
455                 (float)((cycles[1] - cycles[0]) / microseconds) / divisor);
456 #else
457         get_freq(p_client_buffer, client_buffer_size, p_format, divisor);
458 #endif
459 }
460
461 /* return system frequency in MHz (use divisor=1) or GHz (use divisor=1000) */
462 void
463 get_freq(char *p_client_buffer, size_t client_buffer_size,
464                 char *p_format, int divisor)
465 {
466         int freq;
467
468         if (!p_client_buffer || client_buffer_size <= 0
469                         || !p_format || divisor <= 0)
470                 return;
471
472         if (GETSYSCTL("dev.cpu.0.freq", freq) == 0)
473                 snprintf(p_client_buffer, client_buffer_size,
474                                 p_format, freq/divisor);
475         else
476                 snprintf(p_client_buffer, client_buffer_size, p_format, 0.0f);
477 }
478
479 void
480 update_top()
481 {
482         proc_find_top(info.cpu, info.memu);
483 }
484
485 void
486 update_wifi_stats()
487 {
488         /* XXX */
489 }
490 void
491 update_diskio()
492 {
493         int devs_count,
494             num_selected,
495             num_selections;
496         struct device_selection *dev_select = NULL;
497         long select_generation;
498         int dn;
499         static struct statinfo  statinfo_cur;
500         u_int64_t diskio_current = 0;
501
502         bzero(&statinfo_cur, sizeof (statinfo_cur));
503         statinfo_cur.dinfo = (struct devinfo *)malloc(sizeof (struct devinfo));
504         bzero(statinfo_cur.dinfo, sizeof (struct devinfo));
505
506         if (devstat_getdevs(NULL, &statinfo_cur) < 0)
507                 return;
508
509         devs_count = statinfo_cur.dinfo->numdevs;
510         if (devstat_selectdevs(&dev_select, &num_selected, &num_selections,
511                         &select_generation, statinfo_cur.dinfo->generation,
512                         statinfo_cur.dinfo->devices, devs_count, NULL, 0,
513                         NULL, 0, DS_SELECT_ONLY, MAXSHOWDEVS, 1) >= 0) {
514                 for (dn = 0; dn < devs_count; ++dn) {
515                         int di;
516                         struct devstat  *dev;
517
518                         di = dev_select[dn].position;
519                         dev = &statinfo_cur.dinfo->devices[di];
520
521                         diskio_current += dev->bytes[DEVSTAT_READ] +
522                                 dev->bytes[DEVSTAT_WRITE];
523                 }
524
525                 free(dev_select);
526         }
527
528         /*
529          * Since we return (diskio_total_current - diskio_total_old), first
530          * frame will be way too high (it will be equal to
531          * diskio_total_current, i.e. all disk I/O since boot). That's why
532          * it is better to return 0 first time;
533          */
534         if (diskio_setup == 0) {
535                 diskio_setup = 1;
536                 diskio_value = 0;
537         } else
538                 diskio_value = (unsigned int)((diskio_current - diskio_prev)/
539                                 1024);
540         diskio_prev = diskio_current;
541
542         free(statinfo_cur.dinfo);
543 }
544
545 /*
546  * While topless is obviously better, top is also not bad.
547  */
548
549 int
550 comparecpu(const void *a, const void *b)
551 {
552         if (((struct process *)a)->amount > ((struct process *)b)->amount)
553                 return (-1);
554
555         if (((struct process *)a)->amount < ((struct process *)b)->amount)
556                 return (1);
557
558         return (0);
559 }
560
561 int
562 comparemem(const void *a, const void *b)
563 {
564         if (((struct process *)a)->totalmem > ((struct process *)b)->totalmem)
565                 return (-1);
566
567         if (((struct process *)a)->totalmem < ((struct process *)b)->totalmem)
568                 return (1);
569
570         return (0);
571 }
572
573 inline void
574 proc_find_top(struct process **cpu, struct process **mem)
575 {
576         struct kinfo_proc *p;
577         int n_processes;
578         int i, j = 0;
579         struct process *processes;
580
581         int total_pages;
582
583         /* we get total pages count again to be sure it is up to date */
584         if (GETSYSCTL("vm.stats.vm.v_page_count", total_pages) != 0)
585                 CRIT_ERR("Cannot read sysctl"
586                         "\"vm.stats.vm.v_page_count\"");
587
588         p = kvm_getprocs(kd, KERN_PROC_PROC, 0, &n_processes);
589         processes = malloc(n_processes * sizeof (struct process));
590
591         for (i = 0; i < n_processes; i++) {
592                 if (!((p[i].ki_flag & P_SYSTEM)) &&
593                                 p[i].ki_comm != NULL) {
594                         processes[j].pid = p[i].ki_pid;
595                         processes[j].name =  strdup(p[i].ki_comm);
596                         processes[j].amount = 100.0 *
597                                 p[i].ki_pctcpu / FSCALE;
598                         processes[j].totalmem = (float)(p[i].ki_rssize /
599                                         (float)total_pages) * 100.0;
600                         j++;
601                 }
602         }
603
604         qsort(processes, j - 1, sizeof (struct process), comparemem);
605         for (i = 0; i < 10; i++) {
606                 struct process *tmp, *ttmp;
607
608                 tmp = malloc(sizeof (struct process));
609                 tmp->pid = processes[i].pid;
610                 tmp->amount = processes[i].amount;
611                 tmp->totalmem = processes[i].totalmem;
612                 tmp->name = strdup(processes[i].name);
613
614                 ttmp = mem[i];
615                 mem[i] = tmp;
616                 if (ttmp != NULL) {
617                         free(ttmp->name);
618                         free(ttmp);
619                 }
620         }
621
622         qsort(processes, j - 1, sizeof (struct process), comparecpu);
623         for (i = 0; i < 10; i++) {
624                 struct process *tmp, *ttmp;
625
626                 tmp = malloc(sizeof (struct process));
627                 tmp->pid = processes[i].pid;
628                 tmp->amount = processes[i].amount;
629                 tmp->totalmem = processes[i].totalmem;
630                 tmp->name = strdup(processes[i].name);
631
632                 ttmp = cpu[i];
633                 cpu[i] = tmp;
634                 if (ttmp != NULL) {
635                         free(ttmp->name);
636                         free(ttmp);
637                 }
638         }
639
640 #if defined(FREEBSD_DEBUG)
641         printf("=====\nmem\n");
642         for (i = 0; i < 10; i++) {
643                 printf("%d: %s(%d) %.2f\n", i, mem[i]->name,
644                                 mem[i]->pid, mem[i]->totalmem);
645         }
646 #endif
647
648         for (i = 0; i < j; free(processes[i++].name));
649         free(processes);
650 }
651
652 #if     defined(i386) || defined(__i386__)
653 #define APMDEV          "/dev/apm"
654 #define APM_UNKNOWN     255
655
656 int
657 apm_getinfo(int fd, apm_info_t aip)
658 {
659         if (ioctl(fd, APMIO_GETINFO, aip) == -1)
660                 return (-1);
661
662         return (0);
663 }
664
665 char
666 *get_apm_adapter()
667 {
668         int fd;
669         struct apm_info info;
670
671         fd = open(APMDEV, O_RDONLY);
672         if (fd < 0)
673                 return ("ERR");
674
675         if (apm_getinfo(fd, &info) != 0) {
676                 close(fd);
677                 return ("ERR");
678         }
679         close(fd);
680
681         switch (info.ai_acline) {
682                 case 0:
683                         return ("off-line");
684                         break;
685                 case 1:
686                         if (info.ai_batt_stat == 3)
687                                 return ("charging");
688                         else
689                                 return ("on-line");
690                         break;
691                 default:
692                         return ("unknown");
693                         break;
694         }
695 }
696
697 char
698 *get_apm_battery_life()
699 {
700         int fd;
701         u_int batt_life;
702         struct apm_info info;
703         char *out;
704
705         out = (char *)calloc(16, sizeof (char));
706
707         fd = open(APMDEV, O_RDONLY);
708         if (fd < 0) {
709                 strncpy(out, "ERR", 16);
710                 return (out);
711         }
712
713         if (apm_getinfo(fd, &info) != 0) {
714                 close(fd);
715                 strncpy(out, "ERR", 16);
716                 return (out);
717         }
718         close(fd);
719
720         batt_life = info.ai_batt_life;
721         if (batt_life == APM_UNKNOWN)
722                 strncpy(out, "unknown", 16);
723         else if (batt_life <= 100) {
724                 snprintf(out, 16, "%d%%", batt_life);
725                 return (out);
726         } else
727                 strncpy(out, "ERR", 16);
728
729         return (out);
730 }
731
732 char
733 *get_apm_battery_time()
734 {
735         int fd;
736         int batt_time;
737         int h, m, s;
738         struct apm_info info;
739         char *out;
740
741         out = (char *)calloc(16, sizeof (char));
742
743         fd = open(APMDEV, O_RDONLY);
744         if (fd < 0) {
745                 strncpy(out, "ERR", 16);
746                 return (out);
747         }
748
749         if (apm_getinfo(fd, &info) != 0) {
750                 close(fd);
751                 strncpy(out, "ERR", 16);
752                 return (out);
753         }
754         close(fd);
755
756         batt_time = info.ai_batt_time;
757
758         if (batt_time == -1)
759                 strncpy(out, "unknown", 16);
760         else {
761                 h = batt_time;
762                 s = h % 60;
763                 h /= 60;
764                 m = h % 60;
765                 h /= 60;
766                 snprintf(out, 16, "%2d:%02d:%02d", h, m, s);
767         }
768
769         return (out);
770 }
771
772 #endif
773
774 /* empty stub so conky links */
775 void
776 free_all_processes(void)
777 {
778 }