link: Add 'wait-for-link'. * ip/link.scm (message->event+link): New procedure. (new-link-message->link): Use it. (monitor-links, wait-for-link): New procedures. * doc/guile-netlink.texi (Link): Document 'wait-for-link'. Signed-off-by: Julien Lepiller <julien@lepiller.eu>
doc/guile-netlink.texi
567 | 567 | objects. | |
568 | 568 | @end deffn | |
569 | 569 | ||
570 | + | @deffn {Scheme Procedure} wait-for-link @var{name} [#:blocking? #t] | |
571 | + | Wait until a link called @var{name} (a string such as @code{"ens3"}) shows | |
572 | + | up. | |
573 | + | ||
574 | + | When @var{blocking?} is false, use a non-blocking socket and cooperate via | |
575 | + | @code{current-read-waiter}---useful when using Fibers. | |
576 | + | @end deffn | |
577 | + | ||
570 | 578 | @deffn {Sceme Procedure} print-link @var{link} | |
571 | 579 | Display @var{link} on the standard output, using a format similar to | |
572 | 580 | @command{ip link} from @code{iproute2}. |
ip/link.scm
1 | 1 | ;;;; This file is part of Guile Netlink | |
2 | 2 | ;;;; | |
3 | 3 | ;;;; Copyright (C) 2021 Julien Lepiller <julien@lepiller.eu> | |
4 | - | ;;;; | |
4 | + | ;;;; Copyright (C) 2023 Ludovic Court??s <ludo@gnu.org> | |
5 | + | ;;;; | |
5 | 6 | ;;;; This library is free software: you can redistribute it and/or modify | |
6 | 7 | ;;;; it under the terms of the GNU General Public License as published by | |
7 | 8 | ;;;; the Free Software Foundation, either version 3 of the License, or | |
… | |||
31 | 32 | #:use-module (srfi srfi-9) | |
32 | 33 | #:use-module (srfi srfi-34) | |
33 | 34 | #:use-module (srfi srfi-35) | |
35 | + | #:use-module (srfi srfi-71) | |
34 | 36 | #:export (link-add | |
35 | 37 | link-del | |
36 | 38 | link-set | |
37 | 39 | link-show | |
38 | 40 | link-name->index | |
39 | 41 | get-links | |
42 | + | wait-for-link | |
40 | 43 | print-link | |
41 | 44 | ||
42 | 45 | <link> make-link link? | |
… | |||
59 | 62 | (addr link-addr) | |
60 | 63 | (brd link-brd)) | |
61 | 64 | ||
65 | + | (define (message->event+link msg) | |
66 | + | "If MSG relates to a link event, return two values: its kind (e.g., | |
67 | + | RTM_NEWLINK) and its associated <link> value. Otherwise return #f and #f." | |
68 | + | (if (memv (message-kind msg) | |
69 | + | (list RTM_NEWLINK | |
70 | + | RTM_DELLINK | |
71 | + | RTM_SETLINK)) | |
72 | + | (values (message-kind msg) | |
73 | + | (let* ((data (message-data msg)) | |
74 | + | (attrs (link-message-attrs data))) | |
75 | + | (make-link (get-attr attrs IFLA_IFNAME) | |
76 | + | (link-message-index data) | |
77 | + | (link-message-kind data) | |
78 | + | (map int->device-flags (split-flags (link-message-flags data))) | |
79 | + | (get-attr attrs IFLA_MTU) | |
80 | + | (get-attr attrs IFLA_QDISC) | |
81 | + | (get-attr attrs IFLA_OPERSTATE) | |
82 | + | (get-attr attrs IFLA_LINKMODE) | |
83 | + | (get-attr attrs IFLA_GROUP) | |
84 | + | (get-attr attrs IFLA_TXQLEN) | |
85 | + | (get-attr attrs IFLA_ADDRESS) | |
86 | + | (get-attr attrs IFLA_BROADCAST)))) | |
87 | + | (values #f #f))) | |
88 | + | ||
62 | 89 | (define (new-link-message->link msg) | |
63 | 90 | "If MSG has type 'RTM_NEWLINK', return the corresponding <link> object. | |
64 | 91 | Otherwise return #f." | |
65 | - | (and (eqv? (message-kind msg) RTM_NEWLINK) | |
66 | - | (let* ((data (message-data msg)) | |
67 | - | (attrs (link-message-attrs data))) | |
68 | - | (make-link (get-attr attrs IFLA_IFNAME) | |
69 | - | (link-message-index data) | |
70 | - | (link-message-kind data) | |
71 | - | (map int->device-flags (split-flags (link-message-flags data))) | |
72 | - | (get-attr attrs IFLA_MTU) | |
73 | - | (get-attr attrs IFLA_QDISC) | |
74 | - | (get-attr attrs IFLA_OPERSTATE) | |
75 | - | (get-attr attrs IFLA_LINKMODE) | |
76 | - | (get-attr attrs IFLA_GROUP) | |
77 | - | (get-attr attrs IFLA_TXQLEN) | |
78 | - | (get-attr attrs IFLA_ADDRESS) | |
79 | - | (get-attr attrs IFLA_BROADCAST))))) | |
92 | + | (let ((kind link (message->event+link msg))) | |
93 | + | (and (eqv? kind RTM_NEWLINK) link))) | |
80 | 94 | ||
81 | 95 | (define (get-links) | |
82 | 96 | (define request-num (random 65535)) | |
… | |||
390 | 404 | (let ((answer (receive-and-decode-msg sock %default-route-decoder))) | |
391 | 405 | (close-port sock) | |
392 | 406 | (answer-ok? (last answer))))) | |
407 | + | ||
408 | + | (define* (monitor-links proc init terminate? ;TODO: Make public? | |
409 | + | #:key (blocking? #t)) | |
410 | + | "Wait for link events until @var{terminate?} returns true. Call @var{init} | |
411 | + | with the initial list of links; use its result as the initial state. From | |
412 | + | then on, call @code{(@var{proc} @var{event} @var{link} @var{state})} where | |
413 | + | @var{event} is a constant such as @code{RTM_NEWLINK} and @var{link} is the | |
414 | + | corresponding link. Return the final state. | |
415 | + | ||
416 | + | When @code{blocking?} is false, use a non-blocking socket and cooperate via | |
417 | + | @code{current-read-waiter}---useful when using Fibers." | |
418 | + | (define request-num (random 65536)) | |
419 | + | (define message | |
420 | + | (make-message | |
421 | + | RTM_GETLINK | |
422 | + | (logior NLM_F_REQUEST NLM_F_DUMP) | |
423 | + | request-num | |
424 | + | 0 | |
425 | + | (make-link-message AF_UNSPEC 0 0 0 0 '()))) | |
426 | + | ||
427 | + | (let ((sock (connect-route #:flags (if blocking? 0 SOCK_NONBLOCK)))) | |
428 | + | ;; Subscribe to the "link" group. | |
429 | + | (add-socket-membership sock RTNLGRP_LINK) | |
430 | + | ||
431 | + | (send-msg message sock) | |
432 | + | (let* ((answer (receive-and-decode-msg sock %default-route-decoder)) | |
433 | + | (links (filter-map new-link-message->link answer))) | |
434 | + | (let loop ((state (init links))) | |
435 | + | (if (terminate? state) | |
436 | + | (begin | |
437 | + | (close-port sock) | |
438 | + | state) | |
439 | + | (loop (fold (lambda (msg state) | |
440 | + | (let ((event link (message->event+link msg))) | |
441 | + | (proc event link state))) | |
442 | + | state | |
443 | + | (receive-and-decode-msg sock %default-route-decoder)))))))) | |
444 | + | ||
445 | + | ||
446 | + | (define* (wait-for-link name #:key (blocking? #t)) | |
447 | + | "Wait until a link called @var{name} (a string such as @code{\"ens3\"}) shows | |
448 | + | up. | |
449 | + | ||
450 | + | When @var{blocking?} is false, use a non-blocking socket and cooperate via | |
451 | + | @code{current-read-waiter}---useful when using Fibers." | |
452 | + | (monitor-links (lambda (event link result) | |
453 | + | (and (= RTM_NEWLINK) | |
454 | + | (string=? (link-name link) name) | |
455 | + | link)) | |
456 | + | (lambda (links) | |
457 | + | (find (lambda (link) | |
458 | + | (string=? (link-name link) name)) | |
459 | + | links)) | |
460 | + | (lambda (link) ;if LINK is true, terminate | |
461 | + | link) | |
462 | + | #:blocking? blocking?)) |