2

summary

What to utter from commandline to cause a running clientless Emacs daemon/server process to gracefully shutdown--i.e., clean up after itself, then exit--without creating additional Emacs processes?

Note that by commandline, I mean "a shell outside Emacs," not M-x from within Emacs (hence my question does not duplicate this question), though commandline emacsclient --eval satisfies requirement.

Note regarding potential answers: if your answer requires some interaction, please make it either

  1. (preferred) in the "same commandline." Not sure if that's the correct usage (please correct if not); what I mean is something like

    me@it: ~ $ your --code --here
    Save modified buffers? [Y/n] Y
    Active processes exist: kill them all? [Y/n] Y
    Killing daemon ...
    
  2. (acceptable) in a TUI Emacs in the same shell as the commandline, like (e.g.) emacsclient --tty

details

testcase

Suppose one has a clientless emacs daemon/server process. I know I can kill said process from a (Linux) commandline with (e.g.) kill -9 process# or pkill -9 emacs, but that won't allow the process to gracefully shutdown. What I mean:

Let's start with no emacs* processes:

me@it: ~ $ date ; emacs --version
Sun Oct  8 00:57:18 MST 2017
GNU Emacs 24.4.1
...

FWIW, I'm running this on an up-to-date Debian (currently version=8.9) Linux, hence the somewhat downlevel Emacs. No emacs and no lockfiles (more below):

me@it: ~ $ date ; find ~/.emacs.d/ -name '*lock*'
Sun Oct  8 00:57:37 MST 2017
me@it: ~ $ date ; pgrep -l emacs
Sun Oct  8 00:57:43 MST 2017

Now, start a clientless emacs daemon/server:

me@it: ~ $ date ; emacs --daemon &
Sun Oct  8 01:00:05 MST 2017
...

... == omitted lots console spew from startup of my emacs config (Emacs Prelude + some code in ~/.emacs.d/personal/). Besides that, we can confirm the process still exists with

me@it: ~ $ date ; pgrep -l emacs
Sun Oct  8 01:00:56 MST 2017
29422 emacs

... and, since my config uses desktop, there is now a desktop lockfile (which there was not above):

me@it: ~ $ date ; find ~/.emacs.d/ -name '*lock*'
Sun Oct  8 01:01:01 MST 2017
/home/me/.emacs.d/personal/.emacs.desktop.lock

At this point, I want to tell the daemon/server process to

  1. cleanup (e.g., delete its desktop lockfile), before ...
  2. shutdown (e.g., kill its process) without creating additional Emacs processes (clients or servers)

failures

(kill-emacs) and related

The Emacs manual says

To kill Emacs without being prompted about saving, type M-x kill-emacs.

But when I do ...

me@it: ~ $ date ; emacsclient --eval '(kill-emacs)'
Sun Oct  8 01:02:41 MST 2017
me@it: ~ $ date ; pgrep -l emacs
Sun Oct  8 01:02:46 MST 2017
29422 emacs
me@it: ~ $ date ; find ~/.emacs.d/ -name '*lock*'
Sun Oct  8 01:02:53 MST 2017
/home/me/.emacs.d/personal/.emacs.desktop.lock

... there is no change. The good news is, this answer suggests how to actually kill the daemon ...

me@it: ~ $ date ; emacsclient --eval '(let (kill-emacs-hook) (kill-emacs))'
Sun Oct  8 01:03:22 MST 2017
me@it: ~ $ date ; pgrep -l emacs
Sun Oct  8 01:03:26 MST 2017

... the bad news is, (let (kill-emacs-hook) (kill-emacs)) is not graceful:

me@it: ~ $ date ; find ~/.emacs.d/ -name '*lock*'
Sun Oct  8 01:03:50 MST 2017
/home/me/.emacs.d/personal/.emacs.desktop.lock

... i.e., that code is no better than pkill -9 emacs.

(save-buffers-kill-emacs) and related

izkon suggests using

(defun server-shutdown ()
  "Save buffers, Quit, and Shutdown (kill) server"
  (interactive)
  (save-some-buffers)
  (kill-emacs))

which Basil claims is equal to (save-buffers-kill-emacs). Let's test:

After I kill all my Emacs process and delete all lockfiles, I can add the above code to a file loaded from my init.el, and then do

me@it: ~ $ date ; find ~/.emacs.d/ -name '*lock*'
Sun Oct  8 14:08:24 MST 2017
me@it: ~ $ date ; pgrep -l emacs
Sun Oct  8 14:08:33 MST 2017
me@it: ~ $ date ; emacs --daemon &
Sun Oct  8 14:08:49 MST 2017
... console spew follows ...
me@it: ~ $ date ; pgrep -l emacs
Sun Oct  8 14:09:15 MST 2017
6087 emacs
me@it: ~ $ date ; find ~/.emacs.d/ -name '*lock*'
Sun Oct  8 14:09:25 MST 2017
/home/me/.emacs.d/personal/.emacs.desktop.lock

So I have a running daemon. I then do

me@it: ~ $ date ; emacsclient --eval '(server-shutdown)'
Sun Oct  8 14:10:25 MST 2017
me@it: ~ $ date ; pgrep -l emacs
Sun Oct  8 14:10:31 MST 2017
6087 emacs
me@it: ~ $ date ; find ~/.emacs.d/ -name '*lock*'
Sun Oct  8 14:10:33 MST 2017
/home/me/.emacs.d/personal/.emacs.desktop.lock

I.e., no change. However, my results from (save-buffers-kill-emacs) are somewhat worse:

me@it: ~ $ date ; emacsclient --eval '(save-buffers-kill-emacs)'
Sun Oct  8 14:11:38 MST 2017
^C

I.e., (save-buffers-kill-emacs) hangs (unlike (server-shutdown)) at commandline until killed with C-c. (This appears to be due to a currently-open Emacs bug from 2013, as noted by npostavs.) Furthermore, like (server-shutdown) above, it does nothing about the daemon process:

me@it: ~ $ date ; pgrep -l emacs
Sun Oct  8 14:12:12 MST 2017
6087 emacs
me@it: ~ $ date ; find ~/.emacs.d/ -name '*lock*'
Sun Oct  8 14:12:37 MST 2017
/home/me/.emacs.d/personal/.emacs.desktop.lock

(save-buffers-kill-emacs) can be made not to hang (as suggested by Basil), but it still does not work (i.e., gracefully kill the daemon):

me@it:~$ date ; pgrep -l emacs
Mon Oct  9 22:50:39 MST 2017
me@it:~$ date ; find ~/.emacs.d/ -name '*lock*'
Mon Oct  9 22:50:42 MST 2017
me@it:~$ date ; emacs --daemon &
Mon Oct  9 22:51:01 MST 2017
... console spew follows ...
me@it:~$ date ; pgrep -l emacs
Mon Oct  9 22:51:20 MST 2017
8378 emacs
me@it:~$ date ; find ~/.emacs.d/ -name '*lock*'
Mon Oct  9 22:51:26 MST 2017
/home/me/.emacs.d/personal/.emacs.desktop.lock
me@it:~$ date ; emacsclient --eval '(save-buffers-kill-emacs)' --tty
Mon Oct  9 22:51:51 MST 2017

Key y at the prompt (IIRC, text is This Emacs session has clients; exit anyway?) and the terminal client exits immediately. But it does not kill the server:

me@it:~$ date ; pgrep -l emacs
Mon Oct  9 22:51:59 MST 2017
8378 emacs
me@it:~$ date ; find ~/.emacs.d/ -name '*lock*'
Mon Oct  9 22:52:05 MST 2017
/home/me/.emacs.d/personal/.emacs.desktop.lock
TomRoche
  • 592
  • 3
  • 20
  • I once had the same question, but gave up to find a general answer. Any code running from `kill-emacs-hook` might query for input and thus halt the shutdown process. – politza Oct 08 '17 at 14:06
  • Re hanging, this is an open problem in Emacs, cf [Bug#13697](https://debbugs.gnu.org/cgi/bugreport.cgi?bug=13697). – npostavs Oct 09 '17 at 14:44
  • @npostavs: thanks, now noted in section=`(save-buffers-kill-emacs) and related` – TomRoche Oct 11 '17 at 02:49

1 Answers1

-2

The first function in my ~/.emacs.d/init.el is one I got from here:

(defun server-shutdown () "Save buffers, Quit, and Shutdown (kill) server" (interactive) (save-some-buffers) (kill-emacs))

After that, I can emacsclient -t -e '(server-shutdown)'

izkon
  • 1,798
  • 10
  • 23