Subject: bug#44824: [PATCH] org.el: Avoid xdg-open silent failure

From: Maxim Nikulin <[email-masked]>
Date: Sat, 20 Mar 2021 22:45:16 +0700

On 19/03/2021 10:50, Kyle Meyer wrote:
> Maxim Nikulin writes:
> A few comments in addition to Eli's advice to drop the
> (eq system-type 'gnu/linux) condition...

Feel free to commit the change suggested in
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=44824#82
instead of this patch.

>> +(defun org--error-process-sentinel (proc event)
>> +  "Show a message if process failed (exited with non-zero code
>> +or killed by a signal.  Pass the function as :SENTINEL argument
> 
> Please rework the first sentence so that it fits on the first line,
> though I'd be in favor dropping the function and using a lambda in the
> make-process call.

My impression is that org-open-file function is already too long and 
complex. Another reason to use standalone function is that I am unsure 
if elisp compiler and interpreter are smart enough to reuse single 
instance of lambda. I was afraid that every opened file caused creation 
of new sentinel possibly with a closure containing chain of stack 
frames. On the other hand even in worst case memory footprint is 
negligible in comparison to any GUI viewer.

>> +  (unless (string-match "finished" event)
> 
> There's no need for substring matching, right?  So it could be
> 
>    (equal event "finished\n")

I was surprised by final "\n" that is not always suitable and I was in 
doubts concerning its stability. I would prefer something like

     (starts-with event "finished")

Certainly match-data is not necessary, so even match-string-p is better.

>    (when (and (memq (process-status proc) '(exit signal))
>               (/= (process-exit-status proc) 0))

Thank you, I was too lazy to implement such kind of check myself. 
Certainly this variant is better.

I hope, I have addressed other your comments in the updated patch.

commit 5eca7764d94dd46b9f9a7792d1b786a3f03b20b6
Author: Max Nikulin <[email-masked]>
Date:   Wed Feb 17 16:35:58 2021 +0000

    org.el: Avoid xdg-open silent failure
    
    * lisp/org.el (org-open-file): Use 'pipe :connection-type instead of
    'pty to prevent killing of background process on handler exit.
    (Bug#44824)
    
    Problem happens only in some desktop environments where configured
    through `org-file-apps' or mailcap handlers launches actual viewer
    (as defined in .desktop files and obtained from mimeapps.list)
    in background.  E.g. xdg-open invokes "gio open" or kde-open5 for Gnome
    or KDE accordingly and these handlers launches e.g. eog or okular in
    background.  As soon as main process exits, temporary terminal session
    created by `start-process-shell-command' is terminated.  As a result
    background processes receive SIGHUP.
    
    Previously command were executed with no buffer, so the change
    does not affect "needsterminal" and "copiousoutput" mailcap features,
    they are not supported as earlier.
    
    If handler main process fails then show a message with exit reason.
    Output (including error messages) is ignored as before.
    Gtk application tends to report significant amount of failed asserts
    hardly informative for majority of users.

diff --git a/lisp/org.el b/lisp/org.el
index 4db2dbe04..c58708a5f 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -8768,7 +8768,21 @@ If the file does not exist, throw an error."
 
       (save-window-excursion
 	(message "Running %s...done" cmd)
-	(start-process-shell-command cmd nil cmd)
+        ;; Handlers such as "gio open" and kde-open5 start viewer in background
+        ;; and exit immediately.  Avoid `start-process' since it assumes
+        ;; :connection-type 'pty and kills children processes with SIGHUP
+        ;; when temporary terminal session is finished.
+        (make-process
+         :name "org-open-file" :connection-type 'pipe :noquery t
+         :buffer nil ; use "*Messages*" for debugging
+         :sentinel (lambda (proc event)
+                     (when (and (memq (process-status proc) '(exit signal))
+                                (/= (process-exit-status proc) 0))
+                       (message
+                        "Command %s: %s."
+                        (mapconcat #'identity (process-command proc) " ")
+                        (substring event 0 -1))))
+         :command (list shell-file-name shell-command-switch cmd))
 	(and (boundp 'org-wait) (numberp org-wait) (sit-for org-wait))))
      ((or (stringp cmd)
 	  (eq cmd 'emacs))