[PATCH] password-store.el: Supprt asynchronous pass operations which return output.

Ian Eure ian at retrospec.tv
Fri Jan 11 02:26:35 CET 2019


When using EXWM, if PASSWORD-STORE-GET is called and a pinentry
program needs to be executed, Emacs deadlocks.  This happens 
becuase
Emacs blocks waiting for output from gpg(1), which is blocked 
waiting
for output from the pinentry program, which is blocked waiting for
Emacs to manage its window.

This updates PASSWORD-STORE-COPY to work asynchronously.  This 
should
be fine, since its primary purpose is side-effecting, and it 
doesn’t
matter when its evaluation completes.

A new PASSWORD-STORE--RUN-1 function has been added for general 
cases
of async pass(1) commands where the output is needed.  While there 
is
an existing PASSWORD-STORE--RUN-ASYNC, it discards output -- it’s 
only
used for `pass edit', where it’s not needed.  The body of
PASSWORD-STORE--RUN has been replaced with one that uses the async
method, and a wait loop which blocks until it’s complete.

Supporting all this necessitated moving password-store.el to 
lexical
binding and dropping Emacs 24 support.  The latter requirement 
could
be worked around if there are concerns about it.

I’m not sure what the release policy is for this, so I haven’t
incremented the version.

re https://github.com/NicolasPetton/pass#25

diff --git a/contrib/emacs/password-store.el 
b/contrib/emacs/password-store.el
index 10d4f30..abdf754 100644
--- a/contrib/emacs/password-store.el
+++ b/contrib/emacs/password-store.el
@@ -1,11 +1,11 @@
-;;; password-store.el --- Password store (pass) support
+;;; password-store.el --- Password store (pass) support  -*- 
lexical-binding: t; -*-

 ;; Copyright (C) 2014-2018 Svend Sorensen <svend at svends.net>

 ;; Author: Svend Sorensen <svend at svends.net>
 ;; Version: 1.0.2
 ;; URL: https://www.passwordstore.org/
-;; Package-Requires: ((emacs "24") (f "0.11.0") (s "1.9.0") 
    (with-editor "2.5.11"))
+;; Package-Requires: ((emacs "25") (f "0.11.0") (s "1.9.0") 
(with-editor "2.5.11"))
 ;; Keywords: tools pass password password-store

 ;; This file is not part of GNU Emacs.
@@ -59,30 +59,44 @@
       (string-to-number (getenv "PASSWORD_STORE_CLIP_TIME"))
     45))

+(defun password-store--run-1 (callback &rest args)
+  "Run pass with ARGS.
+
+Nil arguments are ignored.  Calls CALLBACK with the output on 
success,
+or outputs error message on failure."
+  (let ((output ""))
+    (make-process
+     :name "password-store-gpg"
+     :command (cons password-store-executable args)
+     :connection-type 'pipe
+     :noquery t
+     :filter (lambda (process text)
+               (setq output (concat output text)))
+     :sentinel (lambda (process state)
+                 (cond
+                  ((string= state "finished\n")
+                   (funcall callback output))
+                  ((string= state "open\n") 
(accept-process-output process))
+                  (t (error (concat "password-store: " 
state))))))))
+
 (defun password-store--run (&rest args)
   "Run pass with ARGS.

 Nil arguments are ignored.  Returns the output on success, or
 outputs error message on failure."
-  (with-temp-buffer
-    (let* ((tempfile (make-temp-file ""))
-           (exit-code
-            (apply 'call-process
-                   (append
-                    (list password-store-executable nil (list t 
                     tempfile) nil)
-                    (delq nil args)))))
-      (unless (zerop exit-code)
-        (erase-buffer)
-        (insert-file-contents tempfile))
-      (delete-file tempfile)
-      (if (zerop exit-code)
-          (s-chomp (buffer-string))
-        (error (s-chomp (buffer-string)))))))
+  (let ((output nil)
+        (slept-for 0))
+    (apply #'password-store--run-1 (lambda (password)
+                                     (setq output password))
+           args)
+    (while (not output)
+      (sleep-for .1))
+    output))

 (defun password-store--run-async (&rest args)
   "Run pass asynchronously with ARGS.

-Nil arguments are ignored."
+Nil arguments are ignored.  Output is discarded."
   (let ((args (mapcar #'shell-quote-argument args)))
     (with-editor-async-shell-command
      (mapconcat 'identity
@@ -103,9 +117,10 @@ Nil arguments are ignored."
 (defun password-store--run-find (&optional string)
   (error "Not implemented"))

-(defun password-store--run-show (entry)
-  (password-store--run "show"
-                       entry))
+(defun password-store--run-show (entry &optional callback)
+  (if callback
+      (password-store--run-1 callback "show" entry)
+    (password-store--run "show" entry)))

 (defun password-store--run-insert (entry password &optional 
 force)
   (error "Not implemented"))
@@ -181,11 +196,17 @@ Nil arguments are ignored."
   (password-store--run-edit entry))

 ;;;###autoload
-(defun password-store-get (entry)
+(defun password-store-get (entry &optional callback)
   "Return password for ENTRY.

-Returns the first line of the password data."
-  (car (s-lines (password-store--run-show entry))))
+Returns the first line of the password data.
+When CALLBACK is non-`NIL', call CALLBACK with the first line 
instead."
+  (if callback
+      (password-store--run-show
+       entry
+       (lambda (password)
+         (funcall callback (car (s-lines password)))))
+    (car (s-lines (password-store--run-show entry)))))

 ;;;###autoload
 (defun password-store-clear ()
@@ -207,13 +228,15 @@ Clear previous password from kill ring. 
Pointer to kill ring is
 stored in `password-store-kill-ring-pointer'.  Password is 
 cleared
 after `password-store-timeout' seconds."
   (interactive (list (password-store--completing-read)))
-  (let ((password (password-store-get entry)))
-    (password-store-clear)
-    (kill-new password)
-    (setq password-store-kill-ring-pointer 
     kill-ring-yank-pointer)
-    (message "Copied %s to the kill ring. Will clear in %s 
     seconds." entry (password-store-timeout))
-    (setq password-store-timeout-timer
-          (run-at-time (password-store-timeout) nil 
           'password-store-clear))))
+  (password-store-get
+   entry
+   (lambda (password)
+     (password-store-clear)
+     (kill-new password)
+     (setq password-store-kill-ring-pointer 
kill-ring-yank-pointer)
+     (message "Copied %s to the kill ring. Will clear in %s 
seconds." entry (password-store-timeout))
+     (setq password-store-timeout-timer
+           (run-at-time (password-store-timeout) nil 
'password-store-clear)))))

 ;;;###autoload
 (defun password-store-init (gpg-id)


More information about the Password-Store mailing list