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