gnu: nss-pam-ldapd: Update to 0.9.8.
[jackhill/guix/guix.git] / guix / build / syscalls.scm
CommitLineData
29fa45f4 1;;; GNU Guix --- Functional package management for GNU
a6e0ae40 2;;; Copyright © 2014, 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org>
577c02eb 3;;; Copyright © 2015 David Thompson <davet@gnu.org>
4f8cede0 4;;; Copyright © 2015 Mark H Weaver <mhw@netris.org>
8336df06 5;;; Copyright © 2017 Mathieu Othacehe <m.othacehe@gmail.com>
29fa45f4
LC
6;;;
7;;; This file is part of GNU Guix.
8;;;
9;;; GNU Guix is free software; you can redistribute it and/or modify it
10;;; under the terms of the GNU General Public License as published by
11;;; the Free Software Foundation; either version 3 of the License, or (at
12;;; your option) any later version.
13;;;
14;;; GNU Guix is distributed in the hope that it will be useful, but
15;;; WITHOUT ANY WARRANTY; without even the implied warranty of
16;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17;;; GNU General Public License for more details.
18;;;
19;;; You should have received a copy of the GNU General Public License
20;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
21
22(define-module (guix build syscalls)
23 #:use-module (system foreign)
24 #:use-module (rnrs bytevectors)
3483f004 25 #:autoload (ice-9 binary-ports) (get-bytevector-n)
29fa45f4 26 #:use-module (srfi srfi-1)
e7f5691d
LC
27 #:use-module (srfi srfi-9)
28 #:use-module (srfi srfi-9 gnu)
26ffb693 29 #:use-module (srfi srfi-11)
15030972 30 #:use-module (srfi srfi-19)
fa73c193 31 #:use-module (srfi srfi-26)
29fa45f4 32 #:use-module (ice-9 rdelim)
4d54785c 33 #:use-module (ice-9 regex)
29fa45f4 34 #:use-module (ice-9 match)
17a4d344 35 #:use-module (ice-9 ftw)
26ffb693 36 #:export (MS_RDONLY
577c02eb
DT
37 MS_NOSUID
38 MS_NODEV
39 MS_NOEXEC
29fa45f4
LC
40 MS_REMOUNT
41 MS_BIND
42 MS_MOVE
577c02eb 43 MS_STRICTATIME
b16d138a
DT
44 MNT_FORCE
45 MNT_DETACH
46 MNT_EXPIRE
47 UMOUNT_NOFOLLOW
ea982704 48 restart-on-EINTR
ccea821b 49 mount-points
715fc9d4
LC
50 swapon
51 swapoff
a1f70878
LC
52
53 file-system?
54 file-system-type
55 file-system-block-size
56 file-system-block-count
57 file-system-blocks-free
58 file-system-blocks-available
59 file-system-file-count
60 file-system-free-file-nodes
61 file-system-identifier
62 file-system-maximum-name-length
63 file-system-fragment-size
200dac06 64 file-system-mount-flags
a1f70878 65 statfs
65f224dc 66 free-disk-space
a1f70878 67
7585016f 68 processes
b4abdeb6 69 mkdtemp!
1752a17a 70 fdatasync
df3ce5c1 71 pivot-root
fa73c193 72 scandir*
4e0ea3eb 73 fcntl-flock
973eea34 74
aa401f9b
LC
75 set-thread-name
76 thread-name
77
ee78d024
DT
78 CLONE_CHILD_CLEARTID
79 CLONE_CHILD_SETTID
8950ed11
DT
80 CLONE_NEWNS
81 CLONE_NEWUTS
82 CLONE_NEWIPC
83 CLONE_NEWUSER
84 CLONE_NEWPID
85 CLONE_NEWNET
86 clone
43ace6ea 87 setns
8950ed11 88
3ca33769
LC
89 PF_PACKET
90 AF_PACKET
b89e7405
LC
91 all-network-interface-names
92 network-interface-names
67e5f3b7 93 network-interface-netmask
c9bf64d6
LC
94 loopback-network-interface?
95 network-interface-address
67e5f3b7 96 set-network-interface-netmask
6944fdbd 97 set-network-interface-up
e7f5691d 98 configure-network-interface
9e38e3cf
LC
99 add-network-route/gateway
100 delete-network-route
e7f5691d
LC
101
102 interface?
103 interface-name
104 interface-flags
105 interface-address
106 interface-netmask
107 interface-broadcast-address
29ff6d9f
LC
108 network-interfaces
109
ae4ff9f3
LC
110 termios?
111 termios-input-flags
112 termios-output-flags
113 termios-control-flags
114 termios-local-flags
115 termios-line-discipline
116 termios-control-chars
117 termios-input-speed
118 termios-output-speed
119 local-flags
a8f3424b 120 tcsetattr-action
ae4ff9f3
LC
121 tcgetattr
122 tcsetattr
123
29ff6d9f
LC
124 window-size?
125 window-size-rows
126 window-size-columns
127 window-size-x-pixels
128 window-size-y-pixels
129 terminal-window-size
15030972
LC
130 terminal-columns
131
132 utmpx?
133 utmpx-login-type
134 utmpx-pid
135 utmpx-line
136 utmpx-id
137 utmpx-user
138 utmpx-host
139 utmpx-termination-status
140 utmpx-exit-status
141 utmpx-session-id
142 utmpx-time
143 utmpx-address
144 login-type
3483f004
LC
145 utmpx-entries
146 (read-utmpx-from-port . read-utmpx)))
29fa45f4
LC
147
148;;; Commentary:
149;;;
150;;; This module provides bindings to libc's syscall wrappers. It uses the
8336df06
MO
151;;; FFI, and thus requires a dynamically-linked Guile.
152;;;
153;;; Some syscalls are already defined in statically-linked Guile by applying
154;;; 'guile-linux-syscalls.patch'.
155;;;
156;;; Visibility of syscall's symbols shared between this module and static Guile
157;;; is a bit delicate. It is handled by 'define-as-needed' macro.
158;;;
159;;; This macro is used to export symbols in dynamic Guile context, and to
160;;; re-export them in static Guile context.
161;;;
162;;; This way, even if they don't appear in #:export list, it is safe to use
163;;; syscalls from this module in static or dynamic Guile context.
29fa45f4
LC
164;;;
165;;; Code:
166
73f38d5f
LC
167\f
168;;;
169;;; Packed structures.
170;;;
171
172(define-syntax sizeof*
173 ;; XXX: This duplicates 'compile-time-value'.
00cd4197 174 (syntax-rules (int128 array)
73f38d5f
LC
175 ((_ int128)
176 16)
00cd4197
LC
177 ((_ (array type n))
178 (* (sizeof* type) n))
73f38d5f
LC
179 ((_ type)
180 (let-syntax ((v (lambda (s)
181 (let ((val (sizeof type)))
182 (syntax-case s ()
183 (_ val))))))
184 v))))
185
186(define-syntax alignof*
187 ;; XXX: This duplicates 'compile-time-value'.
00cd4197 188 (syntax-rules (int128 array)
73f38d5f
LC
189 ((_ int128)
190 16)
00cd4197
LC
191 ((_ (array type n))
192 (alignof* type))
73f38d5f
LC
193 ((_ type)
194 (let-syntax ((v (lambda (s)
195 (let ((val (alignof type)))
196 (syntax-case s ()
197 (_ val))))))
198 v))))
199
200(define-syntax align ;as found in (system foreign)
201 (syntax-rules (~)
202 "Add to OFFSET whatever it takes to get proper alignment for TYPE."
203 ((_ offset (type ~ endianness))
204 (align offset type))
205 ((_ offset type)
206 (1+ (logior (1- offset) (1- (alignof* type)))))))
207
208(define-syntax type-size
209 (syntax-rules (~)
210 ((_ (type ~ order))
211 (sizeof* type))
212 ((_ type)
213 (sizeof* type))))
214
785cfa87
LC
215(define-syntax struct-alignment
216 (syntax-rules ()
217 "Compute the alignment for the aggregate made of TYPES at OFFSET. The
218result is the alignment of the \"most strictly aligned component\"."
219 ((_ offset types ...)
220 (max (align offset types) ...))))
221
222(define-syntax struct-size
223 (syntax-rules ()
224 "Return the size in bytes of the structure made of TYPES."
225 ((_ offset (types-processed ...))
226 ;; The SysV ABI P.S. says: "Aggregates (structures and arrays) and unions
227 ;; assume the alignment of their most strictly aligned component." As an
228 ;; example, a struct such as "int32, int16" has size 8, not 6.
229 (1+ (logior (1- offset)
230 (1- (struct-alignment offset types-processed ...)))))
231 ((_ offset (types-processed ...) type0 types ...)
232 (struct-size (+ (type-size type0) (align offset type0))
233 (type0 types-processed ...)
234 types ...))))
235
73f38d5f 236(define-syntax write-type
f43714e6 237 (syntax-rules (~ array *)
73f38d5f
LC
238 ((_ bv offset (type ~ order) value)
239 (bytevector-uint-set! bv offset value
240 (endianness order) (sizeof* type)))
00cd4197
LC
241 ((_ bv offset (array type n) value)
242 (let loop ((i 0)
243 (value value)
244 (o offset))
245 (unless (= i n)
246 (match value
247 ((head . tail)
248 (write-type bv o type head)
249 (loop (+ 1 i) tail (+ o (sizeof* type))))))))
f43714e6
LC
250 ((_ bv offset '* value)
251 (bytevector-uint-set! bv offset (pointer-address value)
252 (native-endianness) (sizeof* '*)))
73f38d5f
LC
253 ((_ bv offset type value)
254 (bytevector-uint-set! bv offset value
255 (native-endianness) (sizeof* type)))))
256
257(define-syntax write-types
258 (syntax-rules ()
259 ((_ bv offset () ())
260 #t)
261 ((_ bv offset (type0 types ...) (field0 fields ...))
262 (begin
263 (write-type bv (align offset type0) type0 field0)
264 (write-types bv
265 (+ (align offset type0) (type-size type0))
266 (types ...) (fields ...))))))
267
268(define-syntax read-type
00cd4197 269 (syntax-rules (~ array quote *)
73f38d5f
LC
270 ((_ bv offset '*)
271 (make-pointer (bytevector-uint-ref bv offset
272 (native-endianness)
273 (sizeof* '*))))
274 ((_ bv offset (type ~ order))
275 (bytevector-uint-ref bv offset
276 (endianness order) (sizeof* type)))
00cd4197
LC
277 ((_ bv offset (array type n))
278 (unfold (lambda (i) (= i n))
279 (lambda (i)
280 (read-type bv (+ offset (* i (sizeof* type))) type))
281 1+
282 0))
73f38d5f
LC
283 ((_ bv offset type)
284 (bytevector-uint-ref bv offset
285 (native-endianness) (sizeof* type)))))
286
287(define-syntax read-types
288 (syntax-rules ()
289 ((_ return bv offset () (values ...))
290 (return values ...))
291 ((_ return bv offset (type0 types ...) (values ...))
292 (read-types return
293 bv
294 (+ (align offset type0) (type-size type0))
295 (types ...)
296 (values ... (read-type bv
297 (align offset type0)
298 type0))))))
299
8eb790f3
LC
300(define-syntax define-c-struct-macro
301 (syntax-rules ()
302 "Define NAME as a macro that can be queried to get information about the C
303struct it represents. In particular:
304
305 (NAME field-offset FIELD)
306
307returns the offset in bytes of FIELD within the C struct represented by NAME."
308 ((_ name ((fields types) ...))
309 (define-c-struct-macro name
310 (fields ...) 0 ()
311 ((fields types) ...)))
312 ((_ name (fields ...) offset (clauses ...) ((field type) rest ...))
313 (define-c-struct-macro name
314 (fields ...)
315 (+ (align offset type) (type-size type))
316 (clauses ... ((_ field-offset field) (align offset type)))
317 (rest ...)))
318 ((_ name (fields ...) offset (clauses ...) ())
319 (define-syntax name
320 (syntax-rules (field-offset fields ...)
321 clauses ...)))))
322
73f38d5f
LC
323(define-syntax define-c-struct
324 (syntax-rules ()
785cfa87
LC
325 "Define SIZE as the size in bytes of the C structure made of FIELDS. READ
326as a deserializer and WRITE! as a serializer for the C structure with the
327given TYPES. READ uses WRAP-FIELDS to return its value."
328 ((_ name size wrap-fields read write! (fields types) ...)
73f38d5f 329 (begin
8eb790f3
LC
330 (define-c-struct-macro name
331 ((fields types) ...))
785cfa87
LC
332 (define size
333 (struct-size 0 () types ...))
73f38d5f
LC
334 (define (write! bv offset fields ...)
335 (write-types bv offset (types ...) (fields ...)))
f77863a0 336 (define* (read bv #:optional (offset 0))
73f38d5f
LC
337 (read-types wrap-fields bv offset (types ...) ()))))))
338
8eb790f3
LC
339(define-syntax-rule (c-struct-field-offset type field)
340 "Return the offset in BYTES of FIELD within TYPE, where TYPE is a C struct
341defined with 'define-c-struct' and FIELD is a field identifier. An
342expansion-time error is raised if FIELD does not exist in TYPE."
343 (type field-offset field))
344
73f38d5f
LC
345\f
346;;;
347;;; FFI.
348;;;
349
29fa45f4 350(define %libc-errno-pointer
26ffb693 351 ;; Glibc's 'errno' pointer, for use with Guile < 2.0.12.
2ff0da02
LC
352 (let ((errno-loc (false-if-exception
353 (dynamic-func "__errno_location" (dynamic-link)))))
29fa45f4
LC
354 (and errno-loc
355 (let ((proc (pointer->procedure '* errno-loc '())))
356 (proc)))))
357
26ffb693 358(define errno ;for Guile < 2.0.12
29fa45f4
LC
359 (if %libc-errno-pointer
360 (let ((bv (pointer->bytevector %libc-errno-pointer (sizeof int))))
361 (lambda ()
362 "Return the current errno."
363 ;; XXX: We assume that nothing changes 'errno' while we're doing all this.
364 ;; In particular, that means that no async must be running here.
365
366 ;; Use one of the fixed-size native-ref procedures because they are
367 ;; optimized down to a single VM instruction, which reduces the risk
368 ;; that we fiddle with 'errno' (needed on Guile 2.0.5, libc 2.11.)
369 (let-syntax ((ref (lambda (s)
370 (syntax-case s ()
371 ((_ bv)
372 (case (sizeof int)
373 ((4)
374 #'(bytevector-s32-native-ref bv 0))
375 ((8)
376 #'(bytevector-s64-native-ref bv 0))
377 (else
378 (error "unsupported 'int' size"
379 (sizeof int)))))))))
380 (ref bv))))
381 (lambda () 0)))
382
ea982704
LC
383(define (call-with-restart-on-EINTR thunk)
384 (let loop ()
385 (catch 'system-error
386 thunk
387 (lambda args
388 (if (= (system-error-errno args) EINTR)
389 (loop)
390 (apply throw args))))))
391
392(define-syntax-rule (restart-on-EINTR expr)
393 "Evaluate EXPR and restart upon EINTR. Return the value of EXPR."
394 (call-with-restart-on-EINTR (lambda () expr)))
395
4f8cede0 396(define (syscall->procedure return-type name argument-types)
26ffb693
LC
397 "Return a procedure that wraps the C function NAME using the dynamic FFI,
398and that returns two values: NAME's return value, and errno.
399
4f8cede0
MW
400If an error occurs while creating the binding, defer the error report until
401the returned procedure is called."
402 (catch #t
403 (lambda ()
404 (let ((ptr (dynamic-func name (dynamic-link))))
26ffb693
LC
405 ;; The #:return-errno? facility was introduced in Guile 2.0.12.
406 ;; Support older versions of Guile by catching 'wrong-number-of-args'.
407 (catch 'wrong-number-of-args
408 (lambda ()
409 (pointer->procedure return-type ptr argument-types
410 #:return-errno? #t))
411 (lambda (key . rest)
412 (let ((proc (pointer->procedure return-type ptr argument-types)))
413 (lambda args
414 (let ((result (apply proc args))
415 (err (errno)))
416 (values result err))))))))
4f8cede0
MW
417 (lambda args
418 (lambda _
419 (error (format #f "~a: syscall->procedure failed: ~s"
420 name args))))))
421
8336df06
MO
422(define-syntax define-as-needed
423 (syntax-rules ()
424 "Define VARIABLE. If VARIABLE already exists in (guile) then re-export it,
425 otherwise export the newly-defined VARIABLE."
426 ((_ (proc args ...) body ...)
427 (define-as-needed proc (lambda* (args ...) body ...)))
428 ((_ variable value)
429 (begin
430 (when (module-defined? the-scm-module 'variable)
431 (re-export variable))
432
433 (define variable
434 (if (module-defined? the-scm-module 'variable)
435 (module-ref the-scm-module 'variable)
436 value))
437
438 (unless (module-defined? the-scm-module 'variable)
439 (export variable))))))
440
73f38d5f
LC
441\f
442;;;
443;;; File systems.
444;;;
445
29fa45f4
LC
446(define (augment-mtab source target type options)
447 "Augment /etc/mtab with information about the given mount point."
448 (let ((port (open-file "/etc/mtab" "a")))
449 (format port "~a ~a ~a ~a 0 0~%"
450 source target type (or options "rw"))
451 (close-port port)))
452
453(define (read-mtab port)
454 "Read an mtab-formatted file from PORT, returning a list of tuples."
455 (let loop ((result '()))
456 (let ((line (read-line port)))
457 (if (eof-object? line)
458 (reverse result)
459 (loop (cons (string-tokenize line) result))))))
460
461(define (remove-from-mtab target)
462 "Remove mount point TARGET from /etc/mtab."
463 (define entries
464 (remove (match-lambda
465 ((device mount-point type options freq passno)
466 (string=? target mount-point))
467 (_ #f))
cd4a3cb3 468 (call-with-input-file "/etc/mtab" read-mtab)))
29fa45f4 469
cd4a3cb3 470 (call-with-output-file "/etc/mtab"
29fa45f4
LC
471 (lambda (port)
472 (for-each (match-lambda
473 ((device mount-point type options freq passno)
474 (format port "~a ~a ~a ~a ~a ~a~%"
475 device mount-point type options freq passno)))
476 entries))))
477
478;; Linux mount flags, from libc's <sys/mount.h>.
577c02eb
DT
479(define MS_RDONLY 1)
480(define MS_NOSUID 2)
481(define MS_NODEV 4)
482(define MS_NOEXEC 8)
483(define MS_REMOUNT 32)
484(define MS_BIND 4096)
485(define MS_MOVE 8192)
486(define MS_STRICTATIME 16777216)
29fa45f4 487
b16d138a
DT
488(define MNT_FORCE 1)
489(define MNT_DETACH 2)
490(define MNT_EXPIRE 4)
491(define UMOUNT_NOFOLLOW 8)
492
7df4d346
MO
493(define-as-needed (mount source target type
494 #:optional (flags 0) options
f91fcd9c 495 #:key (update-mtab? #f))
7df4d346 496 "Mount device SOURCE on TARGET as a file system TYPE.
f91fcd9c
MO
497Optionally, FLAGS may be a bitwise-or of the MS_* <sys/mount.h>
498constants, and OPTIONS may be a string. When FLAGS contains
499MS_REMOUNT, SOURCE and TYPE are ignored. When UPDATE-MTAB? is true,
500update /etc/mtab. Raise a 'system-error' exception on error."
7df4d346
MO
501 ;; XXX: '#:update-mtab?' is not implemented by core 'mount'.
502 (let ((proc (syscall->procedure int "mount" `(* * * ,unsigned-long *))))
503 (let-values (((ret err)
504 (proc (if source
505 (string->pointer source)
506 %null-pointer)
507 (string->pointer target)
508 (if type
509 (string->pointer type)
510 %null-pointer)
511 flags
512 (if options
513 (string->pointer options)
514 %null-pointer))))
515 (unless (zero? ret)
516 (throw 'system-error "mount" "mount ~S on ~S: ~A"
517 (list source target (strerror err))
518 (list err)))
519 (when update-mtab?
520 (augment-mtab source target type options)))))
521
522(define-as-needed (umount target
523 #:optional (flags 0)
524 #:key (update-mtab? #f))
525 "Unmount TARGET. Optionally FLAGS may be one of the MNT_* or UMOUNT_*
29fa45f4 526constants from <sys/mount.h>."
7df4d346
MO
527 ;; XXX: '#:update-mtab?' is not implemented by core 'umount'.
528 (let ((proc (syscall->procedure int "umount2" `(* ,int))))
529 (let-values (((ret err)
530 (proc (string->pointer target) flags)))
531 (unless (zero? ret)
532 (throw 'system-error "umount" "~S: ~A"
533 (list target (strerror err))
534 (list err)))
535 (when update-mtab?
536 (remove-from-mtab target)))))
29fa45f4 537
ccea821b
LC
538(define (mount-points)
539 "Return the mounts points for currently mounted file systems."
540 (call-with-input-file "/proc/mounts"
541 (lambda (port)
542 (let loop ((result '()))
543 (let ((line (read-line port)))
544 (if (eof-object? line)
545 (reverse result)
546 (match (string-tokenize line)
547 ((source mount-point _ ...)
548 (loop (cons mount-point result))))))))))
549
715fc9d4 550(define swapon
4f8cede0 551 (let ((proc (syscall->procedure int "swapon" (list '* int))))
715fc9d4
LC
552 (lambda* (device #:optional (flags 0))
553 "Use the block special device at DEVICE for swapping."
26ffb693
LC
554 (let-values (((ret err)
555 (proc (string->pointer device) flags)))
715fc9d4
LC
556 (unless (zero? ret)
557 (throw 'system-error "swapon" "~S: ~A"
558 (list device (strerror err))
559 (list err)))))))
560
561(define swapoff
4f8cede0 562 (let ((proc (syscall->procedure int "swapoff" '(*))))
715fc9d4
LC
563 (lambda (device)
564 "Stop using block special device DEVICE for swapping."
26ffb693 565 (let-values (((ret err) (proc (string->pointer device))))
715fc9d4 566 (unless (zero? ret)
60a56db0 567 (throw 'system-error "swapoff" "~S: ~A"
715fc9d4
LC
568 (list device (strerror err))
569 (list err)))))))
570
8336df06
MO
571(define-as-needed RB_AUTOBOOT #x01234567)
572(define-as-needed RB_HALT_SYSTEM #xcdef0123)
573(define-as-needed RB_ENABLED_CAD #x89abcdef)
574(define-as-needed RB_DISABLE_CAD 0)
575(define-as-needed RB_POWER_OFF #x4321fedc)
576(define-as-needed RB_SW_SUSPEND #xd000fce2)
577(define-as-needed RB_KEXEC #x45584543)
578
579(define-as-needed (reboot #:optional (cmd RB_AUTOBOOT))
580 (let ((proc (syscall->procedure int "reboot" (list int))))
581 (let-values (((ret err) (proc cmd)))
582 (unless (zero? ret)
583 (throw 'system-error "reboot" "~S: ~A"
584 (list cmd (strerror err))
585 (list err))))))
586
23e525e1
MO
587(define-as-needed (load-linux-module data #:optional (options ""))
588 (let ((proc (syscall->procedure int "init_module"
589 (list '* unsigned-long '*))))
590 (let-values (((ret err)
591 (proc (bytevector->pointer data)
592 (bytevector-length data)
593 (string->pointer options))))
594 (unless (zero? ret)
595 (throw 'system-error "load-linux-module" "~A"
596 (list (strerror err))
597 (list err))))))
598
17a4d344
LC
599(define (kernel? pid)
600 "Return #t if PID designates a \"kernel thread\" rather than a normal
601user-land process."
602 (let ((stat (call-with-input-file (format #f "/proc/~a/stat" pid)
603 (compose string-tokenize read-string))))
604 ;; See proc.txt in Linux's documentation for the list of fields.
605 (match stat
606 ((pid tcomm state ppid pgrp sid tty_nr tty_pgrp flags min_flt
607 cmin_flt maj_flt cmaj_flt utime stime cutime cstime
608 priority nice num_thread it_real_value start_time
609 vsize rss rsslim
610 (= string->number start_code) (= string->number end_code) _ ...)
611 ;; Got this obscure trick from sysvinit's 'killall5' program.
612 (and (zero? start_code) (zero? end_code))))))
613
614(define (processes)
615 "Return the list of live processes."
616 (sort (filter-map (lambda (file)
617 (let ((pid (string->number file)))
618 (and pid
619 (not (kernel? pid))
620 pid)))
621 (scandir "/proc"))
622 <))
623
b4abdeb6 624(define mkdtemp!
d1f33ba4 625 (let ((proc (syscall->procedure '* "mkdtemp" '(*))))
b4abdeb6
DT
626 (lambda (tmpl)
627 "Create a new unique directory in the file system using the template
628string TMPL and return its file name. TMPL must end with 'XXXXXX'."
26ffb693 629 (let-values (((result err) (proc (string->pointer tmpl))))
b4abdeb6
DT
630 (when (null-pointer? result)
631 (throw 'system-error "mkdtemp!" "~S: ~A"
632 (list tmpl (strerror err))
633 (list err)))
634 (pointer->string result)))))
635
1752a17a
LC
636(define fdatasync
637 (let ((proc (syscall->procedure int "fdatasync" (list int))))
638 (lambda (port)
639 "Flush buffered output of PORT, an output file port, and then call
640fdatasync(2) on the underlying file descriptor."
641 (force-output port)
26ffb693
LC
642 (let*-values (((fd) (fileno port))
643 ((ret err) (proc fd)))
1752a17a
LC
644 (unless (zero? ret)
645 (throw 'system-error "fdatasync" "~S: ~A"
646 (list fd (strerror err))
647 (list err)))))))
648
a1f70878
LC
649
650(define-record-type <file-system>
651 (file-system type block-size blocks blocks-free
652 blocks-available files free-files identifier
00cd4197 653 name-length fragment-size mount-flags spare)
a1f70878
LC
654 file-system?
655 (type file-system-type)
656 (block-size file-system-block-size)
657 (blocks file-system-block-count)
658 (blocks-free file-system-blocks-free)
659 (blocks-available file-system-blocks-available)
660 (files file-system-file-count)
661 (free-files file-system-free-file-nodes)
662 (identifier file-system-identifier)
663 (name-length file-system-maximum-name-length)
664 (fragment-size file-system-fragment-size)
200dac06 665 (mount-flags file-system-mount-flags)
00cd4197 666 (spare file-system--spare))
a1f70878
LC
667
668(define-syntax fsword ;fsword_t
669 (identifier-syntax long))
670
200dac06 671(define-c-struct %statfs ;<bits/statfs.h>
a1f70878
LC
672 sizeof-statfs ;slightly overestimated
673 file-system
674 read-statfs
675 write-statfs!
676 (type fsword)
677 (block-size fsword)
678 (blocks uint64)
679 (blocks-free uint64)
680 (blocks-available uint64)
681 (files uint64)
682 (free-files uint64)
00cd4197 683 (identifier (array int 2))
a1f70878
LC
684 (name-length fsword)
685 (fragment-size fsword)
200dac06 686 (mount-flags fsword)
00cd4197 687 (spare (array fsword 4)))
a1f70878
LC
688
689(define statfs
96f2a432 690 (let ((proc (syscall->procedure int "statfs64" '(* *))))
a1f70878
LC
691 (lambda (file)
692 "Return a <file-system> data structure describing the file system
693mounted at FILE."
26ffb693
LC
694 (let*-values (((stat) (make-bytevector sizeof-statfs))
695 ((ret err) (proc (string->pointer file)
696 (bytevector->pointer stat))))
a1f70878 697 (if (zero? ret)
96f2a432 698 (read-statfs stat)
a1f70878
LC
699 (throw 'system-error "statfs" "~A: ~A"
700 (list file (strerror err))
701 (list err)))))))
702
65f224dc
LC
703(define (free-disk-space file)
704 "Return the free disk space, in bytes, on the file system that hosts FILE."
705 (let ((fs (statfs file)))
706 (* (file-system-block-size fs)
707 (file-system-blocks-available fs))))
708
73f38d5f
LC
709\f
710;;;
711;;; Containers.
712;;;
713
8950ed11 714;; Linux clone flags, from linux/sched.h
ee78d024
DT
715(define CLONE_CHILD_CLEARTID #x00200000)
716(define CLONE_CHILD_SETTID #x01000000)
717(define CLONE_NEWNS #x00020000)
718(define CLONE_NEWUTS #x04000000)
719(define CLONE_NEWIPC #x08000000)
720(define CLONE_NEWUSER #x10000000)
721(define CLONE_NEWPID #x20000000)
722(define CLONE_NEWNET #x40000000)
8950ed11 723
70dfdd50
LC
724(cond-expand
725 (guile-2.2
726 (define %set-automatic-finalization-enabled?!
727 (let ((proc (pointer->procedure int
728 (dynamic-func
729 "scm_set_automatic_finalization_enabled"
730 (dynamic-link))
731 (list int))))
732 (lambda (enabled?)
733 "Switch on or off automatic finalization in a separate thread.
734Turning finalization off shuts down the finalization thread as a side effect."
735 (->bool (proc (if enabled? 1 0))))))
736
737 (define-syntax-rule (without-automatic-finalization exp)
738 "Turn off automatic finalization within the dynamic extent of EXP."
739 (let ((enabled? #t))
740 (dynamic-wind
741 (lambda ()
742 (set! enabled? (%set-automatic-finalization-enabled?! #f)))
743 (lambda ()
744 exp)
745 (lambda ()
746 (%set-automatic-finalization-enabled?! enabled?))))))
747
748 (else
749 (define-syntax-rule (without-automatic-finalization exp)
750 ;; Nothing to do here: Guile 2.0 does not have a separate finalization
751 ;; thread.
752 exp)))
753
8950ed11 754;; The libc interface to sys_clone is not useful for Scheme programs, so the
0e3cc311
LC
755;; low-level system call is wrapped instead. The 'syscall' function is
756;; declared in <unistd.h> as a variadic function; in practice, it expects 6
757;; pointer-sized arguments, as shown in, e.g., x86_64/syscall.S.
8950ed11 758(define clone
4f8cede0
MW
759 (let* ((proc (syscall->procedure int "syscall"
760 (list long ;sysno
761 unsigned-long ;flags
762 '* '* '*
763 '*)))
8950ed11
DT
764 ;; TODO: Don't do this.
765 (syscall-id (match (utsname:machine (uname))
766 ("i686" 120)
767 ("x86_64" 56)
768 ("mips64" 5055)
4f8cede0
MW
769 ("armv7l" 120)
770 (_ #f))))
8950ed11
DT
771 (lambda (flags)
772 "Create a new child process by duplicating the current parent process.
773Unlike the fork system call, clone accepts FLAGS that specify which resources
774are shared between the parent and child processes."
26ffb693 775 (let-values (((ret err)
70dfdd50
LC
776 ;; Guile 2.2 runs a finalization thread. 'primitive-fork'
777 ;; takes care of shutting it down before forking, and we
778 ;; must do the same here. Failing to do that, if the
779 ;; child process calls 'primitive-fork', it will hang
780 ;; while trying to pthread_join the finalization thread
781 ;; since that thread does not exist.
782 (without-automatic-finalization
783 (proc syscall-id flags
784 %null-pointer ;child stack
785 %null-pointer %null-pointer ;ptid & ctid
786 %null-pointer)))) ;unused
cf897cba
DT
787 (if (= ret -1)
788 (throw 'system-error "clone" "~d: ~A"
789 (list flags (strerror err))
790 (list err))
791 ret)))))
8950ed11 792
43ace6ea 793(define setns
39e336b5
DT
794 ;; Some systems may be using an old (pre-2.14) version of glibc where there
795 ;; is no 'setns' function available.
796 (false-if-exception
d1f33ba4 797 (let ((proc (syscall->procedure int "setns" (list int int))))
39e336b5
DT
798 (lambda (fdes nstype)
799 "Reassociate the current process with the namespace specified by FDES, a
43ace6ea
DT
800file descriptor obtained by opening a /proc/PID/ns/* file. NSTYPE specifies
801which type of namespace the current process may be reassociated with, or 0 if
802there is no such limitation."
26ffb693 803 (let-values (((ret err) (proc fdes nstype)))
39e336b5
DT
804 (unless (zero? ret)
805 (throw 'system-error "setns" "~d ~d: ~A"
806 (list fdes nstype (strerror err))
807 (list err))))))))
43ace6ea 808
df3ce5c1 809(define pivot-root
4f8cede0 810 (let ((proc (syscall->procedure int "pivot_root" (list '* '*))))
df3ce5c1
DT
811 (lambda (new-root put-old)
812 "Change the root file system to NEW-ROOT and move the current root file
813system to PUT-OLD."
26ffb693
LC
814 (let-values (((ret err)
815 (proc (string->pointer new-root)
816 (string->pointer put-old))))
df3ce5c1
DT
817 (unless (zero? ret)
818 (throw 'system-error "pivot_root" "~S ~S: ~A"
819 (list new-root put-old (strerror err))
820 (list err)))))))
821
7585016f 822\f
fa73c193
LC
823;;;
824;;; Opendir & co.
825;;;
826
827(define-c-struct %struct-dirent-header
828 sizeof-dirent-header
829 (lambda (inode offset length type name)
830 ;; Convert TYPE to symbols like 'stat:type' does.
831 (let ((type (cond ((= type DT_REG) 'regular)
832 ((= type DT_LNK) 'symlink)
833 ((= type DT_DIR) 'directory)
834 ((= type DT_FIFO) 'fifo)
835 ((= type DT_CHR) 'char-special)
836 ((= type DT_BLK) 'block-special)
837 ((= type DT_SOCK) 'socket)
838 (else 'unknown))))
839 `((type . ,type)
840 (inode . ,inode))))
841 read-dirent-header
842 write-dirent-header!
843 (inode int64)
844 (offset int64)
845 (length unsigned-short)
846 (type uint8)
847 (name uint8)) ;first byte of 'd_name'
848
849;; Constants for the 'type' field, from <dirent.h>.
850(define DT_UNKNOWN 0)
851(define DT_FIFO 1)
852(define DT_CHR 2)
853(define DT_DIR 4)
854(define DT_BLK 6)
855(define DT_REG 8)
856(define DT_LNK 10)
857(define DT_SOCK 12)
858(define DT_WHT 14)
859
860(define string->pointer/utf-8
861 (cut string->pointer <> "UTF-8"))
862
863(define pointer->string/utf-8
864 (cut pointer->string <> <> "UTF-8"))
865
866(define opendir*
867 (let ((proc (syscall->procedure '* "opendir" '(*))))
868 (lambda* (name #:optional (string->pointer string->pointer/utf-8))
869 (let-values (((ptr err)
870 (proc (string->pointer name))))
871 (if (null-pointer? ptr)
872 (throw 'system-error "opendir*"
3bacc655 873 "~A: ~A" (list name (strerror err))
fa73c193
LC
874 (list err))
875 ptr)))))
876
877(define closedir*
878 (let ((proc (syscall->procedure int "closedir" '(*))))
879 (lambda (directory)
880 (let-values (((ret err)
881 (proc directory)))
882 (unless (zero? ret)
883 (throw 'system-error "closedir"
884 "closedir: ~A" (list (strerror err))
885 (list err)))))))
886
887(define readdir*
888 (let ((proc (syscall->procedure '* "readdir64" '(*))))
889 (lambda* (directory #:optional (pointer->string pointer->string/utf-8))
890 (let ((ptr (proc directory)))
891 (and (not (null-pointer? ptr))
892 (cons (pointer->string
893 (make-pointer (+ (pointer-address ptr)
894 (c-struct-field-offset
895 %struct-dirent-header name)))
896 -1)
897 (read-dirent-header
898 (pointer->bytevector ptr sizeof-dirent-header))))))))
899
900(define* (scandir* name #:optional
901 (select? (const #t))
902 (entry<? (lambda (entry1 entry2)
903 (match entry1
904 ((name1 . _)
905 (match entry2
906 ((name2 . _)
907 (string<? name1 name2)))))))
908 #:key
909 (string->pointer string->pointer/utf-8)
910 (pointer->string pointer->string/utf-8))
911 "This procedure improves on Guile's 'scandir' procedure in several ways:
912
913 1. Systematically encode decode file names using STRING->POINTER and
914 POINTER->STRING (UTF-8 by default; this works around a defect in Guile 2.0/2.2
915 where 'scandir' decodes file names according to the current locale, which is
916 not always desirable.
917
918 2. Each entry that is returned has the form (NAME . PROPERTIES).
919 PROPERTIES is an alist showing additional properties about the entry, as
920 found in 'struct dirent'. An entry may look like this:
921
922 (\"foo.scm\" (type . regular) (inode . 123456))
923
924 Callers must be prepared to deal with the case where 'type' is 'unknown'
925 since some file systems do not provide that information.
926
927 3. Raise to 'system-error' when NAME cannot be opened."
928 (let ((directory (opendir* name string->pointer)))
929 (dynamic-wind
930 (const #t)
931 (lambda ()
932 (let loop ((result '()))
933 (match (readdir* directory pointer->string)
934 (#f
935 (sort result entry<?))
936 (entry
937 (loop (if (select? entry)
938 (cons entry result)
939 result))))))
940 (lambda ()
941 (closedir* directory)))))
942
943\f
4e0ea3eb
LC
944;;;
945;;; Advisory file locking.
946;;;
947
d33c8b46
LC
948(define-c-struct %struct-flock ;<fcntl.h>
949 sizeof-flock
950 list
951 read-flock
952 write-flock!
953 (type short)
954 (whence short)
955 (start size_t)
956 (length size_t)
957 (pid int))
4e0ea3eb
LC
958
959(define F_SETLKW
960 ;; On Linux-based systems, this is usually 7, but not always
961 ;; (exceptions include SPARC.) On GNU/Hurd, it's 9.
962 (cond ((string-contains %host-type "sparc") 9) ; sparc-*-linux-gnu
963 ((string-contains %host-type "linux") 7) ; *-linux-gnu
964 (else 9))) ; *-gnu*
965
966(define F_SETLK
967 ;; Likewise: GNU/Hurd and SPARC use 8, while the others typically use 6.
968 (cond ((string-contains %host-type "sparc") 8) ; sparc-*-linux-gnu
969 ((string-contains %host-type "linux") 6) ; *-linux-gnu
970 (else 8))) ; *-gnu*
971
972(define F_xxLCK
973 ;; The F_RDLCK, F_WRLCK, and F_UNLCK constants.
974 (cond ((string-contains %host-type "sparc") #(1 2 3)) ; sparc-*-linux-gnu
975 ((string-contains %host-type "hppa") #(1 2 3)) ; hppa-*-linux-gnu
976 ((string-contains %host-type "linux") #(0 1 2)) ; *-linux-gnu
977 (else #(1 2 3)))) ; *-gnu*
978
979(define fcntl-flock
980 (let ((proc (syscall->procedure int "fcntl" `(,int ,int *))))
981 (lambda* (fd-or-port operation #:key (wait? #t))
982 "Perform locking OPERATION on the file beneath FD-OR-PORT. OPERATION
983must be a symbol, one of 'read-lock, 'write-lock, or 'unlock. When WAIT? is
984true, block until the lock is acquired; otherwise, thrown an 'flock-error'
985exception if it's already taken."
986 (define (operation->int op)
987 (case op
988 ((read-lock) (vector-ref F_xxLCK 0))
989 ((write-lock) (vector-ref F_xxLCK 1))
990 ((unlock) (vector-ref F_xxLCK 2))
991 (else (error "invalid fcntl-flock operation" op))))
992
993 (define fd
994 (if (port? fd-or-port)
995 (fileno fd-or-port)
996 fd-or-port))
997
d33c8b46
LC
998 (define bv
999 (make-bytevector sizeof-flock))
1000
1001 (write-flock! bv 0
1002 (operation->int operation) SEEK_SET
1003 0 0 ;whole file
1004 0)
1005
4e0ea3eb
LC
1006 ;; XXX: 'fcntl' is a vararg function, but here we happily use the
1007 ;; standard ABI; crossing fingers.
26ffb693
LC
1008 (let-values (((ret err)
1009 (proc fd
1010 (if wait?
1011 F_SETLKW ;lock & wait
1012 F_SETLK) ;non-blocking attempt
1013 (bytevector->pointer bv))))
d33c8b46
LC
1014 (unless (zero? ret)
1015 ;; Presumably we got EAGAIN or so.
1016 (throw 'flock-error err))))))
4e0ea3eb
LC
1017
1018\f
aa401f9b
LC
1019;;;
1020;;; Miscellaneous, aka. 'prctl'.
1021;;;
1022
1023(define %prctl
1024 ;; Should it win the API contest against 'ioctl'? You tell us!
1025 (syscall->procedure int "prctl"
1026 (list int unsigned-long unsigned-long
1027 unsigned-long unsigned-long)))
1028
1029(define PR_SET_NAME 15) ;<linux/prctl.h>
1030(define PR_GET_NAME 16)
1031
1032(define %max-thread-name-length
1033 ;; Maximum length in bytes of the process name, including the terminating
1034 ;; zero.
1035 16)
1036
1037(define (set-thread-name name)
1038 "Set the name of the calling thread to NAME. NAME is truncated to 15
1039bytes."
1040 (let ((ptr (string->pointer name)))
1041 (let-values (((ret err)
1042 (%prctl PR_SET_NAME
1043 (pointer-address ptr) 0 0 0)))
1044 (unless (zero? ret)
1045 (throw 'set-process-name "set-process-name"
1046 "set-process-name: ~A"
1047 (list (strerror err))
1048 (list err))))))
1049
1050(define (thread-name)
1051 "Return the name of the calling thread as a string."
1052 (let ((buf (make-bytevector %max-thread-name-length)))
1053 (let-values (((ret err)
1054 (%prctl PR_GET_NAME
1055 (pointer-address (bytevector->pointer buf))
1056 0 0 0)))
1057 (if (zero? ret)
1058 (bytes->string (bytevector->u8-list buf))
1059 (throw 'process-name "process-name"
1060 "process-name: ~A"
1061 (list (strerror err))
1062 (list err))))))
1063
1064\f
7585016f
LC
1065;;;
1066;;; Network interfaces.
1067;;;
1068
1069(define SIOCGIFCONF ;from <bits/ioctls.h>
1070 (if (string-contains %host-type "linux")
1071 #x8912 ;GNU/Linux
1072 #xf00801a4)) ;GNU/Hurd
973eea34
LC
1073(define SIOCGIFFLAGS
1074 (if (string-contains %host-type "linux")
1075 #x8913 ;GNU/Linux
1076 #xc4804191)) ;GNU/Hurd
c9bf64d6
LC
1077(define SIOCSIFFLAGS
1078 (if (string-contains %host-type "linux")
1079 #x8914 ;GNU/Linux
1080 -1)) ;FIXME: GNU/Hurd?
1081(define SIOCGIFADDR
1082 (if (string-contains %host-type "linux")
1083 #x8915 ;GNU/Linux
1084 -1)) ;FIXME: GNU/Hurd?
1085(define SIOCSIFADDR
1086 (if (string-contains %host-type "linux")
1087 #x8916 ;GNU/Linux
1088 -1)) ;FIXME: GNU/Hurd?
67e5f3b7
LC
1089(define SIOCGIFNETMASK
1090 (if (string-contains %host-type "linux")
1091 #x891b ;GNU/Linux
1092 -1)) ;FIXME: GNU/Hurd?
1093(define SIOCSIFNETMASK
1094 (if (string-contains %host-type "linux")
1095 #x891c ;GNU/Linux
1096 -1)) ;FIXME: GNU/Hurd?
9e38e3cf
LC
1097(define SIOCADDRT
1098 (if (string-contains %host-type "linux")
1099 #x890B ;GNU/Linux
1100 -1)) ;FIXME: GNU/Hurd?
1101(define SIOCDELRT
1102 (if (string-contains %host-type "linux")
1103 #x890C ;GNU/Linux
1104 -1)) ;FIXME: GNU/Hurd?
973eea34
LC
1105
1106;; Flags and constants from <net/if.h>.
1107
c5cd4fad
MO
1108(define-as-needed IFF_UP #x1) ;Interface is up
1109(define-as-needed IFF_BROADCAST #x2) ;Broadcast address valid.
1110(define-as-needed IFF_LOOPBACK #x8) ;Is a loopback net.
973eea34
LC
1111
1112(define IF_NAMESIZE 16) ;maximum interface name size
7585016f 1113
9d9d0c9c
LC
1114(define-c-struct %ifconf-struct
1115 sizeof-ifconf
1116 list
1117 read-ifconf
1118 write-ifconf!
1119 (length int) ;int ifc_len
1120 (request '*)) ;struct ifreq *ifc_ifcu
7585016f
LC
1121
1122(define ifreq-struct-size
973eea34
LC
1123 ;; 'struct ifreq' begins with an array of IF_NAMESIZE bytes containing the
1124 ;; interface name (nul-terminated), followed by a bunch of stuff. This is
1125 ;; its size in bytes.
7585016f
LC
1126 (if (= 8 (sizeof '*))
1127 40
1128 32))
1129
c9bf64d6 1130(define-c-struct sockaddr-in ;<linux/in.h>
785cfa87 1131 sizeof-sockaddrin
13f0c6ed
LC
1132 (lambda (family port address)
1133 (make-socket-address family address port))
c9bf64d6
LC
1134 read-sockaddr-in
1135 write-sockaddr-in!
1136 (family unsigned-short)
1137 (port (int16 ~ big))
1138 (address (int32 ~ big)))
1139
1140(define-c-struct sockaddr-in6 ;<linux/in6.h>
785cfa87 1141 sizeof-sockaddr-in6
13f0c6ed
LC
1142 (lambda (family port flowinfo address scopeid)
1143 (make-socket-address family address port flowinfo scopeid))
c9bf64d6
LC
1144 read-sockaddr-in6
1145 write-sockaddr-in6!
1146 (family unsigned-short)
1147 (port (int16 ~ big))
1148 (flowinfo (int32 ~ big))
1149 (address (int128 ~ big))
1150 (scopeid int32))
1151
1152(define (write-socket-address! sockaddr bv index)
1153 "Write SOCKADDR, a socket address as returned by 'make-socket-address', to
1154bytevector BV at INDEX."
1155 (let ((family (sockaddr:fam sockaddr)))
1156 (cond ((= family AF_INET)
1157 (write-sockaddr-in! bv index
1158 family
1159 (sockaddr:port sockaddr)
1160 (sockaddr:addr sockaddr)))
1161 ((= family AF_INET6)
1162 (write-sockaddr-in6! bv index
1163 family
1164 (sockaddr:port sockaddr)
1165 (sockaddr:flowinfo sockaddr)
1166 (sockaddr:addr sockaddr)
1167 (sockaddr:scopeid sockaddr)))
1168 (else
1169 (error "unsupported socket address" sockaddr)))))
1170
3ca33769
LC
1171(define PF_PACKET 17) ;<bits/socket.h>
1172(define AF_PACKET PF_PACKET)
1173
1174(define* (read-socket-address bv #:optional (index 0))
c9bf64d6
LC
1175 "Read a socket address from bytevector BV at INDEX."
1176 (let ((family (bytevector-u16-native-ref bv index)))
1177 (cond ((= family AF_INET)
13f0c6ed 1178 (read-sockaddr-in bv index))
c9bf64d6 1179 ((= family AF_INET6)
13f0c6ed 1180 (read-sockaddr-in6 bv index))
c9bf64d6 1181 (else
3ca33769
LC
1182 ;; XXX: Unsupported address family, such as AF_PACKET. Return a
1183 ;; vector such that the vector can at least call 'sockaddr:fam'.
1184 (vector family)))))
c9bf64d6 1185
7585016f
LC
1186(define %ioctl
1187 ;; The most terrible interface, live from Scheme.
d1f33ba4 1188 (syscall->procedure int "ioctl" (list int unsigned-long '*)))
7585016f 1189
57f068be
LC
1190(define (bytes->string bytes)
1191 "Read BYTES, a list of bytes, and return the null-terminated string decoded
1192from there, or #f if that would be an empty string."
1193 (match (take-while (negate zero?) bytes)
1194 (()
1195 #f)
1196 (non-zero
1197 (list->string (map integer->char non-zero)))))
1198
7585016f
LC
1199(define (bytevector->string-list bv stride len)
1200 "Return the null-terminated strings found in BV every STRIDE bytes. Read at
1201most LEN bytes from BV."
1202 (let loop ((bytes (take (bytevector->u8-list bv)
1203 (min len (bytevector-length bv))))
1204 (result '()))
1205 (match bytes
1206 (()
1207 (reverse result))
1208 (_
1209 (loop (drop bytes stride)
57f068be 1210 (cons (bytes->string bytes) result))))))
7585016f 1211
b89e7405
LC
1212(define* (network-interface-names #:optional sock)
1213 "Return the names of existing network interfaces. This is typically limited
4d54785c 1214to interfaces that are currently up."
7585016f
LC
1215 (let* ((close? (not sock))
1216 (sock (or sock (socket SOCK_STREAM AF_INET 0)))
1217 (len (* ifreq-struct-size 10))
1218 (reqs (make-bytevector len))
9d9d0c9c
LC
1219 (conf (make-bytevector sizeof-ifconf)))
1220 (write-ifconf! conf 0
1221 len (bytevector->pointer reqs))
1222
26ffb693 1223 (let-values (((ret err)
9d9d0c9c
LC
1224 (%ioctl (fileno sock) SIOCGIFCONF
1225 (bytevector->pointer conf))))
26ffb693
LC
1226 (when close?
1227 (close-port sock))
1228 (if (zero? ret)
1229 (bytevector->string-list reqs ifreq-struct-size
9d9d0c9c 1230 (match (read-ifconf conf)
26ffb693
LC
1231 ((len . _) len)))
1232 (throw 'system-error "network-interface-list"
1233 "network-interface-list: ~A"
1234 (list (strerror err))
1235 (list err))))))
7585016f 1236
4d54785c
LC
1237(define %interface-line
1238 ;; Regexp matching an interface line in Linux's /proc/net/dev.
a220b262 1239 (make-regexp "^[[:blank:]]*([[:graph:]]+):.*$"))
4d54785c 1240
b89e7405
LC
1241(define (all-network-interface-names)
1242 "Return all the names of the registered network interfaces, including those
1243that are not up."
4d54785c
LC
1244 (call-with-input-file "/proc/net/dev" ;XXX: Linux-specific
1245 (lambda (port)
1246 (let loop ((interfaces '()))
1247 (let ((line (read-line port)))
1248 (cond ((eof-object? line)
1249 (reverse interfaces))
1250 ((regexp-exec %interface-line line)
1251 =>
1252 (lambda (match)
1253 (loop (cons (match:substring match 1) interfaces))))
1254 (else
1255 (loop interfaces))))))))
1256
c5cd4fad 1257(define-as-needed (network-interface-flags socket name)
973eea34
LC
1258 "Return a number that is the bit-wise or of 'IFF*' flags for network
1259interface NAME."
1260 (let ((req (make-bytevector ifreq-struct-size)))
1261 (bytevector-copy! (string->utf8 name) 0 req 0
1262 (min (string-length name) (- IF_NAMESIZE 1)))
26ffb693
LC
1263 (let-values (((ret err)
1264 (%ioctl (fileno socket) SIOCGIFFLAGS
1265 (bytevector->pointer req))))
973eea34
LC
1266 (if (zero? ret)
1267
c5cd4fad
MO
1268 ;; The 'ifr_flags' field is IF_NAMESIZE bytes after the
1269 ;; beginning of 'struct ifreq', and it's a short int.
973eea34
LC
1270 (bytevector-sint-ref req IF_NAMESIZE (native-endianness)
1271 (sizeof short))
1272
1273 (throw 'system-error "network-interface-flags"
1274 "network-interface-flags on ~A: ~A"
1275 (list name (strerror err))
1276 (list err))))))
1277
1278(define (loopback-network-interface? name)
1279 "Return true if NAME designates a loopback network interface."
1280 (let* ((sock (socket SOCK_STREAM AF_INET 0))
1281 (flags (network-interface-flags sock name)))
1282 (close-port sock)
1283 (not (zero? (logand flags IFF_LOOPBACK)))))
1284
c5cd4fad 1285(define-as-needed (set-network-interface-flags socket name flags)
c9bf64d6
LC
1286 "Set the flag of network interface NAME to FLAGS."
1287 (let ((req (make-bytevector ifreq-struct-size)))
1288 (bytevector-copy! (string->utf8 name) 0 req 0
1289 (min (string-length name) (- IF_NAMESIZE 1)))
1290 ;; Set the 'ifr_flags' field.
1291 (bytevector-uint-set! req IF_NAMESIZE flags (native-endianness)
1292 (sizeof short))
26ffb693
LC
1293 (let-values (((ret err)
1294 (%ioctl (fileno socket) SIOCSIFFLAGS
1295 (bytevector->pointer req))))
c9bf64d6
LC
1296 (unless (zero? ret)
1297 (throw 'system-error "set-network-interface-flags"
1298 "set-network-interface-flags on ~A: ~A"
1299 (list name (strerror err))
1300 (list err))))))
1301
c5cd4fad 1302(define-as-needed (set-network-interface-address socket name sockaddr)
c9bf64d6
LC
1303 "Set the address of network interface NAME to SOCKADDR."
1304 (let ((req (make-bytevector ifreq-struct-size)))
1305 (bytevector-copy! (string->utf8 name) 0 req 0
1306 (min (string-length name) (- IF_NAMESIZE 1)))
1307 ;; Set the 'ifr_addr' field.
1308 (write-socket-address! sockaddr req IF_NAMESIZE)
26ffb693
LC
1309 (let-values (((ret err)
1310 (%ioctl (fileno socket) SIOCSIFADDR
1311 (bytevector->pointer req))))
c9bf64d6
LC
1312 (unless (zero? ret)
1313 (throw 'system-error "set-network-interface-address"
1314 "set-network-interface-address on ~A: ~A"
1315 (list name (strerror err))
1316 (list err))))))
1317
67e5f3b7
LC
1318(define (set-network-interface-netmask socket name sockaddr)
1319 "Set the network mask of interface NAME to SOCKADDR."
1320 (let ((req (make-bytevector ifreq-struct-size)))
1321 (bytevector-copy! (string->utf8 name) 0 req 0
1322 (min (string-length name) (- IF_NAMESIZE 1)))
1323 ;; Set the 'ifr_addr' field.
1324 (write-socket-address! sockaddr req IF_NAMESIZE)
1325 (let-values (((ret err)
1326 (%ioctl (fileno socket) SIOCSIFNETMASK
1327 (bytevector->pointer req))))
1328 (unless (zero? ret)
1329 (throw 'system-error "set-network-interface-netmask"
1330 "set-network-interface-netmask on ~A: ~A"
1331 (list name (strerror err))
1332 (list err))))))
1333
c9bf64d6
LC
1334(define (network-interface-address socket name)
1335 "Return the address of network interface NAME. The result is an object of
1336the same type as that returned by 'make-socket-address'."
1337 (let ((req (make-bytevector ifreq-struct-size)))
1338 (bytevector-copy! (string->utf8 name) 0 req 0
1339 (min (string-length name) (- IF_NAMESIZE 1)))
26ffb693
LC
1340 (let-values (((ret err)
1341 (%ioctl (fileno socket) SIOCGIFADDR
1342 (bytevector->pointer req))))
c9bf64d6
LC
1343 (if (zero? ret)
1344 (read-socket-address req IF_NAMESIZE)
1345 (throw 'system-error "network-interface-address"
1346 "network-interface-address on ~A: ~A"
1347 (list name (strerror err))
1348 (list err))))))
1349
67e5f3b7
LC
1350(define (network-interface-netmask socket name)
1351 "Return the netmask of network interface NAME. The result is an object of
1352the same type as that returned by 'make-socket-address'."
1353 (let ((req (make-bytevector ifreq-struct-size)))
1354 (bytevector-copy! (string->utf8 name) 0 req 0
1355 (min (string-length name) (- IF_NAMESIZE 1)))
1356 (let-values (((ret err)
1357 (%ioctl (fileno socket) SIOCGIFNETMASK
1358 (bytevector->pointer req))))
1359 (if (zero? ret)
1360 (read-socket-address req IF_NAMESIZE)
1361 (throw 'system-error "network-interface-netmask"
1362 "network-interface-netmask on ~A: ~A"
1363 (list name (strerror err))
1364 (list err))))))
1365
e9ff8d9f
LC
1366(define* (configure-network-interface name sockaddr flags
1367 #:key netmask)
c9bf64d6 1368 "Configure network interface NAME to use SOCKADDR, an address as returned by
e9ff8d9f
LC
1369'make-socket-address', and FLAGS, a bitwise-or of IFF_* constants. If NETMASK
1370is true, it must be a socket address to use as the network mask."
c9bf64d6
LC
1371 (let ((sock (socket (sockaddr:fam sockaddr) SOCK_STREAM 0)))
1372 (dynamic-wind
1373 (const #t)
1374 (lambda ()
1375 (set-network-interface-address sock name sockaddr)
e9ff8d9f
LC
1376 (set-network-interface-flags sock name flags)
1377 (when netmask
1378 (set-network-interface-netmask sock name netmask)))
c9bf64d6
LC
1379 (lambda ()
1380 (close-port sock)))))
1381
6944fdbd
LC
1382(define* (set-network-interface-up name
1383 #:key (family AF_INET))
1384 "Turn up the interface NAME."
1385 (let ((sock (socket family SOCK_STREAM 0)))
1386 (dynamic-wind
1387 (const #t)
1388 (lambda ()
1389 (let ((flags (network-interface-flags sock name)))
1390 (set-network-interface-flags sock name
1391 (logior flags IFF_UP))))
1392 (lambda ()
1393 (close-port sock)))))
1394
e7f5691d 1395\f
9e38e3cf
LC
1396;;;
1397;;; Network routes.
1398;;;
1399
1400(define-c-struct %rtentry ;'struct rtentry' from <net/route.h>
1401 sizeof-rtentry
1402 list
1403 read-rtentry
1404 write-rtentry!
1405 (pad1 unsigned-long)
1406 (destination (array uint8 16)) ;struct sockaddr
1407 (gateway (array uint8 16)) ;struct sockaddr
1408 (genmask (array uint8 16)) ;struct sockaddr
1409 (flags unsigned-short)
1410 (pad2 short)
1411 (pad3 long)
1412 (tos uint8)
1413 (class uint8)
1414 (pad4 (array uint8 (if (= 8 (sizeof* '*)) 3 1)))
1415 (metric short)
1416 (device '*)
1417 (mtu unsigned-long)
1418 (window unsigned-long)
1419 (initial-rtt unsigned-short))
1420
1421(define RTF_UP #x0001) ;'rtentry' flags from <net/route.h>
1422(define RTF_GATEWAY #x0002)
1423
1424(define %sockaddr-any
1425 (make-socket-address AF_INET INADDR_ANY 0))
1426
1427(define add-network-route/gateway
1428 ;; To allow field names to be matched as literals, we need to move them out
1429 ;; of the lambda's body since the parameters have the same name. A lot of
1430 ;; fuss for very little.
1431 (let-syntax ((gateway-offset (identifier-syntax
1432 (c-struct-field-offset %rtentry gateway)))
1433 (destination-offset (identifier-syntax
1434 (c-struct-field-offset %rtentry destination)))
1435 (genmask-offset (identifier-syntax
1436 (c-struct-field-offset %rtentry genmask))))
1437 (lambda* (socket gateway
1438 #:key (destination %sockaddr-any) (genmask %sockaddr-any))
1439 "Add a network route for DESTINATION (a socket address as returned by
1440'make-socket-address') that goes through GATEWAY (a socket address). For
1441instance, the call:
1442
1443 (add-network-route/gateway sock
1444 (make-socket-address
1445 AF_INET
1446 (inet-pton AF_INET \"192.168.0.1\")
1447 0))
1448
1449is equivalent to this 'net-tools' command:
1450
1451 route add -net default gw 192.168.0.1
1452
1453because the default value of DESTINATION is \"0.0.0.0\"."
1454 (let ((route (make-bytevector sizeof-rtentry 0)))
1455 (write-socket-address! gateway route gateway-offset)
1456 (write-socket-address! destination route destination-offset)
1457 (write-socket-address! genmask route genmask-offset)
1458 (bytevector-u16-native-set! route
1459 (c-struct-field-offset %rtentry flags)
1460 (logior RTF_UP RTF_GATEWAY))
1461 (let-values (((ret err)
1462 (%ioctl (fileno socket) SIOCADDRT
1463 (bytevector->pointer route))))
1464 (unless (zero? ret)
1465 (throw 'system-error "add-network-route/gateway"
1466 "add-network-route/gateway: ~A"
1467 (list (strerror err))
1468 (list err))))))))
1469
1470(define delete-network-route
1471 (let-syntax ((destination-offset (identifier-syntax
1472 (c-struct-field-offset %rtentry destination))))
1473 (lambda* (socket destination)
1474 "Delete the network route for DESTINATION. For instance, the call:
1475
1476 (delete-network-route sock
1477 (make-socket-address AF_INET INADDR_ANY 0))
1478
1479is equivalent to the 'net-tools' command:
1480
1481 route del -net default
1482"
1483
1484 (let ((route (make-bytevector sizeof-rtentry 0)))
1485 (write-socket-address! destination route destination-offset)
1486 (let-values (((ret err)
1487 (%ioctl (fileno socket) SIOCDELRT
1488 (bytevector->pointer route))))
1489 (unless (zero? ret)
1490 (throw 'system-error "delete-network-route"
1491 "delete-network-route: ~A"
1492 (list (strerror err))
1493 (list err))))))))
1494
1495\f
e7f5691d
LC
1496;;;
1497;;; Details about network interfaces---aka. 'getifaddrs'.
1498;;;
1499
1500;; Network interfaces. XXX: We would call it <network-interface> but that
1501;; would collide with the ioctl wrappers above.
1502(define-record-type <interface>
1503 (make-interface name flags address netmask broadcast-address)
1504 interface?
1505 (name interface-name) ;string
1506 (flags interface-flags) ;or'd IFF_* values
1507 (address interface-address) ;sockaddr | #f
1508 (netmask interface-netmask) ;sockaddr | #f
1509 (broadcast-address interface-broadcast-address)) ;sockaddr | #f
1510
1511(define (write-interface interface port)
1512 (match interface
1513 (($ <interface> name flags address)
1514 (format port "#<interface ~s " name)
1515 (unless (zero? (logand IFF_UP flags))
1516 (display "up " port))
001dae0d
LC
1517
1518 ;; Check whether ADDRESS really is a sockaddr.
1519 (when address
1520 (if (member (sockaddr:fam address) (list AF_INET AF_INET6))
1521 (format port "~a " (inet-ntop (sockaddr:fam address)
1522 (sockaddr:addr address)))
1523 (format port "family:~a " (sockaddr:fam address))))
1524
e7f5691d
LC
1525 (format port "~a>" (number->string (object-address interface) 16)))))
1526
1527(set-record-type-printer! <interface> write-interface)
1528
1529(define (values->interface next name flags address netmask
1530 broadcast-address data)
1531 "Given the raw field values passed as arguments, return a pair whose car is
1532an <interface> object, and whose cdr is the pointer NEXT."
1533 (define (maybe-socket-address pointer)
1534 (if (null-pointer? pointer)
1535 #f
1536 (read-socket-address (pointer->bytevector pointer 50)))) ;XXX: size
1537
1538 (cons (make-interface (if (null-pointer? name)
1539 #f
1540 (pointer->string name))
1541 flags
1542 (maybe-socket-address address)
1543 (maybe-socket-address netmask)
1544 (maybe-socket-address broadcast-address)
1545 ;; Ignore DATA.
1546 )
1547 next))
1548
1549(define-c-struct ifaddrs ;<ifaddrs.h>
785cfa87 1550 %sizeof-ifaddrs
e7f5691d
LC
1551 values->interface
1552 read-ifaddrs
1553 write-ifaddrs!
1554 (next '*)
1555 (name '*)
1556 (flags unsigned-int)
1557 (addr '*)
1558 (netmask '*)
1559 (broadcastaddr '*)
1560 (data '*))
1561
e7f5691d
LC
1562(define (unfold-interface-list ptr)
1563 "Call 'read-ifaddrs' on PTR and all its 'next' fields, recursively, and
1564return the list of resulting <interface> objects."
1565 (let loop ((ptr ptr)
1566 (result '()))
1567 (if (null-pointer? ptr)
1568 (reverse result)
f77863a0 1569 (match (read-ifaddrs (pointer->bytevector ptr %sizeof-ifaddrs))
e7f5691d
LC
1570 ((ifaddr . ptr)
1571 (loop ptr (cons ifaddr result)))))))
1572
1573(define network-interfaces
d1f33ba4 1574 (let ((proc (syscall->procedure int "getifaddrs" (list '*))))
e7f5691d
LC
1575 (lambda ()
1576 "Return a list of <interface> objects, each denoting a configured
1577network interface. This is implemented using the 'getifaddrs' libc function."
26ffb693
LC
1578 (let*-values (((ptr)
1579 (bytevector->pointer (make-bytevector (sizeof* '*))))
1580 ((ret err)
1581 (proc ptr)))
e7f5691d
LC
1582 (if (zero? ret)
1583 (let* ((ptr (dereference-pointer ptr))
1584 (result (unfold-interface-list ptr)))
1585 (free-ifaddrs ptr)
1586 result)
1587 (throw 'system-error "network-interfaces" "~A"
1588 (list (strerror err))
1589 (list err)))))))
1590
1591(define free-ifaddrs
d1f33ba4 1592 (syscall->procedure void "freeifaddrs" '(*)))
e7f5691d 1593
29ff6d9f
LC
1594\f
1595;;;
1596;;; Terminals.
1597;;;
1598
ae4ff9f3
LC
1599(define-syntax bits->symbols-body
1600 (syntax-rules ()
1601 ((_ bits () ())
1602 '())
1603 ((_ bits (name names ...) (value values ...))
1604 (let ((result (bits->symbols-body bits (names ...) (values ...))))
1605 (if (zero? (logand bits value))
1606 result
1607 (cons 'name result))))))
1608
1609(define-syntax define-bits
1610 (syntax-rules (define)
1611 "Define the given numerical constants under CONSTRUCTOR, such that
1612 (CONSTRUCTOR NAME) returns VALUE. Define BITS->SYMBOLS as a procedure that,
1613given an integer, returns the list of names of the constants that are or'd."
1614 ((_ constructor bits->symbols (define names values) ...)
1615 (begin
1616 (define-syntax constructor
1617 (syntax-rules (names ...)
334c9530 1618 ((_) 0)
ae4ff9f3 1619 ((_ names) values) ...
334c9530
LC
1620 ((_ first rest (... ...))
1621 (logior (constructor first) rest (... ...)))))
ae4ff9f3 1622 (define (bits->symbols bits)
334c9530 1623 (bits->symbols-body bits (names ...) (values ...)))))))
ae4ff9f3
LC
1624
1625;; 'local-flags' bits from <bits/termios.h>
1626(define-bits local-flags
1627 local-flags->symbols
1628 (define ISIG #o0000001)
1629 (define ICANON #o0000002)
1630 (define XCASE #o0000004)
1631 (define ECHO #o0000010)
1632 (define ECHOE #o0000020)
1633 (define ECHOK #o0000040)
1634 (define ECHONL #o0000100)
1635 (define NOFLSH #o0000200)
1636 (define TOSTOP #o0000400)
1637 (define ECHOCTL #o0001000)
1638 (define ECHOPRT #o0002000)
1639 (define ECHOKE #o0004000)
1640 (define FLUSHO #o0010000)
1641 (define PENDIN #o0040000)
1642 (define IEXTEN #o0100000)
1643 (define EXTPROC #o0200000))
1644
1645;; "Actions" values for 'tcsetattr'.
a8f3424b
LC
1646(define-bits tcsetattr-action
1647 %unused-tcsetattr-action->symbols
1648 (define TCSANOW 0)
1649 (define TCSADRAIN 1)
1650 (define TCSAFLUSH 2))
ae4ff9f3
LC
1651
1652(define-record-type <termios>
1653 (termios input-flags output-flags control-flags local-flags
1654 line-discipline control-chars
1655 input-speed output-speed)
1656 termios?
1657 (input-flags termios-input-flags)
1658 (output-flags termios-output-flags)
1659 (control-flags termios-control-flags)
1660 (local-flags termios-local-flags)
1661 (line-discipline termios-line-discipline)
1662 (control-chars termios-control-chars)
1663 (input-speed termios-input-speed)
1664 (output-speed termios-output-speed))
1665
1666(define-c-struct %termios ;<bits/termios.h>
1667 sizeof-termios
1668 termios
1669 read-termios
1670 write-termios!
1671 (input-flags unsigned-int)
1672 (output-flags unsigned-int)
1673 (control-flags unsigned-int)
1674 (local-flags unsigned-int)
1675 (line-discipline uint8)
1676 (control-chars (array uint8 32))
1677 (input-speed unsigned-int)
1678 (output-speed unsigned-int))
1679
1680(define tcgetattr
1681 (let ((proc (syscall->procedure int "tcgetattr" (list int '*))))
1682 (lambda (fd)
1683 "Return the <termios> structure for the tty at FD."
26ffb693
LC
1684 (let*-values (((bv) (make-bytevector sizeof-termios))
1685 ((ret err) (proc fd (bytevector->pointer bv))))
ae4ff9f3
LC
1686 (if (zero? ret)
1687 (read-termios bv)
1688 (throw 'system-error "tcgetattr" "~A"
1689 (list (strerror err))
1690 (list err)))))))
1691
1692(define tcsetattr
1693 (let ((proc (syscall->procedure int "tcsetattr" (list int int '*))))
1694 (lambda (fd actions termios)
a8f3424b
LC
1695 "Use TERMIOS for the tty at FD. ACTIONS is one of of the values
1696produced by 'tcsetattr-action'; see tcsetattr(3) for details."
ae4ff9f3
LC
1697 (define bv
1698 (make-bytevector sizeof-termios))
1699
1700 (let-syntax ((match/write (syntax-rules ()
1701 ((_ fields ...)
1702 (match termios
1703 (($ <termios> fields ...)
1704 (write-termios! bv 0 fields ...)))))))
1705 (match/write input-flags output-flags control-flags local-flags
1706 line-discipline control-chars input-speed output-speed))
1707
26ffb693 1708 (let-values (((ret err) (proc fd actions (bytevector->pointer bv))))
ae4ff9f3
LC
1709 (unless (zero? ret)
1710 (throw 'system-error "tcgetattr" "~A"
1711 (list (strerror err))
1712 (list err)))))))
1713
29ff6d9f
LC
1714(define-syntax TIOCGWINSZ ;<asm-generic/ioctls.h>
1715 (identifier-syntax #x5413))
1716
1717(define-record-type <window-size>
1718 (window-size rows columns x-pixels y-pixels)
1719 window-size?
1720 (rows window-size-rows)
1721 (columns window-size-columns)
1722 (x-pixels window-size-x-pixels)
1723 (y-pixels window-size-y-pixels))
1724
1725(define-c-struct winsize ;<bits/ioctl-types.h>
785cfa87 1726 sizeof-winsize
29ff6d9f
LC
1727 window-size
1728 read-winsize
1729 write-winsize!
1730 (rows unsigned-short)
1731 (columns unsigned-short)
1732 (x-pixels unsigned-short)
1733 (y-pixels unsigned-short))
1734
29ff6d9f
LC
1735(define* (terminal-window-size #:optional (port (current-output-port)))
1736 "Return a <window-size> structure describing the terminal at PORT, or raise
1737a 'system-error' if PORT is not backed by a terminal. This procedure
1738corresponds to the TIOCGWINSZ ioctl."
26ffb693
LC
1739 (let*-values (((size) (make-bytevector sizeof-winsize))
1740 ((ret err) (%ioctl (fileno port) TIOCGWINSZ
1741 (bytevector->pointer size))))
29ff6d9f 1742 (if (zero? ret)
785cfa87 1743 (read-winsize size)
29ff6d9f
LC
1744 (throw 'system-error "terminal-window-size" "~A"
1745 (list (strerror err))
1746 (list err)))))
1747
1748(define* (terminal-columns #:optional (port (current-output-port)))
1749 "Return the best approximation of the number of columns of the terminal at
1750PORT, trying to guess a reasonable value if all else fails. The result is
1751always a positive integer."
1752 (define (fall-back)
1753 (match (and=> (getenv "COLUMNS") string->number)
1754 (#f 80)
1755 ((? number? columns)
1756 (if (> columns 0) columns 80))))
1757
1758 (catch 'system-error
1759 (lambda ()
6d2b4391
LC
1760 (if (file-port? port)
1761 (match (window-size-columns (terminal-window-size port))
1762 ;; Things like Emacs shell-mode return 0, which is unreasonable.
1763 (0 (fall-back))
1764 ((? number? columns) columns))
1765 (fall-back)))
29ff6d9f
LC
1766 (lambda args
1767 (let ((errno (system-error-errno args)))
5cd25aad
LC
1768 ;; ENOTTY is what we're after but 2012-and-earlier Linux versions
1769 ;; would return EINVAL instead in some cases:
1770 ;; <https://bugs.ruby-lang.org/issues/10494>.
a6e0ae40
LC
1771 ;; Furthermore, some FUSE file systems like unionfs return ENOSYS for
1772 ;; that ioctl.
1773 (if (memv errno (list ENOTTY EINVAL ENOSYS))
29ff6d9f
LC
1774 (fall-back)
1775 (apply throw args))))))
1776
15030972
LC
1777\f
1778;;;
1779;;; utmpx.
1780;;;
1781
1782(define-record-type <utmpx-entry>
1783 (utmpx type pid line id user host termination exit
1784 session time address)
1785 utmpx?
1786 (type utmpx-login-type) ;login-type
1787 (pid utmpx-pid)
1788 (line utmpx-line) ;device name
1789 (id utmpx-id)
1790 (user utmpx-user) ;user name
1791 (host utmpx-host) ;host name | #f
1792 (termination utmpx-termination-status)
1793 (exit utmpx-exit-status)
1794 (session utmpx-session-id) ;session ID, for windowing
1795 (time utmpx-time) ;entry time
1796 (address utmpx-address))
1797
1798(define-c-struct %utmpx ;<utmpx.h>
1799 sizeof-utmpx
1800 (lambda (type pid line id user host termination exit session
1801 seconds useconds address %reserved)
1802 (utmpx type pid
1803 (bytes->string line) id
1804 (bytes->string user)
1805 (bytes->string host) termination exit
1806 session
1807 (make-time time-utc (* 1000 useconds) seconds)
1808 address))
1809 read-utmpx
1810 write-utmpx!
1811 (type short)
1812 (pid int)
1813 (line (array uint8 32))
1814 (id (array uint8 4))
1815 (user (array uint8 32))
1816 (host (array uint8 256))
1817 (termination short)
1818 (exit short)
1819 (session int32)
1820 (time-seconds int32)
1821 (time-useconds int32)
1822 (address-v6 (array int32 4))
1823 (%reserved (array uint8 20)))
1824
1825(define-bits login-type
1826 %unused-login-type->symbols
1827 (define EMPTY 0) ;No valid user accounting information.
1828 (define RUN_LVL 1) ;The system's runlevel.
1829 (define BOOT_TIME 2) ;Time of system boot.
1830 (define NEW_TIME 3) ;Time after system clock changed.
1831 (define OLD_TIME 4) ;Time when system clock changed.
1832
1833 (define INIT_PROCESS 5) ;Process spawned by the init process.
1834 (define LOGIN_PROCESS 6) ;Session leader of a logged in user.
1835 (define USER_PROCESS 7) ;Normal process.
1836 (define DEAD_PROCESS 8) ;Terminated process.
1837
1838 (define ACCOUNTING 9)) ;System accounting.
1839
1840(define setutxent
1841 (let ((proc (syscall->procedure void "setutxent" '())))
1842 (lambda ()
1843 "Open the user accounting database."
1844 (proc))))
1845
1846(define endutxent
1847 (let ((proc (syscall->procedure void "endutxent" '())))
1848 (lambda ()
1849 "Close the user accounting database."
1850 (proc))))
1851
1852(define getutxent
1853 (let ((proc (syscall->procedure '* "getutxent" '())))
1854 (lambda ()
1855 "Return the next entry from the user accounting database."
1856 (let ((ptr (proc)))
1857 (if (null-pointer? ptr)
1858 #f
1859 (read-utmpx (pointer->bytevector ptr sizeof-utmpx)))))))
1860
1861(define (utmpx-entries)
1862 "Return the list of entries read from the user accounting database."
1863 (setutxent)
1864 (let loop ((entries '()))
1865 (match (getutxent)
1866 (#f
1867 (endutxent)
1868 (reverse entries))
1869 ((? utmpx? entry)
1870 (loop (cons entry entries))))))
1871
3483f004
LC
1872(define (read-utmpx-from-port port)
1873 "Read a utmpx entry from PORT. Return either the EOF object or a utmpx
1874entry."
1875 (match (get-bytevector-n port sizeof-utmpx)
1876 ((? eof-object? eof)
1877 eof)
1878 ((? bytevector? bv)
1879 (read-utmpx bv))))
1880
29fa45f4 1881;;; syscalls.scm ends here