Solved Rc.d daemon as user (emacs)

Emacs can be run as a daemon using a flag: emacs --daemon. Then, all subsequent calls of emacsclient should connect to the server, saving startup time. FreeBSD's Emacs package doesn't seem to include any rc.d scripts so it's up to the user to procure one and that's what I'm trying to do.

I learned a bit about rc.d scripting already by reading Practical rc.d scripting in BSD [1]. There also seems to be exactly one prior thread on the forums that touches this very topic although somewhat differently [2]. A certain post on reddit can also be used as a guidance [3]. I tinkered with the templates in these sources and put together a script, but it has problems and I would appreciate input from anyone literate in the matter.

The idea is to have a working Emacs daemon, which comes down to emacsclient being able to connect to the running daemon. The service should be ran as a user (current unprivileged user or dedicated emacs user, perhaps?), to avoid privilege issues (I don't know if it's the case in general or a result of the security.bsd.see_other_uids=0 hardening option, but my unprivileged user running the emacs client can't see the emacs socket of the daemon started as root).

I present what I was able to patch together below. A discussion follows.

Bash:
#!/bin/sh

# PROVIDE: emacsd
# REQUIRE: LOGIN

. /etc/rc.subr


name="emacsd"
rcvar="emacsd_enable"
emacsd_command="/usr/local/bin/emacs"
emacsd_flags="daemon"
pidfile="/var/run/${name}.pid"
command="/usr/sbin/daemon"
command_args="-P ${pidfile} -r -f -u ${emacsd_user} ${emacsd_command}"
stop_postcmd="emacsd_exit"

load_rc_config $name
: "${emacsd_enable:="NO"}"
: "${emacsd_user:="artur"}"


emacsd_exit()
{
    [ -e "${pidfile}" ] && \
    echo "Stopping supervising daemon." && \
    kill -s TERM "$(cat ${pidfile})"
}


run_rc_command "$1"


Discussion:
1) I try to make use of daemon(8) as in [3][9]. This introduces the problem of pid file, which cannot be accessed without elevated privilege and I am not sure what the good practice dictates here: specifying the pidfile variable somewhere in emacsd_user Home dir? Will it be safe or suffice to get it running? Perhaps I should just give up on daemon(8) and the pidfile completely?
2) emacsd_command: following the recommended practice I include daemon in the emacsd_flags variable. This is different than in [2] and [4], where the users are advised to use a dedicated ${name}_start() method that includes the --daemon and other flags.
3) Related to that, in [2] and [4] the user substitution is handled within that same method with su. The manual page for daemon(8) shows that it has -u flag, that substitutes the user. The manual mentiones potential subsitute privileges that may be needed (as they in fact seem to be in case of pidfile). According to [6] emacsd_user also specifies who to run a service as. I am at a loss, which of those options should be specified and used.
4) More generally, I am wondering if there may be an elegant way to specify the "${emacsd_user:="artur"}" to be more universal? I figure that with "${emacsd_user:="$(id -un)"}" the expansion happens when the script is run, that is on system startup, so it goes to... root? Again, that's not really a problem to me, as I know to specify it in /etc/rc.conf, but I curious if it could be made more general (root? emacsuser?).

That's more or less it. While my example concerns an emacs server specifically, the questions are of universal nature, so perhaps it can be of some use to others interested in service scripts. I include sources mentioned in the text above, as well as a reference to applicable manuals.


Resources:
[1] Practical rc.d scripting in BSD
[2] Running Emacs as a daemon
[3] Supervised FreeBSD rc.d script for a Go daemon
[4] How to run a daemon as a user that isn't root?
[5] rc(8)
[6] rc.conf(5)
[7] rc.subr(8)
[8] rcorder(8)
[9] daemon(8)
 
I finally found some time to come back to this and had what seems to me like the right idea.
My intention is to have this script use daemon(8), run as a configurable user (artur), to start the emacs daemon, creating its .pid file in the configurable pid directory (/var/run/artur). That directory is set up in start_precmd. The command checks whether the /var/run subdirectory for the emacsd_user exists, creates it if necessary, and sets permissions. I think I have to give it the execute permission at that point, hence 0700 [1]. The reason I created the start_precmd in the first place was because with the script in the first post, the .pid file would not be created (with a permission denied error). This lead me to believe that in order to use a .pid file, I need to provide correct access to /var/run/ to the user starting the daemon.
Well, the pid file is still not cooperating.

Bash:
#!/bin/sh
#

# PROVIDE: emacsd
# REQUIRE: LOGIN

. /etc/rc.subr


name="emacsd"
rcvar="emacsd_enable"
emacsd_command="/usr/local/bin/emacs"
emacsd_flags="daemon"
pidfile="${emacsd_piddir}/${name}.pid"
command="/usr/sbin/daemon"
command_args="-P ${pidfile} -r -f -u ${emacsd_user} ${emacsd_command}"
start_precmd="${name}_prestart"
stop_postcmd="${name}_exit"

load_rc_config $name
: "${emacsd_enable:="NO"}"
: "${emacsd_user:="artur"}"
: "${emacsd_piddir:="/var/run/${emacsd_user}"}"

emacsd_prestart()
{
    ! [ -d "${emacsd_piddir}" ] && \
    mkdir "${emacsd_piddir}"
    chown "${emacsd_user}":"${emacsd_user}" "${emacsd_piddir}" && \
    chmod 0700 "${emacsd_piddir}"
}

emacsd_exit()
{
    [ -e "${pidfile}" ] && \
    echo "Stopping supervising daemon." && \
    kill -s TERM "$(cat "${pidfile}")"
}


run_rc_command "$1"


As I mentioned in the intro above, the script fails to run the emacs daemon as user.

On boot (dmesg) I get:
Bash:
daemon: ppidfile ``/emacsd.pid'': Permission denied

On doas service emacsd start:
Bash:
Starting emacsd.
usage: daemon [-cfHrS] [-p child_pidfile] [-P supervisor_pidfile]
              [-u user] [-o output_file] [-t title]
              [-l syslog_facility] [-s syslog_priority]
              [-T syslog_tag] [-m output_mask] [-R restart_delay_secs]
command arguments ...

I don't know what's the problem here. daemon(8) entry on the '-P supervisor_pidfile' flag says:
-P supervisor_pidfile
Write the ID of the daemon process into the supervisor_pidfile
using the pidfile(3) functionality. The program is executed in a
spawned child process while the daemon waits until it terminates
to keep the supervisor_pidfile locked and removes it after the
process exits. The supervisor_pidfile owner is the user who runs
the daemon regardless of whether the -u option is used or not.

Since both my user and root should have access to the directory I intended for the .pid file, I am at a loss again. Any ideas?

[1] I'm not sure if for security reasons, those permissions shouldn't be dropped to 0600 in an additional start_postcmd.
 

Thank you for this suggestion. While I'm not certain it is necessary, I still used it to simplify the code.

I am moving forward towards getting it up and functional. I rearranged the lines, which may have been a cause of problems, and fixed some mistakes. The code below can properly run the daemon(8) and execute the emacs server process.

Some observations (in order of issues raised in the OP):
1) Pidfile - this is taken care of elegantly by the `start_precmd' method and uses suggestion given by SirDice in the post above.
2) The original code in the OP and the second post used `emacsd_flags' incorrectly (${name}_flags lists arguments for ${command}, which in this case is the daemon(8) utility, not the `emacs' executable). Now I stich together the command to be exectuted by using `command', `emacsd_flags', `proc_cmd' and `proc_args', which seem to get concatenated in that order. Some existing daemons I consulted use `command_args' instead of `emacsd_flags', which may seem cleaner and works identically in this case, but the Practical scripting article ([1] in the OP) suggests to leave `command_args' for descriptor redirections and use ${name}_flags for hyphened arguments. *Importantly*, for emacs process we need to use the `--fg-daemon' (foreground daemon) emacs flag instead of the "default" `--daemon' (alias `--bg-daemon', background daemon) flag. Using the latter detaches emacs server from the daemon(8) process, nullifying its purpose.
3) With regards to point 3) in the OP, I found out that *no* -u flag should be added to daemon(8) as argument as it prevents child emacs server process from properly executing.
4) This is difficult or undoable, but primarily redundant.

Bash:
#!/bin/sh
#

# PROVIDE: emacsd
# REQUIRE: LOGIN
# KEYWORD: shutdown

. /etc/rc.subr

name="emacsd"
rcvar="${name}_enable"
start_precmd="${name}_prestart"
stop_postcmd="${name}_exit"

load_rc_config $name
: "${emacsd_enable:="NO"}"
: "${emacsd_user:="artur"}"


piddir="/var/run/${emacsd_user}"
pidfile="${piddir}/${name}.pid"
command="/usr/sbin/daemon"
proc_cmd="/usr/local/bin/emacs"
proc_args="--fg-daemon=emacsd"
emacsd_flags="-f -P ${pidfile} -r ${proc_cmd} ${proc_args}"
#command_args="2>/dev/null 1>&2" # recommended use
#command_args="-f -P ${pidfile} -r ${proc_cmd} ${proc_args}" # works nonetheless
#emacsd_group="${emacsd_user}"

emacsd_prestart()
{
    ! [ -d "${piddir}" ] && \
    install -d -o "$(id -u ${emacsd_user})" -g "$(id -u ${emacsd_user})" "${piddir}" -
    return 0
}

emacsd_exit()
{
    [ -e "${pidfile}" ] && \
    echo "Stopping supervising daemon." && \
    kill -s TERM "$(cat "${pidfile}")"
}


run_rc_command "$1"

Hopefully these hints can be of use to someone else writing an rc.d service. If someone wants to use this daemon, they are free to do it, but be aware that it may not be fully functional yet because I have encountered another problem with process sockets.

There seem to be two overlapping issues.
I) One is that emacs server spawned by daemon(8) doesn't seem to listen to the socket, so I have to specify the `-s /server/socket/dir' when running emacsclient. This is not a case when emacs daemon is started manually from shell after login (a simple emacsclient -c works).
II) It turns out that when started prior to logging in, the emacs daemon chooses `/tmp/emacs$UID' (i.e. /tmp/emacs1001) for its server socket dir, but Emacs 27.1 prefers the XDG_RUNTIME_DIR (i.e. /tmp/1001-runtime-dir/emacs) for listening to a socket. Since I set this env variable in my .profile I encounter some kind of mismatch there and cannot get my emacs user config when connecting to the server with emacsclient -c -s /tmp/emacs1001.

> All in all, if after login I simply run:
emacs --daemon and
emacsclient -c
it works as intended.

> If after login I manually run the rc.d script command:
/usr/sbin/daemon -f -P /var/run/artur/emacsd.pid -r /usr/local/bin/emacs --fg-daemon=emacsd -u artur and
emacsclient -c -s /tmp/1001-runtime-dir/emacs/emacsd
the daemon gets started properly, uses the XDG_RUNTIME_DIR for socket properly, and can be connected to when provided with socket file. As per (I), running the client without that doesn't connect to the server.

>If I let the rc.d do the job I get a daemon that isn't using the proper socket, and I can't get my emacs user config. Perhaps setting the XDG_RUNTIME_DIR in the rc.d script can help - I am going to test it. I still think an rc.d solution is more elegant and manageable than simply running the daemon in .profile script but it is a lot of work. I will report back if I can get it working. Meanwhile, if anyone has any comments, I am open to hear them.

That's it for now.
 
I think I have a decent enough solution for an rc.d Emacs daemon.

Bash:
#!/bin/sh
#

# PROVIDE: emacsd
# REQUIRE: LOGIN
# KEYWORD: shutdown

. /etc/rc.subr

name="emacsd"
rcvar="${name}_enable"
start_precmd="${name}_prestart"

load_rc_config $name
: "${emacsd_enable:="NO"}"
: "${emacsd_user:="artur"}"


piddir="/var/run/${emacsd_user}"
pidfile="${piddir}/${name}_daemon.pid"
pidfile_child="${piddir}/${name}_emacs.pid"
emacsd_env="XDG_RUNTIME_DIR=/tmp/$(id -u ${emacsd_user})-runtime-dir HOME=/home/${emacsd_user}"
proc_cmd="/usr/local/bin/emacs"
proc_args="--fg-daemon -u ${emacsd_user}"
command="/usr/sbin/daemon"
command_args="-f -P ${pidfile} -p ${pidfile_child} -r ${proc_cmd} ${proc_args}"


emacsd_prestart()
{
    [ -z "${XDG_RUNTIME_DIR}" ] && XDG_RUNTIME_DIR="/tmp/$(id -u ${emacsd_user})-runtime-dir"
    ! [ -d "${XDG_RUNTIME_DIR}" ] && \
    install -d -o "$(id -u ${emacsd_user})" -m 0700 "${XDG_RUNTIME_DIR}" -

    ! [ -d "${piddir}" ] && \
    install -d -o "$(id -u ${emacsd_user})" -g "$(id -u ${emacsd_user})" "${piddir}" -
    return 0
}


run_rc_command "$1"


Some notes about the above:
1) The daemon(8) process creates two .pid files in /var/run/<user>, one for itself (supervisor) and one for the child (Emacs server). To be able to do that, it has to ensure that directory exists before running the main command. This is achieved in the latter part of the emacsd_prestart() method.
2) I choose to pass -f to daemon(8) and not enable any logging, because that causes Emacs daemon to print its lengthy startup message:
Warning: due to a long standing Gtk+ bug
Emacs might crash when run in daemon mode and the X11 connection is unexpectedly lost.
Using an Emacs configured with --with-x-toolkit=lucid does not have this problem.
All your packages are already installed
Loading /home/artur/.config/emacs/artur-init.el (source)...
Loading /home/artur/.config/emacs/artur-init.el (source)...done
Starting Emacs daemon.
3) A emacsd_start method could be nice for adding a startup message (e.g. echo "Starting ${name}.") on top of running the command, but it changes the env routine and requires more work than its worth.
4) Talking about env, emacsd_env variable sets two variables needed to make the Emacs server work as smoothly as possible: XDG_RUNTIME_DIR that my Emacs 27.2 seems to prefer, and HOME needed to load user config correctly. Additionally, the first part of the emacsd_prestart() method makes sure the variable is set locally and that Emacs's`server-socket-dir' exists (it is where Emacs daemon puts its socket file). This approach avoids exporitng these variables. This also lets us use a simple emacsclient -c to properly connect to the server without having to point to the socket file. [1]
5) I removed the emacsd_exit() method. I tested the behaviour of daemon(8)'s flags -p -P and if I only specify the supervisor .pid file (-P), stopping the service with service(8) properly kills the daemon(8) process, instead of purported child process (as per discussion in link [3] in OP). If I only specify the child .pid file (-p), service(8) doesn't work properly. With both specified, behaviour is identical as in the first case.

I will proceed to mark this as solved, but comments and suggestions are always welcome.

[1} Adding the daemon [name] to --fg-daemon=[name] in proc_args, messes emacsclient's ability to see the socket, and then the socket path needs to be passed as argument to the client (i.e. emacsclient -c -s /tmp/$(id -u)-runtime-dir/emacs/[name] if using XDG_RUNTIME_DIR as above), or set and exported as environment variable EMACS_SOCKET_NAME (e.g. ...="$(sockstat -lq | grep 'emacs' | awk '{print $6}')"). The defaults let us avoid all that.
 
Thank You for this script.

But I got some issues with it...

1) This command asks more priveleges:
Code:
 `sockstat -lq | grep 'emacs' | awk '{print $6}'`
Code:
> sockstat -lq | grep 'emacs' | awk '{print $6}'
sockstat: getprotobyname: sctp: Permission denied
;
2) This command doesn't works as expected:
emacsclient -c
even without this [name]:
Code:
Adding the daemon [name] to --fg-daemon=[name] in  proc_args
, output is:
Code:
emacsclient: can't find socket; have you started the server?
emacsclient: To start the server in Emacs, type "M-x server-start".
emacsclient: No socket or alternate editor.  Please use:

    --socket-name
    --server-file      (or environment variable EMACS_SERVER_FILE)
    --alternate-editor (or environment variable ALTERNATE_EDITOR)
.
Is it possible to do something with it?
 
Hi. Good to see someone else putting it to use.

1) This command asks more priveleges:
Code:
 `sockstat -lq | grep 'emacs' | awk '{print $6}'`
Code:
> sockstat -lq | grep 'emacs' | awk '{print $6}'
sockstat: getprotobyname: sctp: Permission denied

I don't know why the command is asking you for elevated privilege, but since you're using it, I assume you are trying to set the EMACS_SOCKET_NAME environment variable, which shouldn't be necessary (it is when you pick the socket name, as you pointed out).
Can you clarify that your emacs daemon starts but the emacsclient does not connect to it? My htop reports the following (I blank the username as it's a business laptop).

20220225_emacsd.png


Anyhow, both of your questions seem to relate to the socket file, so what you really need to do is locate the socket file and check its pathname.

Can you describe your setup? TUI-only/GUI? X or Wayland? WM or desktop environment? I use Wayland and Sway wm, but your system could be one that makes no use of XDG_RUNTIME_DIR or sets it to some other directory.

To that end, I'd first make sure what the value of your XDG_RUNTIME_DIR is and then see if you can find your emacs socket there. If your system doesn't use this variable (if there are indeed such systems), then look around at /tmp/. This may also be about dir ownership and privilege modes.

Another quick test you could conduct is putting the following lines in your .profile file. This is what I use in my setup, and you may notice that this agrees with what the emacsd uses. It's of course up to the user to choose what exact directory they use, but what you or your system sets (and then expects) must agree with what the emacsd script uses.

Code:
[ -z "${XDG_RUNTIME_DIR}" ] && XDG_RUNTIME_DIR="/tmp/$(id -u)-runtime-dir" && export XDG_RUNTIME_DIR
! [ -d "${XDG_RUNTIME_DIR}" ] && mkdir "${XDG_RUNTIME_DIR}" && chmod 0700 "${XDG_RUNTIME_DIR}"

So the best approach for now should be to see if your system uses XDG_RUNTIME_DIR, find its value, and then edit this emacs script to use that directory instead of what I use.

Try to make some use of these hints and report back.

~artur
 
Hi.
I like your way of doing things, it seems to me a true Un*x in every element.
I'm running emacs-nox11 in dwm environment under urxvt.
I prefer TUI, just for not to get used to the good, for the well feeling of myself in any environment, even ones without X11.
As far as I remember:
1) This directory created, and socket file is there (if I correctly memorize it's path):
/tmp/myusername-runtime-dir/emacs/server
2) I've just hardcoded this into my ~/.profile (as temporary ad-hoc solution):
EMACS_SOCKET_NAME=/tmp/myusername-runtime-dir/emacs/server && export EMACS_SOCKET_NAME
I can't answer all your questions, because this setup was made on workstation at my office, so I'll do it after this weekend.
 
I'm on FreeBSD 13.0 if it matters.
Just a little correction: socket dir is
/tmp/myuserid-runtime-dir/emacs/server
I have found the problem:
> env|grep -i xdg
XDG_RUNTIME_DIR=/var/run/user/1001
There is
> ls /var/run/user/1001/emacs
directory , but it's empty.
Now it works, modified version of your script, here it is:
Bash:
#!/bin/sh
#

# PROVIDE: emacsd
# REQUIRE: LOGIN
# KEYWORD: shutdown

. /etc/rc.subr

name="emacsd"
rcvar="${name}_enable"
start_precmd="${name}_prestart"

load_rc_config $name
: "${emacsd_enable:="NO"}"
: "${emacsd_user:="nobody"}"

piddir="/var/run/${emacsd_user}"
pidfile="${piddir}/${name}_daemon.pid"
pidfile_child="${piddir}/${name}_emacs.pid"
user_id="$(id -u ${emacsd_user}"
emacsd_env="$(env -iL ${user_id} XDG_RUNTIME_DIR=/var/run/user/${user_id} HOME=/home/${emacsd_user} | tr '\n' ' ')"
proc_cmd="/usr/local/bin/emacs"
proc_args="--fg-daemon -u ${emacsd_user}"
command="/usr/sbin/daemon"
command_args="-f -P ${pidfile} -p ${pidfile_child} -r ${proc_cmd} ${proc_args}"

emacsd_prestart()
{
    [ -z "${XDG_RUNTIME_DIR}" ] && XDG_RUNTIME_DIR="/var/run/user/$(id -u ${emacsd_user})"
    ! [ -d "${XDG_RUNTIME_DIR}" ] && \
    install -d -o "$(id -u ${emacsd_user})" -m 0700 "${XDG_RUNTIME_DIR}" -

    ! [ -d "${piddir}" ] && \
    install -d -o "$(id -u ${emacsd_user})" -g "$(id -u ${emacsd_user})" "${piddir}" -
    return 0
}

run_rc_command "$1"
I had trouble with populating PATH environment variable to emacs's exec-path, when using emacsclient. (magit can't find git binary, because of absence of /usr/local/bin in emacs's exec-path variable). Don't know why, but I can't persistently modify exec-path with emacs's "customize", it works just during one session.
I change your script to populate $PATH from /etc/login.conf with env command. I don't like this pipe and want to make this piece of code some prettier, but it works. Could you give me a link on ${name}_env variable usage in rc.d scripts?
 
Last edited:
There is a problem in this script, just after login emacsclient can't find emacs daemon, but it exists in process list. it (emacsclient) works just after restarting daemon: service emacsd restart
Any ideas?
 
Last edited:
Hi. I'm glad to hear you made some progress.
Could you give me a link on ${name}_env variable usage in rc.d scripts?
It's in this very nice manual: rc.subr(8).

There is a problem in this script, just after login emacsclient can't find emacs daemon, but it exists in process list. it (emacsclient) works just after restarting daemon: service emacsd restart
Any ideas?
I cannot test it now, but I am almost certain that the problem is with the way you extract the environment variable, that is when rc runs your script, it is before the user session and env is not populated (or rather, you access root env). Thus, you should simply put them as a space-separated list of variables as I do in my example.

The environment handling, is by the way, the single greatest limitation that I see with this Emacs server setup. The daemon only has access to those variables defined in the ${name}_env (or ${name}_env_file) variable, and Emacs, being the mammoth it is, uses the environment extensively (for instance, if you use GnuPG to encrypt files, GNUPGHOME needs to be given, or if you want to make use of (getenv) in your Emacs init file to universally set up some directories used by Emacs packages, the variables too need to be provided to the rc script). There's also the question of whether things like D-Bus system session (used by emacsd) communicates gracefully with the user D-Bus sessions, which may be necessary for things as simple as forwarding a link from an org-mode file to be opened in the browser. I heard GNU Guix (the OS, not the package manager), and its init system GNU Shepherd, can run services as user, solving all this, but I'm pretty sure rc can't do that.

If you were to find yourself in need to have access to the complete user environment with whatever may get exported during a session, I think it's still best to resort to starting the Emacs server in your .profile file or in the startup script / config of your graphical environment as it opens Emacs up to the whole set of variables. Even using daemon(8) still makes sense in that case, as it automatically restarts the child process (emacs --fg-daemon) and so a simple command like pkill -f -x ".*emacs --.*-daemon.*" can be used to quickly restart the whole thing, like service emacsd restart would do.
 
Top