avahi.scm 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2020 Mathieu Othacehe <othacehe@gnu.org>
  3. ;;;
  4. ;;; This file is part of GNU Guix.
  5. ;;;
  6. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  7. ;;; under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation; either version 3 of the License, or (at
  9. ;;; your option) any later version.
  10. ;;;
  11. ;;; GNU Guix is distributed in the hope that it will be useful, but
  12. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;;; GNU General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU General Public License
  17. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  18. (define-module (guix avahi)
  19. #:use-module (guix records)
  20. #:use-module (guix build syscalls)
  21. #:use-module (avahi)
  22. #:use-module (avahi client)
  23. #:use-module (avahi client lookup)
  24. #:use-module (avahi client publish)
  25. #:use-module (srfi srfi-9)
  26. #:use-module (ice-9 threads)
  27. #:export (avahi-service
  28. avahi-service?
  29. avahi-service-name
  30. avahi-service-type
  31. avahi-service-interface
  32. avahi-service-local-address
  33. avahi-service-address
  34. avahi-service-port
  35. avahi-service-txt
  36. avahi-publish-service-thread
  37. avahi-browse-service-thread))
  38. (define-record-type* <avahi-service>
  39. avahi-service make-avahi-service
  40. avahi-service?
  41. (name avahi-service-name)
  42. (type avahi-service-type)
  43. (interface avahi-service-interface)
  44. (local-address avahi-service-local-address)
  45. (address avahi-service-address)
  46. (port avahi-service-port)
  47. (txt avahi-service-txt))
  48. (define* (avahi-publish-service-thread name
  49. #:key
  50. type port
  51. (stop-loop? (const #f))
  52. (timeout 100)
  53. (txt '()))
  54. "Publish the service TYPE using Avahi, for the given PORT, on all interfaces
  55. and for all protocols. Also, advertise the given TXT record list.
  56. This procedure starts a new thread running the Avahi event loop. It exits
  57. when STOP-LOOP? procedure returns true."
  58. (define client-callback
  59. (lambda (client state)
  60. (when (eq? state client-state/s-running)
  61. (let ((group (make-entry-group client (const #t))))
  62. (apply
  63. add-entry-group-service! group interface/unspecified
  64. protocol/unspecified '()
  65. name type #f #f port txt)
  66. (commit-entry-group group)))))
  67. (call-with-new-thread
  68. (lambda ()
  69. (let* ((poll (make-simple-poll))
  70. (client (make-client (simple-poll poll)
  71. (list
  72. client-flag/ignore-user-config)
  73. client-callback)))
  74. (while (not (stop-loop?))
  75. (iterate-simple-poll poll timeout))))))
  76. (define (interface->ip-address interface)
  77. "Return the local IP address of the given INTERFACE."
  78. (let* ((socket (socket AF_INET SOCK_STREAM 0))
  79. (address (network-interface-address socket interface))
  80. (ip (inet-ntop (sockaddr:fam address)
  81. (sockaddr:addr address))))
  82. (close-port socket)
  83. ip))
  84. (define never
  85. ;; Never true.
  86. (const #f))
  87. (define* (avahi-browse-service-thread proc
  88. #:key
  89. types
  90. (ignore-local? #t)
  91. (family AF_INET)
  92. (stop-loop? never)
  93. (timeout (if (eq? stop-loop? never)
  94. #f
  95. 100)))
  96. "Browse services which type is part of the TYPES list, using Avahi. The
  97. search is restricted to services with the given FAMILY. Each time a service
  98. is found or removed, PROC is called and passed as argument the corresponding
  99. AVAHI-SERVICE record. If a service is available on multiple network
  100. interfaces, it will only be reported on the first interface found.
  101. This procedure starts a new thread running the Avahi event loop. It exits
  102. when STOP-LOOP? procedure returns true."
  103. (define %known-hosts
  104. ;; Set of Avahi discovered hosts.
  105. (make-hash-table))
  106. (define (service-resolver-callback resolver interface protocol event
  107. service-name service-type domain
  108. host-name address-type address port
  109. txt flags)
  110. ;; Handle service resolution events.
  111. (cond ((eq? event resolver-event/found)
  112. ;; Add the service if the host is unknown. This means that if a
  113. ;; service is available on multiple network interfaces for a single
  114. ;; host, only the first interface found will be considered.
  115. (unless (or (hash-ref %known-hosts service-name)
  116. (and ignore-local?
  117. (member lookup-result-flag/local flags)))
  118. (let* ((address (inet-ntop family address))
  119. (local-address (interface->ip-address interface))
  120. (service* (avahi-service
  121. (name service-name)
  122. (type service-type)
  123. (interface interface)
  124. (local-address local-address)
  125. (address address)
  126. (port port)
  127. (txt txt))))
  128. (hash-set! %known-hosts service-name service*)
  129. (proc 'new-service service*)))))
  130. (free-service-resolver! resolver))
  131. (define (service-browser-callback browser interface protocol event
  132. service-name service-type
  133. domain flags)
  134. (cond
  135. ((eq? event browser-event/new)
  136. (make-service-resolver (service-browser-client browser)
  137. interface protocol
  138. service-name service-type domain
  139. protocol/unspecified '()
  140. service-resolver-callback))
  141. ((eq? event browser-event/remove)
  142. (let ((service (hash-ref %known-hosts service-name)))
  143. (when service
  144. (proc 'remove-service service)
  145. (hash-remove! %known-hosts service-name))))))
  146. (define client-callback
  147. (lambda (client state)
  148. (if (eq? state client-state/s-running)
  149. (for-each (lambda (type)
  150. (make-service-browser client
  151. interface/unspecified
  152. protocol/inet
  153. type #f '()
  154. service-browser-callback))
  155. types))))
  156. (let* ((poll (make-simple-poll))
  157. (client (make-client (simple-poll poll)
  158. '() ;; no flags
  159. client-callback)))
  160. (and (client? client)
  161. (while (not (stop-loop?))
  162. (if timeout
  163. (iterate-simple-poll poll timeout)
  164. (iterate-simple-poll poll))))))