aboutsummaryrefslogtreecommitdiff
path: root/src/util.lisp
diff options
context:
space:
mode:
authorSean Whitton <spwhitton@spwhitton.name>2021-07-18 22:31:41 -0700
committerSean Whitton <spwhitton@spwhitton.name>2021-07-24 09:36:57 -0700
commitf4e9170e73cb4bcfa7328422b4ff4f72d1339dd0 (patch)
tree3d05965a50b0293e1b3f3297fd0ce07fe8a1b468 /src/util.lisp
parent5186767b9c32b9f7481bfa85813c1ad34ac5f15c (diff)
downloadconsfigurator-f4e9170e73cb4bcfa7328422b4ff4f72d1339dd0.tar.gz
new approach to calling fork(2) in remote Lisp images
Drop CAN-PROBABLY-FORK because we now only try to fork(2) in contexts in which there shouldn't ever be any other threads running, apart from Lisp implementation finaliser threads and the like. We no longer need to RESET-DATA-SOURCES before CONTINUE-DEPLOY* because we now only fork(2) in contexts in which *NO-DATA-SOURCES* is t. Signed-off-by: Sean Whitton <spwhitton@spwhitton.name>
Diffstat (limited to 'src/util.lisp')
-rw-r--r--src/util.lisp70
1 files changed, 70 insertions, 0 deletions
diff --git a/src/util.lisp b/src/util.lisp
index 6de9e71..5524188 100644
--- a/src/util.lisp
+++ b/src/util.lisp
@@ -532,6 +532,47 @@ of this macro."
Should be called soon after fork(2) in child processes."
(signal 'in-child-process))
+;;; Use only implementation-specific fork, waitpid etc. calls to avoid thread
+;;; woes. Things like chroot(2) and setuid(2), however, should be okay.
+
+(defun fork ()
+ ;; Normalise any other implementations such that we signal an error if
+ ;; fork(2) returns -1, so caller doesn't have to check for that.
+ #+sbcl (sb-posix:fork))
+
+(defun waitpid (pid options)
+ ;; Normalise any other implementations such that we always return (values
+ ;; PID EXIT-STATUS), as SB-POSIX:WAITPID does.
+ #+sbcl (sb-posix:waitpid pid options))
+
+(defun wifexited (status)
+ #+sbcl (sb-posix:wifexited status))
+
+(defun wexitstatus (status)
+ #+sbcl (sb-posix:wexitstatus status))
+
+(defun setsid ()
+ #+sbcl (sb-posix:setsid))
+
+(defun umask (mode)
+ #+sbcl (sb-posix:umask mode))
+
+(defmacro forked-progn (child-pid child-form &body parent-forms)
+ (with-gensyms (retval)
+ `(progn
+ #-(or sbcl) (error "Don't know how to safely fork(2) in this Lisp.")
+ (mapc-open-output-streams
+ #'force-output
+ *standard-output* *error-output* *debug-io* *terminal-io*)
+ (let ((,retval (fork)))
+ (if (zerop ,retval)
+ ;; We leave it to the caller to appropriately call CLOSE or
+ ;; CLEAR-INPUT on input streams shared with the parent, because
+ ;; at least SBCL's CLEAR-INPUT clears the OS buffer as well as
+ ;; Lisp's, potentially denying data to both sides of the fork.
+ ,child-form
+ (let ((,child-pid ,retval)) ,@parent-forms))))))
+
(define-condition skipped-properties () ()
(:documentation
"There were failed changes, but instead of aborting, that particular property
@@ -617,3 +658,32 @@ Does not currently establish a PAM session."
(stream ,file :direction :output :if-exists :supersede)
(with-standard-io-syntax
(prin1 ,data stream)))))))
+
+
+;;;; Streams
+
+(defun stream->input-stream (stream)
+ (etypecase stream
+ (synonym-stream (stream->input-stream
+ (symbol-value (synonym-stream-symbol stream))))
+ (two-way-stream (two-way-stream-input-stream stream))
+ (stream (and (input-stream-p stream) stream))))
+
+(defun mapc-open-input-streams (function &rest streams)
+ (dolist (stream streams streams)
+ (when-let ((input-stream (stream->input-stream stream)))
+ (when (open-stream-p input-stream)
+ (funcall function input-stream)))))
+
+(defun stream->output-stream (stream)
+ (etypecase stream
+ (synonym-stream (stream->output-stream
+ (symbol-value (synonym-stream-symbol stream))))
+ (two-way-stream (two-way-stream-output-stream stream))
+ (stream (and (output-stream-p stream) stream))))
+
+(defun mapc-open-output-streams (function &rest streams)
+ (dolist (stream streams streams)
+ (when-let ((output-stream (stream->output-stream stream)))
+ (when (open-stream-p output-stream)
+ (funcall function output-stream)))))