Solved Rc.d daemon as user (emacs)

arturb

New Member

Reaction score: 1
Messages: 6

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)
 
OP
A

arturb

New Member

Reaction score: 1
Messages: 6

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.
 
OP
A

arturb

New Member

Reaction score: 1
Messages: 6


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.
 
OP
A

arturb

New Member

Reaction score: 1
Messages: 6

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.
 
Top