[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[shepherd] 05/32: shepherd: Factorize out the main loop.
From: |
Ludovic Courtès |
Subject: |
[shepherd] 05/32: shepherd: Factorize out the main loop. |
Date: |
Wed, 30 Mar 2022 11:01:27 -0400 (EDT) |
civodul pushed a commit to branch master
in repository shepherd.
commit e541398683fb8ddd657a19154b880a2b38a72428
Author: Ludovic Courtès <ludo@gnu.org>
AuthorDate: Mon Mar 21 09:56:28 2022 +0100
shepherd: Factorize out the main loop.
* modules/shepherd.scm (run-daemon): New procedure, with code moved
from...
(main): ... here.
---
modules/shepherd.scm | 162 +++++++++++++++++++++++++++------------------------
1 file changed, 86 insertions(+), 76 deletions(-)
diff --git a/modules/shepherd.scm b/modules/shepherd.scm
index 4747733..4365ca8 100644
--- a/modules/shepherd.scm
+++ b/modules/shepherd.scm
@@ -117,6 +117,86 @@ already ~a threads running, disabling 'signalfd' support")
((signal-handler signal))))
+(define* (run-daemon #:key (config-file (default-config-file)) persistency
+ socket-file pid-file signal-port poll-services?)
+ ;; This _must_ succeed. (We could also put the `catch' around
+ ;; `main', but it is often useful to get the backtrace, and
+ ;; `caught-error' does not do this yet.)
+ (catch #t
+ (lambda ()
+ (load-in-user-module (or config-file (default-config-file))))
+ (lambda (key . args)
+ (caught-error key args)
+ (quit 1)))
+ ;; Start what was started last time.
+ (and persistency
+ (catch 'system-error
+ (lambda ()
+ (start-in-order (read (open-input-file
+ persistency-state-file))))
+ (lambda (key . args)
+ (apply format #f (gettext (cadr args)) (caddr args))
+ (quit 1))))
+
+ ;; Ignore SIGPIPE so that we don't die if a client closes the connection
+ ;; prematurely.
+ (sigaction SIGPIPE SIG_IGN)
+
+ (if (not socket-file)
+ ;; Get commands from the standard input port.
+ (process-textual-commands (current-input-port))
+ ;; Process the data arriving at a socket.
+ (call-with-server-socket
+ socket-file
+ (lambda (sock)
+
+ ;; Possibly write out our PID, which means we're ready to accept
+ ;; connections. XXX: What if we daemonized already?
+ (match pid-file
+ ((? string? file)
+ (with-atomic-file-output pid-file
+ (cute display (getpid) <>)))
+ (#t (display (getpid)))
+ (_ #t))
+
+ ;; XXX: This call mostly to resolve 'handle-SIGCHLD' upfront.
+ ;; This works around Guile 3.0.2 occasionally failing with:
+ ;; "Failed to autoload handle-SIGCHLD in (ice-9 readline):"
+ (handle-SIGCHLD)
+
+ (let next-command ((ports (if signal-port
+ (list signal-port sock)
+ (list sock))))
+ (define (read-from sock)
+ (match (accept sock)
+ ((command-source . client-address)
+ (setvbuf command-source (buffering-mode block) 1024)
+ (process-connection command-source))
+ (_ #f)))
+
+ ;; When not using signalfd(2), there's always a time window
+ ;; before 'select' during which a handler async can be queued
+ ;; but not executed. Work around it by exiting 'select' every
+ ;; few seconds.
+ (match (select ports (list) (list)
+ (and (not signal-port)
+ (if poll-services? 0.5 30)))
+ (((port _ ...) _ _)
+ (if (and signal-port (eq? port signal-port))
+ (handle-signal-port port)
+ (read-from sock)))
+ (_
+ ;; 'select' returned an empty set, probably due to EINTR.
+ ;; Explicitly call the SIGCHLD handler because we cannot be
+ ;; sure the async will be queued and executed before we call
+ ;; 'select' again.
+ (handle-SIGCHLD)))
+
+ (when poll-services?
+ (check-for-dead-services))
+ (next-command ports))))))
+
+
;; Main program.
(define (main . args)
(define poll-services?
@@ -286,82 +366,12 @@ already ~a threads running, disabling 'signalfd' support")
(sigaction signal (signal-handler signal)))
(delete SIGCHLD %precious-signals))
- ;; This _must_ succeed. (We could also put the `catch' around
- ;; `main', but it is often useful to get the backtrace, and
- ;; `caught-error' does not do this yet.)
- (catch #t
- (lambda ()
- (load-in-user-module (or config-file (default-config-file))))
- (lambda (key . args)
- (caught-error key args)
- (quit 1)))
- ;; Start what was started last time.
- (and persistency
- (catch 'system-error
- (lambda ()
- (start-in-order (read (open-input-file
- persistency-state-file))))
- (lambda (key . args)
- (apply format #f (gettext (cadr args)) (caddr args))
- (quit 1))))
-
- ;; Ignore SIGPIPE so that we don't die if a client closes the connection
- ;; prematurely.
- (sigaction SIGPIPE SIG_IGN)
-
- (if (not socket-file)
- ;; Get commands from the standard input port.
- (process-textual-commands (current-input-port))
- ;; Process the data arriving at a socket.
- (call-with-server-socket
- socket-file
- (lambda (sock)
-
- ;; Possibly write out our PID, which means we're ready to accept
- ;; connections. XXX: What if we daemonized already?
- (match pid-file
- ((? string? file)
- (with-atomic-file-output pid-file
- (cute display (getpid) <>)))
- (#t (display (getpid)))
- (_ #t))
-
- ;; XXX: This call mostly to resolve 'handle-SIGCHLD' upfront.
- ;; This works around Guile 3.0.2 occasionally failing with:
- ;; "Failed to autoload handle-SIGCHLD in (ice-9 readline):"
- (handle-SIGCHLD)
-
- (let next-command ((ports (if signal-port
- (list signal-port sock)
- (list sock))))
- (define (read-from sock)
- (match (accept sock)
- ((command-source . client-address)
- (setvbuf command-source (buffering-mode block) 1024)
- (process-connection command-source))
- (_ #f)))
-
- ;; When not using signalfd(2), there's always a time window
- ;; before 'select' during which a handler async can be queued
- ;; but not executed. Work around it by exiting 'select' every
- ;; few seconds.
- (match (select ports (list) (list)
- (and (not signal-port)
- (if poll-services? 0.5 30)))
- (((port _ ...) _ _)
- (if (and signal-port (eq? port signal-port))
- (handle-signal-port port)
- (read-from sock)))
- (_
- ;; 'select' returned an empty set, probably due to EINTR.
- ;; Explicitly call the SIGCHLD handler because we cannot be
- ;; sure the async will be queued and executed before we call
- ;; 'select' again.
- (handle-SIGCHLD)))
-
- (when poll-services?
- (check-for-dead-services))
- (next-command ports))))))))
+ (run-daemon #:socket-file socket-file
+ #:config-file config-file
+ #:pid-file pid-file
+ #:signal-port signal-port
+ #:poll-services? poll-services?
+ #:persistency persistency))))
;; Start all of SERVICES, which is a list of canonical names (FIXME?),
;; but in a order where all dependencies are fulfilled before we
- [shepherd] 15/32: doc: Fix inetutils cross-reference., (continued)
- [shepherd] 15/32: doc: Fix inetutils cross-reference., Ludovic Courtès, 2022/03/30
- [shepherd] 12/32: service: 'read-pid-file' uses (@ (guile) sleep) when it's not suspendable., Ludovic Courtès, 2022/03/30
- [shepherd] 18/32: service: Add the #:transient? slot., Ludovic Courtès, 2022/03/30
- [shepherd] 17/32: service: Remove unused 'make-init.d-service'., Ludovic Courtès, 2022/03/30
- [shepherd] 24/32: shepherd: "shepherd -s -" replies to the current output port., Ludovic Courtès, 2022/03/30
- [shepherd] 26/32: service: Add #:max-connections to 'make-inetd-constructor'., Ludovic Courtès, 2022/03/30
- [shepherd] 16/32: support: 'l10n' accepts plural forms., Ludovic Courtès, 2022/03/30
- [shepherd] 23/32: shepherd: Remove half-baked readline support., Ludovic Courtès, 2022/03/30
- [shepherd] 30/32: Avoid Guile run-time warning about overridden 'sleep' binding., Ludovic Courtès, 2022/03/30
- [shepherd] 29/32: shepherd: Gracefully handle failure to open the socket., Ludovic Courtès, 2022/03/30
- [shepherd] 05/32: shepherd: Factorize out the main loop.,
Ludovic Courtès <=
- [shepherd] 19/32: service: Add inetd constructor and destructor., Ludovic Courtès, 2022/03/30
- [shepherd] 20/32: service: Allow 'running' value to be a thunk., Ludovic Courtès, 2022/03/30
- [shepherd] 27/32: service: 'make-inetd-constructor' lets the caller specify socket ownership., Ludovic Courtès, 2022/03/30
- [shepherd] 32/32: build: Bump to version 0.9.0rc1., Ludovic Courtès, 2022/03/30