bug#62044: 30.0.50; ERC 5.5: Auto-reconnect is broken
"J.P." <[email protected]> Mon, 23 Feb 2026 23:49:32 -0800
| Newsgroups | gmane.emacs.bugs,gmane.emacs.erc.general |
|---|---|
| Message-ID | <[email protected]> |
--=-=-= Content-Type: text/plain Even with the relatively recent addition of various probing strategies, it seems some setups may still have problems reconnecting reliably. Another approach similar to `erc-server-delayed-check-reconnect' was bandied about around the time that variant was added. The idea was to let users provide some external subprocess command to determine whether the server is truly ready. The attached PoC tries to do that by offering a simple wrapper that keeps trying the external command and, on success, defers to a proper `erc-server-reconnect-function'. If it or some improved version proves useful to anyone, we can include it in 5.7. BTW, the names of the various function values for the option `erc-server-reconnect-function' are quite similar and easily confused with one another. If anyone has better names, please suggest. Thanks. P.S. The first patch is a minor fixup and unrelated. --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-5.7-Make-reconnect-detection-more-readable-in-erc-op.patch From 145836df52e5854575cee1510e72789d8d46da3c Mon Sep 17 00:00:00 2001 From: "F. Jason Park" <[email protected]> Date: Tue, 11 Feb 2025 22:02:16 -0800 Subject: [PATCH 1/2] [5.7] Make reconnect detection more readable in erc-open * lisp/erc/erc.el (erc-open): Bind `erc--server-reconnecting' instead of relying on confusing single-use variable. --- lisp/erc/erc.el | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el index 572b73188e3..0770508d236 100644 --- a/lisp/erc/erc.el +++ b/lisp/erc/erc.el @@ -2610,13 +2610,18 @@ erc-open (old-recon-count erc-server-reconnect-count) (old-point nil) (delayed-modules nil) - (continued-session (or erc--server-reconnecting - erc--target-priors - (and-let* (((not target)) - (m (buffer-local-value - 'erc-input-marker buffer)) - ((marker-position m))) - (buffer-local-variables buffer))))) + (erc--server-reconnecting + (or erc--server-reconnecting + ;; Interpret an entry-point invocation reassociated with + ;; an existing session via explicit ID as an "implied" + ;; reconnection, but only if it at least tried to connect. + (and-let* ((id) (connect) ; `target' must be null + (m (buffer-local-value 'erc-input-marker buffer)) + ((marker-position m)) + ((buffer-local-value 'erc-server-process buffer)) + (netid (buffer-local-value 'erc-networks--id buffer))) + (cl-assert (string-equal (erc-networks--id-given netid) id)) + (buffer-local-variables buffer))))) (when connect (run-hook-with-args 'erc-before-connect server port nick)) (set-buffer buffer) (setq old-point (point)) @@ -2681,7 +2686,8 @@ erc-open (when erc-log-p (get-buffer-create (concat "*ERC-DEBUG: " server "*")))) - (erc--initialize-markers old-point continued-session) + (erc--initialize-markers old-point (or erc--server-reconnecting + erc--target-priors)) (erc-determine-parameters server port nick full-name user passwd) (save-excursion (run-mode-hooks) (dolist (mod (car delayed-modules)) -- 2.53.0 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0002-POC-Add-external-variant-for-erc-server-reconnect-fu.patch From 0ed3c9df94b59dd2b44f4caccb044857d360a1e0 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" <[email protected]> Date: Tue, 8 Apr 2025 18:00:08 -0700 Subject: [PATCH 2/2] [POC] Add external variant for erc-server-reconnect-function * lisp/erc/erc-backend.el (erc-server-reconnect-function): Add `erc-server-external-check-reconnect'. (erc-server-external-check-reconnect-command): New variable. (erc-server-external-check-reconnect-function): New variable. (erc-server-external-check-reconnect): New function. * test/lisp/erc/erc-scenarios-base-auto-recon.el (erc-scenarios-base-auto-recon-check/external): New function. (Bug#62044) --- lisp/erc/erc-backend.el | 39 +++++++++++ .../lisp/erc/erc-scenarios-base-auto-recon.el | 64 +++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/lisp/erc/erc-backend.el b/lisp/erc/erc-backend.el index 819b8ae5033..4943bab75bd 100644 --- a/lisp/erc/erc-backend.el +++ b/lisp/erc/erc-backend.el @@ -438,6 +438,7 @@ erc-server-reconnect-function :package-version '(ERC . "5.6.1") :type '(choice (function-item erc-server-delayed-reconnect) (function-item erc-server-delayed-check-reconnect) + (function-item erc-server-external-check-reconnect) (function-item erc-server-prefer-check-reconnect) function)) @@ -956,6 +957,44 @@ erc-server-delayed-check-reconnect ;; C-g during blocking connect, like with the SOCKS connector. (quit (erc--cancel-auto-reconnect-timer)))))) +(defvar erc-server-external-check-reconnect-command + '("nc" "-z" "10.0.0.1" "6670") + "External command to run successfully before attempting to reconnect. +Only effective if `erc-server-reconnect-function' is set to +`erc-server-external-check-reconnect'. Possible values might include: +\"ping -c 1 10.0.0.1\" or \"openssl s_client -cert /tmp/client.pem +10.0.0.1:6670\" split into lists, of course.") + +(defvar erc-server-external-check-reconnect-function + #'erc-server-prefer-check-reconnect + "Function `erc-server-external-check-reconnect' calls to reconnect. +Presumably another valid `erc-server-reconnect-function'.") + +(defun erc-server-external-check-reconnect (buffer) + "Gate reconnect attempt on the success of some external process. +If successful, call `erc-server-external-check-reconnect-function' with +BUFFER. Otherwise, call `erc-schedule-reconnect'." + (require 'net-utils) + (cl-assert (not (eq erc-server-external-check-reconnect-function + erc-server-reconnect-function))) + (let* ((command erc-server-external-check-reconnect-command) + (name (car command)) + (display-buffer-alist '((t always))) + (buf (and + (fboundp 'net-utils-run-program) ; silence compiler + (net-utils-run-program (concat "ERC " name) + (string-join command " ") + name (cdr command)))) + (proc (get-buffer-process buf))) + (set-process-sentinel + proc + (lambda (proc event) + (when (string-match (rx (| "finished" "exit" "failed")) event) + (if (zerop (process-exit-status proc)) + (run-at-time 0 nil erc-server-external-check-reconnect-function + buffer) + (erc-schedule-reconnect buffer 0))))))) + (defun erc-server-prefer-check-reconnect (buffer) "Defer to another reconnector based on BUFFER's `erc-session-connector'. Prefer `erc-server-delayed-check-reconnect' if the connector is known to diff --git a/test/lisp/erc/erc-scenarios-base-auto-recon.el b/test/lisp/erc/erc-scenarios-base-auto-recon.el index 4694f426dfd..782ac8bb852 100644 --- a/test/lisp/erc/erc-scenarios-base-auto-recon.el +++ b/test/lisp/erc/erc-scenarios-base-auto-recon.el @@ -140,4 +140,68 @@ erc-scenarios-base-auto-recon-check/reuse (erc-cmd-RECONNECT "cancel") (funcall expect 10 "canceled"))))) +;; FIXME reuse code from tests above. +(ert-deftest erc-scenarios-base-auto-recon-check/external () + :tags '(:expensive-test :unstable) + (unless (executable-find "ncat") + (ert-skip "Requires ncat")) + + (erc-scenarios-common-with-cleanup + ((erc-server-flood-penalty 0.1) + (port (erc-scenarios-base-auto-recon--get-unused-port)) + (erc--server-reconnect-timeout-scale-function (lambda (_) 1)) + (erc-server-auto-reconnect t) + (erc-server-reconnect-function #'erc-server-external-check-reconnect) + (erc-server-external-check-reconnect-command + (list "nc" "-z" "127.0.0.1" "18000")) + (expect (erc-d-t-make-expecter)) + (erc-scenarios-common-dialog "base/reconnect") + (erc-server-delayed-check-reconnect-reuse-process-p nil) + (dumb-server nil)) + + (ert-info ("Dialing fails: nobody home") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :full-name "tester") + (erc-d-t-wait-for 10 (not (erc-server-process-alive))) + (erc-d-t-wait-for 10 erc--server-reconnect-timer) + (funcall expect 10 "Opening connection") + (funcall expect 10 "failed") + + ;; Start the listener to fulfill nc command. + (set-process-query-on-exit-flag + (start-process "ERC ncat" (get-buffer-create "*ERC external-recon*") + "ncat" "-k" "-l" "127.0.0.1" "18000") + nil))) + + (ert-info ("Service appears") + (setq dumb-server (erc-d-run "localhost" port + 'just-eof 'unexpected-disconnect)) + (with-current-buffer (format "127.0.0.1:%d" port) + (funcall expect 10 "server is in debug mode") + (should (equal (buffer-name) "FooNet")))) + + (ert-info ("Service interrupted, reconnect starts again") + (with-current-buffer "FooNet" + (funcall expect 10 "failed") + (funcall expect 10 '(: "reconnecting" (+ nonl) "attempt 1/2")))) + + (ert-info ("Service restored") + (delete-process dumb-server) + (setq dumb-server (erc-d-run "localhost" port + 'just-eof 'unexpected-disconnect)) + (with-current-buffer "FooNet" + (funcall expect 10 "server is in debug mode"))) + + (ert-info ("Service interrupted a third time, reconnect starts yet again") + (with-current-buffer "FooNet" + (funcall expect 10 "failed") + (funcall expect 10 '(: "reconnecting" (+ nonl) "attempt 1/2")) + (erc-cmd-RECONNECT "cancel") + (funcall expect 10 "canceled")))) + + (when noninteractive + (mapc #'kill-buffer (match-buffers (rx "*ERC "))))) + ;;; erc-scenarios-base-auto-recon.el ends here -- 2.53.0 --=-=-=--