fix: daemon port conflict handling, multi-port TUI connect
- start-daemon: handle ADDRESS-IN-USE-ERROR by trying ports 9105-9115 instead of crashing. Logs which port is used. - Add *daemon-port* defvar to track actual listening port - main: wrap start-daemon in handler-case so the daemon doesn't crash if all ports are in use - connect-daemon (TUI): try ports 9105-9115 with 2s timeout each instead of retrying the same port 3 times - Add debug messages for connection success and disconnection timestamp
This commit is contained in:
@@ -756,35 +756,31 @@ supplied (e.g. \"/\"), pre-fill the select filter with it."
|
||||
|
||||
** Connection
|
||||
#+BEGIN_SRC lisp :tangle /home/user/.local/share/passepartout/lisp/channel-tui-main.lisp
|
||||
(defun connect-daemon (&optional (host "127.0.0.1") (port 9105))
|
||||
(add-msg :system "* Connecting to daemon... *")
|
||||
(loop for attempt from 1 to 3
|
||||
for backoff = 0 then 3
|
||||
do (sleep backoff)
|
||||
(handler-case
|
||||
(let ((s (usocket:socket-connect host port :timeout 5)))
|
||||
(defun connect-daemon (&optional (host "127.0.0.1") (start-port 9105) (end-port 9115))
|
||||
"Connect to daemon, trying ports START-PORT to END-PORT."
|
||||
(add-msg :system (format nil "* Connecting to daemon... *"))
|
||||
(loop for port from start-port to end-port
|
||||
do (handler-case
|
||||
(let ((s (usocket:socket-connect host port :timeout 2)))
|
||||
(setf (st :stream) (usocket:socket-stream s)
|
||||
(st :connected) t)
|
||||
(add-msg :system (format nil "* Connected to daemon on port ~d *" port))
|
||||
(bt:make-thread (lambda () (reader-loop (st :stream)))
|
||||
:name "tui-reader")
|
||||
(return-from connect-daemon t))
|
||||
(usocket:connection-refused-error (c)
|
||||
(declare (ignore c))
|
||||
(when (= attempt 3)
|
||||
(add-msg :system (format nil "* No daemon on port ~a after ~a attempts *"
|
||||
port attempt))))
|
||||
(usocket:connection-refused-error ()
|
||||
(when (= port end-port)
|
||||
(add-msg :system (format nil "* No daemon on ports ~d-~d *" start-port end-port))))
|
||||
(error (c)
|
||||
(add-msg :system (format nil "* Connection attempt ~a failed: ~a *"
|
||||
attempt c))
|
||||
(when (= attempt 3)
|
||||
(add-msg :system "* TIP: run 'passepartout daemon' first *")))))
|
||||
(when (= port end-port)
|
||||
(add-msg :system (format nil "* No daemon on ports ~d-~d (error: ~a) *" start-port end-port c))))))
|
||||
nil)
|
||||
|
||||
(defun disconnect-daemon ()
|
||||
(when (st :stream)
|
||||
(ignore-errors (close (st :stream)))
|
||||
(setf (st :stream) nil (st :connected) nil)
|
||||
(add-msg :system "* Disconnected *")))
|
||||
(add-msg :system (format nil "* Disconnected [now=~a] *" (now))))
|
||||
#+END_SRC
|
||||
|
||||
** Main Loop
|
||||
|
||||
@@ -367,7 +367,10 @@ Boot sequence:
|
||||
|
||||
(when (fboundp 'events-start-heartbeat)
|
||||
(events-start-heartbeat))
|
||||
(start-daemon)
|
||||
(handler-case (start-daemon)
|
||||
(error (c)
|
||||
(log-message "DAEMON: Failed to start — ~a" c)
|
||||
(format *error-output* "~&DAEMON: Failed to start — ~a~%" c)))
|
||||
|
||||
#+sbcl
|
||||
(sb-sys:enable-interrupt sb-unix:sigint
|
||||
|
||||
@@ -147,6 +147,7 @@ The daemon sends a handshake message on connection, then enters a read loop, inj
|
||||
|
||||
#+begin_src lisp
|
||||
(defvar *daemon-socket* nil)
|
||||
(defvar *daemon-port* nil "The port the daemon is actually listening on (may differ from default if 9105 was in use).")
|
||||
|
||||
(defun client-handle-connection (socket)
|
||||
"Handles a single TUI/CLI client connection in a dedicated thread."
|
||||
@@ -174,18 +175,30 @@ The daemon sends a handshake message on connection, then enters a read loop, inj
|
||||
(error (c) (log-message "CLIENT ERROR: ~a" c)))
|
||||
(ignore-errors (usocket:socket-close socket))))
|
||||
|
||||
(defun start-daemon (&key (port 9105))
|
||||
"Starts the network listener for TUI/CLI clients."
|
||||
(setf *daemon-socket* (usocket:socket-listen "127.0.0.1" port :reuse-address t))
|
||||
(log-message "DAEMON: Listening on localhost:~a" port)
|
||||
(bt:make-thread
|
||||
(lambda ()
|
||||
(loop
|
||||
(let ((client-socket (usocket:socket-accept *daemon-socket*)))
|
||||
(when client-socket
|
||||
(bt:make-thread (lambda () (client-handle-connection client-socket))
|
||||
:name "passepartout-client-handler")))))
|
||||
:name "passepartout-server-listener"))
|
||||
(defun start-daemon (&key (port 9105) (max-retries 10))
|
||||
"Starts the network listener for TUI/CLI clients.
|
||||
If PORT is taken, tries subsequent ports up to PORT+MAX-RETRIES."
|
||||
(loop for attempt from 0 below max-retries
|
||||
for p = (+ port attempt)
|
||||
do (handler-case
|
||||
(progn
|
||||
(setf *daemon-socket* (usocket:socket-listen "127.0.0.1" p :reuse-address t))
|
||||
(log-message "DAEMON: Listening on localhost:~a" p)
|
||||
(setf *daemon-port* p)
|
||||
(bt:make-thread
|
||||
(lambda ()
|
||||
(loop
|
||||
(let ((client-socket (usocket:socket-accept *daemon-socket*)))
|
||||
(when client-socket
|
||||
(bt:make-thread (lambda () (client-handle-connection client-socket))
|
||||
:name "passepartout-client-handler")))))
|
||||
:name "passepartout-server-listener")
|
||||
(return p))
|
||||
(usocket:address-in-use-error ()
|
||||
(when (= attempt (1- max-retries))
|
||||
(log-message "DAEMON: All ports ~d-~d in use — giving up" port (+ port max-retries -1))
|
||||
(error "No available port for daemon"))
|
||||
(log-message "DAEMON: Port ~d in use, trying ~d..." p (1+ p))))))
|
||||
#+end_src
|
||||
|
||||
** Handshake Logic
|
||||
|
||||
Reference in New Issue
Block a user