Subject: bug#68799: 30.0.50; emacs --fg-daemon fails silently if server-start fails

From: Spencer Baugh <[email-masked]>
Date: Tue, 13 Feb 2024 12:37:30 -0500

Eli Zaretskii <[email-masked]> writes:

>> Cc: Stefan Monnier <[email-masked]>, Jason Rumney <[email-masked]>
>> From: Spencer Baugh <[email-masked]>
>> Date: Mon, 12 Feb 2024 17:10:34 -0500
>> 
>> Spencer Baugh <[email-masked]> writes:
>> > 1. emacs -Q --fg-daemon=/nonexistent/dir/sock
>> > 2. Emacs prints "Starting Emacs daemon." and sits in foreground.
>> > 3. emacsclient -c -s /nonexistent/dir/sock
>> > 4. emacsclient prints and exits:
>> > emacsclient: can't find socket; have you started the server?
>> > emacsclient: To start the server in Emacs, type "M-x server-start".
>> > emacsclient: error accessing socket "/nonexistent/dir/sock"
>> >
>> > This is because in step 1, the server actually failed to start, but
>> > Emacs did not log that at all.  In fact, it's impossible to access the
>> > Emacs started in 1 now, since it's not actually running a server and it
>> > has no frames.
>> 
>> Okay, I found what one might call the root cause, the following code and
>> comment.  Adding Stefan and Jason to CC as the original authors.
>> 
>> /* The initial frame is a special non-displaying frame. It
>>    will be current in daemon mode when there are no frames
>>    to display, and in non-daemon mode before the real frame
>>    has finished initializing.  If an error is thrown in the
>>    latter case while creating the frame, then the frame
>>    will never be displayed, so the safest thing to do is
>>    write to stderr and quit.  In daemon mode, there are
>>    many other potential errors that do not prevent frames
>>    from being created, so continuing as normal is better in
>>    that case.  */
>> || (!IS_DAEMON && FRAME_INITIAL_P (sf))
>> 
>> The comment is mostly sensible: we should exit while initializing, and
>> shouldn't exit while in the steady state of daemon mode.
>
> Bug#1310 and bug#1836 are also relevant here.

Indeed.

>> However, it doesn't handle the case when Emacs is initializing *and* in
>> daemon mode.  In that case, an error will prevent frames from being
>> created, just like in non-daemon mode.
>> 
>> So this check really wants to be something more like:
>> || ( IS_DAEMON && [something to check if Emacs is starting up])
>> || (!IS_DAEMON && FRAME_INITIAL_P (sf))
>> 
>> Not sure what [something to check if Emacs is starting up] should be
>> though.
>
> after-init-time, I guess.  But note that this still leaves a window
> between where that is set non-nil and starting the server.

Oh, actually there is a DAEMON_RUNNING define which is exactly what we
want here.  DAEMON_RUNNING just didn't exist at the time the check on
IS_DAEMON was added.  Patch using DAEMON_RUNNING attached.

> Also, the patch for server-start which prevents it from erroring out
> is still needed, because we do want to show the error message about
> being unable to start the daemon.
>
> And this solution will still print error messages that are not
> specific enough to be helpful.  E.g., what do you do if you try
> starting the daemon and see
>
>    wrong-type-argument, stringp, nil
>
> and that's all?  And that is even before we consider the use case
> where stderr of the daemon is redirected to the great void, which
> happens in some cases.
>
> So my vote is still for diagnosing the important places where a fatal
> error could happen, and adding a clear diagnostic there.  Yes, it is
> more work.  But the gain will be much higher.

Agreed, I will also work on that as a separate change.

>From a24a2b1ceb12f11c9d345190fbf554f27c4ec186 Mon Sep 17 00:00:00 2001
From: Spencer Baugh <[email-masked]>
Date: Tue, 13 Feb 2024 12:20:39 -0500
Subject: [PATCH] Check daemon is initialized before supressing errors

Previously, the default error handler would correctly suppress
unhandled errors raised when IS_DAEMON and the initial frame was
current, since this is the normal state of operation for a daemon-mode
Emacs.  However, this also incorrectly suppressed errors raised while
a daemon-mode Emacs was starting up.

Now, errors raised while a daemon-mode Emacs is starting up will be
handled just like errors when a non-daemon Emacs is starting up.

This was previously the case before changes for bug#1310 and bug#1836,
which added the suppression of errors when IS_DAEMON.  DAEMON_RUNNING
didn't exist at the time of those changes, but now it does, so we can
do better.

* src/keyboard.c (Fcommand_error_default_function): Check
!DAEMON_RUNNING in addition to IS_DAEMON. (bug#68799)
* src/lisp.h (DAEMON_RUNNING): Add a clarifying comment about what
this define means.
---
 src/keyboard.c | 5 +++--
 src/lisp.h     | 1 +
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/keyboard.c b/src/keyboard.c
index ff89b1b3510..5c00996ea6d 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -1076,8 +1076,9 @@ DEFUN ("command-error-default-function", Fcommand_error_default_function,
 	     write to stderr and quit.  In daemon mode, there are
 	     many other potential errors that do not prevent frames
 	     from being created, so continuing as normal is better in
-	     that case.  */
-	  || (!IS_DAEMON && FRAME_INITIAL_P (sf))
+	     that case, as long as the daemon has actually finished
+	     initialization. */
+	  || (!(IS_DAEMON && !DAEMON_RUNNING) && FRAME_INITIAL_P (sf))
 	  || noninteractive))
     {
       print_error_message (data, Qexternal_debugging_output,
diff --git a/src/lisp.h b/src/lisp.h
index 5326824bf38..176ca24b318 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -5038,6 +5038,7 @@ fast_c_string_match_ignore_case (Lisp_Object regexp,
 /* 0 not a daemon, 1 foreground daemon, 2 background daemon.  */
 extern int daemon_type;
 #define IS_DAEMON (daemon_type != 0)
+/* True means daemon-initialized has not yet been called. */
 #define DAEMON_RUNNING (daemon_type >= 0)
 #else  /* WINDOWSNT */
 extern void *w32_daemon_event;
-- 
2.39.3