civodul pushed a commit to branch wip-syslogd
in repository shepherd.
commit 714cfc3f47ec6fc1fa471ed04a2a6754d8c8c50b
Author: Ludovic Courtès <[email protected]>
AuthorDate: Wed Jul 17 16:45:25 2024 +0200
squash! Move from SOCK_STREAM to SOCK_DGRAM.
---
modules/shepherd/service/system-log.scm | 163 +++++++++++++++-----------------
tests/services/system-log.sh | 6 +-
2 files changed, 82 insertions(+), 87 deletions(-)
diff --git a/modules/shepherd/service/system-log.scm
b/modules/shepherd/service/system-log.scm
index f9e1be0..1a672af 100644
--- a/modules/shepherd/service/system-log.scm
+++ b/modules/shepherd/service/system-log.scm
@@ -28,8 +28,11 @@
#:use-module (ice-9 match)
#:use-module (ice-9 regex)
#:autoload (ice-9 rdelim) (read-line)
+ #:autoload (ice-9 iconv) (bytevector->string)
#:use-module (ice-9 vlist)
- #:use-module (rnrs io ports)
+ #:use-module ((rnrs io ports) #:hide (bytevector->string))
+ #:use-module (rnrs bytevectors)
+ #:autoload (rnrs bytevectors gnu) (bytevector-slice)
#:use-module (fibers operations)
#:use-module (fibers channels)
#:autoload (fibers io-wakeup) (wait-until-port-readable-operation)
@@ -123,80 +126,37 @@
(define %default-priority (system-log-priority notice))
(define %default-facility (system-log-facility user))
+(define (parse-system-log-message line)
+ "Parse @var{line} and return a @code{<system-log-message>} record
+representing it."
+ (match (false-if-exception (regexp-exec %system-log-message-rx line))
+ (#f
+ (system-log-message (logior %default-facility %default-priority)
+ #f #f line))
+ (m
+ (let* ((facility+priority (string->number (match:substring m 1)))
+ (process+pid (match:substring m 3))
+ (process pid (match (regexp-exec %process+pid-rx
+ process+pid)
+ (#f (values process+pid #f))
+ (m (values (match:substring m 1)
+ (string->number
+ (match:substring m 2)))))))
+ (system-log-message facility+priority
+ process pid
+ (match:substring m 4))))))
+
(define (read-system-log-message port)
+ "Read a system log message from @var{port}. Return the end-of-file object
+or a <system-log-message> object."
(match (read-line port)
- ((? eof-object? eof)
- eof)
- (line
- (match (false-if-exception (regexp-exec %system-log-message-rx line))
- (#f
- (system-log-message (logior %default-facility %default-priority)
- #f #f line))
- (m
- (let* ((facility+priority (string->number (match:substring m 1)))
- (process+pid (match:substring m 3))
- (process pid (match (regexp-exec %process+pid-rx
- process+pid)
- (#f (values process+pid #f))
- (m (values (match:substring m 1)
- (string->number
- (match:substring m 2)))))))
- (system-log-message facility+priority
- process pid
- (match:substring m 4))))))))
-
-(define (read-client-log client dispatcher)
- (let loop ()
- (match (read-system-log-message client)
- ((? eof-object?)
- #t)
- (#f
- (loop))
- (message
- (put-message dispatcher message)
- (loop)))))
-
-(define (spawn-child-service client parent id dispatcher)
- "Register and start a new service that reads messages from @var{client}, a
-socket, passing them to @var{dispatcher} for actual logging. @var{parent} and
-@var{id} are used to generate the service's name."
- (letrec* ((name (string->symbol
- (string-append (symbol->string parent) "-client-"
- (number->string id))))
- (child (service
- (list name)
- #:transient? #t
- #:respawn? #f
- #:requirement (list parent)
- #:start (lambda ()
- (spawn-fiber
- (lambda ()
- (read-client-log client dispatcher)))
- client)
- #:stop (lambda (client)
- (close-port client)
- #f))))
- (register-services (list child))
- (start-service child)))
-
-(define (accept/get-message socket channel)
- "Wait for connections on @var{socket} and for messages on @var{channel}.
-Return either the return value of the @code{accept} procedure, for incoming
-connections, or the message received on @var{channel} wrapped in a list whose
-first element is @code{'message}."
- (perform-operation
- (choice-operation
- (wrap-operation (wait-until-port-readable-operation socket)
- (lambda ()
- (accept socket (logior SOCK_NONBLOCK SOCK_CLOEXEC))))
- (wrap-operation (get-operation channel)
- (lambda (message)
- (list 'message message))))))
+ ((? eof-object? eof) eof)
+ (line (parse-system-log-message line))))
(define* (open-socket file #:key (backlog 512))
- "Open a socket listening on @var{file} and return it."
+ "Open a datagram socket listening on @var{file} and return it."
(let ((socket (socket AF_UNIX
- (logior SOCK_STREAM SOCK_NONBLOCK SOCK_CLOEXEC)
+ (logior SOCK_DGRAM SOCK_NONBLOCK SOCK_CLOEXEC)
0)))
(catch 'system-error
(lambda ()
@@ -207,24 +167,56 @@ first element is @code{'message}."
(false-if-exception (delete-file file))
(bind socket AF_UNIX file))
(apply throw args))))
- (listen socket backlog)
socket))
-(define (run-system-log channel name socket dispatcher)
- "Run the system log, where @var{name} is its service name. Listen for
-connections on @var{socket} and for control messages on @var{channel}. Send
-incoming system log messages to @var{dispatcher}."
- (let loop ((id 0))
- (match (accept/get-message socket channel)
- (('message ('terminate reply))
+(define (wait-for-input-or-message socket channel)
+ "Wait for input on @var{socket} or messages on @var{channel}. Return
+@var{socket} when input is available or the message received on @var{channel}."
+ (perform-operation
+ (choice-operation
+ (wrap-operation (wait-until-port-readable-operation socket)
+ (const socket))
+ (get-operation channel))))
+
+(define (maybe-utf8->string bv)
+ "Attempt to decode BV as UTF-8 string and return it. Gracefully handle the
+case where BV does not contain only valid UTF-8."
+ (catch 'decoding-error
+ (lambda ()
+ (utf8->string bv))
+ (lambda _
+ ;; Fall back to something that should be safe.
+ (bytevector->string bv "ISO-8859-1" 'substitute))))
+
+(define (run-system-log channel socket dispatcher)
+ "Return the system log, getting instructions on @var{channel}, receiving
+messages to be logged on @var{socket} and passing them to @var{dispatcher}."
+ (define (input-marker? x)
+ (eq? x socket))
+
+ (define buffer
+ (make-bytevector 1024))
+
+ (let loop ()
+ (match (wait-for-input-or-message socket channel)
+ ((? input-marker?)
+ (match (recvfrom! socket buffer)
+ ((0 . _)
+ #t)
+ ((bytes . sender)
+ (let ((bytes (if (= (char->integer #\newline)
+ (bytevector-u8-ref buffer (- bytes 1)))
+ (- bytes 1)
+ bytes)))
+ (put-message dispatcher
+ (parse-system-log-message
+ (maybe-utf8->string
+ (bytevector-slice buffer 0 bytes)))))))
+ (loop))
+ (('terminate reply)
(local-output (l10n "Terminating system log service."))
(close-port socket)
- (put-message dispatcher `(terminate ,reply)))
- ((port . client)
- (local-output (l10n "New system log connection from ~s.")
- client)
- (spawn-child-service port name id dispatcher)
- (loop (+ 1 id))))))
+ (put-message dispatcher `(terminate ,reply))))))
(define (log-dispatcher channel log-file)
"Dispatch system log messages received on @var{channel} to log files. Call
@@ -307,8 +299,7 @@ to write to."
(dispatcher (spawn-log-dispatcher log-file)))
(spawn-fiber
(lambda ()
- (run-system-log channel (car provision)
- socket dispatcher)))
+ (run-system-log channel socket dispatcher)))
socket))
#:stop (lambda (socket)
(let ((reply (make-channel)))
diff --git a/tests/services/system-log.sh b/tests/services/system-log.sh
index 7f388e1..074f949 100644
--- a/tests/services/system-log.sh
+++ b/tests/services/system-log.sh
@@ -66,7 +66,7 @@ cat > "$logger" <<EOF
!#
(display "starting logger\n")
-(let ((sock (socket AF_UNIX SOCK_STREAM 0)))
+(let ((sock (socket AF_UNIX SOCK_DGRAM 0)))
(connect sock AF_UNIX "$syslog_socket")
(display "<86> Jun 29 10:45:54 sudo: pam_unix(sudo:session): session opened
for user root\n" sock)
(display "<85>Jul 14 12:32:50 sudo: pam_unix(sudo:auth): authentication
failure; logname= uid=1000\n" sock)
@@ -107,6 +107,10 @@ grep "ntpd\[427\]: new interface" "$syslog_debug_file"
grep "elogind\[286\]: Power key pressed short" "$syslog_file"
grep "NetworkManager\[319\]: .*sleep" "$syslog_file"
+test $(wc -l < "$syslog_auth_file") -eq 3
+test $(wc -l < "$syslog_debug_file") -eq 1
+test $(wc -l < "$syslog_file") -eq 2
+
for file in "$syslog_file" "$syslog_auth_file" "$syslog_debug_file"
do
cat "$file"