Actually, checking the elisp reference manual provided some more
insight:

       If a connection from a process contains buffered data,
    ‘accept-process-output’ can return non-‘nil’ even after the process has
    exited.  Therefore, although the following loop:
    
         ;; This loop contains a bug.
         (while (process-live-p process)
           (accept-process-output process))
    
    will often read all output from PROCESS, it has a race condition and can
    miss some output if ‘process-live-p’ returns ‘nil’ while the connection
    still contains data.  Better is to write the loop like this:
    
         (while (accept-process-output process))

So instead of:

>   (let ((output nil)
>         (slept-for 0)
>         (process (apply #'password-store--run-1
>                         (lambda (password)
>                           (setq output password))
>                         (delq nil args))))
>     (accept-process-output process .1 nil t)
>     (while (process-live-p process)
>       (accept-process-output process .1 nil t))
>     output))

it should probably better be:

  (let ((output nil)
        (slept-for 0)
        (process (apply #'password-store--run-1
                        (lambda (password)
                          (setq output password))
                        (delq nil args))))
    ;; wait for output or process termination (max. wait time: 5s)
    (while (accept-process-output process 5.0))
    output))


I added a timeout which terminates `accept-process-output' after 5s just
to make sure that a non-responsive pass process doesn't block emacs.
It's debatable if this is needed or even makes any sense.

An updated patch is attached.

Best Regards,
Kai

>From e5c00971f4907ae077be41bf8a28ce41e8992c52 Mon Sep 17 00:00:00 2001
From: Kai Tetzlaff <[email protected]>
Date: Sun, 27 Feb 2022 08:55:52 +0100
Subject: [PATCH] Fix infloop in password-store--run

For certain commands (like password-store--run-generate,
password-store--run-remove) with password entries in sub-directories,
password-store--run would infloop. The output variable never received any
actual output. This seems to be a timing issue as e.g. stepping through the
code in edebug never caused the issue.

This commit changes the check for the finished pass process and also inserts
calls to accept-process-output in order to guarantee that output really gets
processed while the loop is running.
---
 contrib/emacs/password-store.el | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/contrib/emacs/password-store.el b/contrib/emacs/password-store.el
index 61c339e..1515e0e 100644
--- a/contrib/emacs/password-store.el
+++ b/contrib/emacs/password-store.el
@@ -99,12 +99,13 @@ or outputs error message on failure."
 Nil arguments are ignored.  Returns the output on success, or
 outputs error message on failure."
   (let ((output nil)
-        (slept-for 0))
-    (apply #'password-store--run-1 (lambda (password)
-                                     (setq output password))
-           (delq nil args))
-    (while (not output)
-      (sleep-for .1))
+        (slept-for 0)
+        (process (apply #'password-store--run-1
+                        (lambda (password)
+                          (setq output password))
+                        (delq nil args))))
+    ;; wait for output or process termination (max. wait time: 5s)
+    (while (accept-process-output process 5.0))
     output))
 
 (defun password-store--run-async (&rest args)
-- 
2.34.1

Reply via email to