summary refs log tree commit diff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2014-09-14 21:39:51 +0200
committerLudovic Courtès <ludo@gnu.org>2014-09-14 23:49:02 +0200
commit7585016f53e2e8be1f82ed303ae084464422c2a8 (patch)
treec318e4e69fd0aa7d24d5631c2ff62a0df30c1245
parent150d8e6414cad90e1da7d767251b874688e89e26 (diff)
downloadguix-7585016f53e2e8be1f82ed303ae084464422c2a8.tar.gz
syscalls: Add 'network-interfaces'.
* guix/build/syscalls.scm (SIOCGIFCONF, ifconf-struct,
  ifreq-struct-size): New variables.
  (%ioctl, bytevector->string-list, network-interfaces): New
  procedures.
* tests/syscalls.scm ("network-interfaces"): New test.
-rw-r--r--guix/build/syscalls.scm67
-rw-r--r--tests/syscalls.scm8
2 files changed, 73 insertions, 2 deletions
diff --git a/guix/build/syscalls.scm b/guix/build/syscalls.scm
index 7a1bad7331..cd2797219f 100644
--- a/guix/build/syscalls.scm
+++ b/guix/build/syscalls.scm
@@ -30,7 +30,8 @@
             MS_MOVE
             mount
             umount
-            processes))
+            processes
+            network-interfaces))
 
 ;;; Commentary:
 ;;;
@@ -180,4 +181,68 @@ user-land process."
                     (scandir "/proc"))
         <))
 
+
+;;;
+;;; Network interfaces.
+;;;
+
+(define SIOCGIFCONF                               ;from <bits/ioctls.h>
+  (if (string-contains %host-type "linux")
+      #x8912                                      ;GNU/Linux
+      #xf00801a4))                                ;GNU/Hurd
+
+(define ifconf-struct
+  ;; 'struct ifconf', from <net/if.h>.
+  (list int                                       ;int ifc_len
+        '*))                                      ;struct ifreq *ifc_ifcu
+
+(define ifreq-struct-size
+  ;; 'struct ifreq' begins with a char array containing the interface name,
+  ;; followed by a bunch of stuff.  This is its size in bytes.
+  (if (= 8 (sizeof '*))
+      40
+      32))
+
+(define %ioctl
+  ;; The most terrible interface, live from Scheme.
+  (pointer->procedure int
+                      (dynamic-func "ioctl" (dynamic-link))
+                      (list int unsigned-long '*)))
+
+(define (bytevector->string-list bv stride len)
+  "Return the null-terminated strings found in BV every STRIDE bytes.  Read at
+most LEN bytes from BV."
+  (let loop ((bytes  (take (bytevector->u8-list bv)
+                           (min len (bytevector-length bv))))
+             (result '()))
+    (match bytes
+      (()
+       (reverse result))
+      (_
+       (loop (drop bytes stride)
+             (cons (list->string (map integer->char
+                                      (take-while (negate zero?) bytes)))
+                   result))))))
+
+(define* (network-interfaces #:optional sock)
+  "Return the list of existing network interfaces."
+  (let* ((close? (not sock))
+         (sock   (or sock (socket SOCK_STREAM AF_INET 0)))
+         (len    (* ifreq-struct-size 10))
+         (reqs   (make-bytevector len))
+         (conf   (make-c-struct ifconf-struct
+                                (list len (bytevector->pointer reqs))))
+         (ret    (%ioctl (fileno sock) SIOCGIFCONF conf))
+         (err    (errno)))
+    (when close?
+      (close-port sock))
+    (if (zero? ret)
+        (bytevector->string-list reqs ifreq-struct-size
+                                 (match (parse-c-struct conf ifconf-struct)
+                                   ((len . _) len)))
+        (throw 'system-error "network-interface-list"
+               "network-interface-list: ~A"
+               (list (strerror err))
+               (list err)))))
+
 ;;; syscalls.scm ends here
diff --git a/tests/syscalls.scm b/tests/syscalls.scm
index ab34fc825b..fa6b67bf39 100644
--- a/tests/syscalls.scm
+++ b/tests/syscalls.scm
@@ -18,7 +18,8 @@
 
 (define-module (test-syscalls)
   #:use-module (guix build syscalls)
-  #:use-module (srfi srfi-64))
+  #:use-module (srfi srfi-64)
+  #:use-module (ice-9 match))
 
 ;; Test the (guix build syscalls) module, although there's not much that can
 ;; actually be tested without being root.
@@ -42,6 +43,11 @@
       ;; Both return values have been encountered in the wild.
       (memv (system-error-errno args) (list EPERM ENOENT)))))
 
+(test-assert "network-interfaces"
+  (match (network-interfaces)
+    (((? string? names) ..1)
+     (member "lo" names))))
+
 (test-end)