Subject: Re: [bug#44800] [PATCH v2 1/3] Add Avahi support.

From: Ludovic Courtès <[email-masked]>
Date: Fri, 27 Nov 2020 18:04:44 +0100

Mathieu Othacehe <[email-masked]> skribis:

> * guix/avahi.scm: New file.
> * Makefile.am (MODULES): Add it.
> * configure.ac: Add Guile-Avahi dependency.
> * doc/guix.texi (Requirements): Document it.
> * gnu/packages/package-management.scm (guix)[native-inputs]: Add
> "guile-avahi",
> [propagated-inputs]: ditto.
> * guix/self.scm (specification->package): Add guile-avahi.
> (compiled-guix): Ditto.

[...]

> --- a/configure.ac
> +++ b/configure.ac
> @@ -161,6 +161,12 @@ if test "x$have_guile_lzlib" != "xyes"; then
>    AC_MSG_ERROR([Guile-lzlib is missing; please install it.])
>  fi
>  
> +dnl Check for Guile-Avahi.
> +GUILE_MODULE_AVAILABLE([have_guile_avahi], [(avahi)])
> +if test "x$have_guile_avahi" != "xyes"; then
> +  AC_MSG_ERROR([Guile-Avahi is missing; please install it.])
> +fi

I wonder if we could/should make it an optional dependency.

(guix avahi) would need to autoload (avahi), which might be slightly
annoying.

An argument in favor of making it mandatory is that it would help make
the feature more widely used, and thus more widely useful.

> +(define-record-type* <avahi-service>
> +  avahi-service make-avahi-service
> +  avahi-service?
> +  (name avahi-service-name)
> +  (type avahi-service-type)
> +  (interface avahi-service-interface)
> +  (local-address avahi-service-local-address)
> +  (address avahi-service-address)
> +  (port avahi-service-port)
> +  (txt avahi-service-txt))

You could use (srfi srfi-9) ‘define-record-type’ since the extra (guix
records) features are not necessary here.

> +(define* (avahi-publish-service-thread name
> +                                       #:key
> +                                       type port
> +                                       (stop-loop? (const #f))
> +                                       (timeout 100)
> +                                       (txt '()))
> +  "Publish the service TYPE using Avahi, for the given PORT, on all interfaces
> +and for all protocols. Also, advertise the given TXT record list.
> +
> +This procedure starts a new thread running the Avahi event loop.  It exits
> +when STOP-LOOP? procedure returns true."
> +  (define client-callback
> +    (lambda (client state)
> +      (when (eq? state client-state/s-running)
> +        (let ((group (make-entry-group client (const #t))))
> +          (apply
> +           add-entry-group-service! group interface/unspecified
> +           protocol/unspecified '()
> +           name type #f #f port txt)
> +          (commit-entry-group group)))))
> +
> +  (call-with-new-thread
> +   (lambda ()
> +     (let* ((poll (make-simple-poll))
> +            (client (make-client (simple-poll poll)
> +                                 (list
> +                                  client-flag/ignore-user-config)
> +                                 client-callback)))
> +       (while (not (stop-loop?))
> +         (iterate-simple-poll poll timeout))))))

(I wanted to add an API in Guile-Avahi to “invert inversion of control”
so that one could escape callback hell but never got around to
completing it.)

> +(define (interface->ip-address interface)
> +  "Return the local IP address of the given INTERFACE."
> +  (let ((address
> +         (network-interface-address
> +          (socket AF_INET SOCK_STREAM 0) interface)))
> +    (inet-ntop (sockaddr:fam address) (sockaddr:addr address))))

Make sure to close the socket.

Can’t we obtain the IP address without creating a socket actually?  Noob
here.

> +    ;; Handle service resolution events.
> +    (cond ((eq? event resolver-event/found)
> +           (info (G_ "resolved service `~a' at `~a:~a'~%")
> +                 service-name (inet-ntop family address) port)

IWBN to not add UI code in here.

Thanks,
Ludo’.