I really like the Lisp approach of accessing foreign functions; it puts the programmer in charge (as usual) instead of making him wait for some bindings to appear or get updated.
Here’s a little recipe that shows how to get the load averages (the thing uptime shows) in Lisp, which will be useful later when we build a solid logging foundation with Gary’s log5 package.
In case you don’t know, the load average shows an approximation of the number of processes in the system’s task queue, thus serving as indication for machine load.
First, let’s do the initialization work for CFFI, as pointed out in its user guide:
(asdf:oos 'asdf:load-op 'cffi)
(defpackage :cffi-user
(:use :common-lisp :cffi))
(in-package :cffi-user)
(define-foreign-library libc
(:unix (:or "libc.so.6" "libc.so.5" "libc.so"))
(t (:default "libc.so")))
(use-foreign-library libc)
Now we actually need to start thinking. How do we get at the numbers?
Let’s find the C function:
% apropos load | egrep -i "average|avg"
getloadavg (3) - get system load averages
[...]
% man 3 getloadavg
The man page gives us this prototype:
int getloadavg(double loadavg[], int nelem);
It also tells us that the first parameter will be filled with nelem samples and notes that (at least on my Linux system) the maximum number of samples is three, denoting the load averages of the last 1, 5 and 15 minutes. Let’s say that we want all three.
Now unfortunately the CFFI manual doesn’t say anything about arrays. But we can rewrite the prototype as
int getloadavg(double* loadavg, int nelem);
leading to the following CFFI function spec:
(defcfun "getloadavg" :int (loadavg :pointer) (nelem :int))
Now we are able to use foreign-alloc to allocate a pointer of the correct size (i.e. 3*sizeof(double)), and mem-aref to access the resulting array.
Combined with matching LOOP and FORMAT programs and the manual garbage collection we get:
(defun load-averages ()
(let ((loadavg (foreign-alloc :double :count 3)))
(getloadavg loadavg 3) ; note the imperative style we are forced to use
(prog1 ; we need to clean up after producing the return value
(format nil "~{~,2F~^ ~}" (loop for i from 0 to 2
collect (mem-aref loadavg :double i)))
(foreign-free loadavg))))
If you have questions regarding any part of that last snippet, feel free to ask.
Note that we don’t do any error checking here; getloadavg will return -1 on failure, although I can’t imagine why it would do so.
You can access the full code at http://paste.lisp.org/display/54746.
I hope this post wasn’t overly verbose (read: boring) to you.
It was my intention to make this understandable for beginners.