systemd, tmux, and WeeChat
Today, edunham posted a recipe for starting screen+irssi at boot using rc.local. That's pretty cool (and useful!), but it doesn't fit into my set up very well. I know that some time this week, my VPS provider will be doing maintenance and rebooting my shell server, so it seemed like a good time set up a more automatic persistent IRC.
My setup is different in 3 key ways: I don't use screen, I don't use irssi, and I don't (want to) use rc.local. Instead I've got tmux, WeeChat, and systemd. I figure these three things are roughly equivalent, so I set off to try and apply the same idea to my setup.
Step 1: Systemd user sessions
I could run this as root and hard code my user into the init files, or be clever and try and make the user configurable, and somehow allow for multiple user sessions (I'm not the only one to use this box) and blah blah blah. This all sounds not quite right. This is a service just for me, right? It should run as me! Luckily, systemd has a user mode that works well for this. On my VPS, which is running an up-to-date Arch installation, this Just Worked™. Systemd had already helpfully created user sessions where needed. Awesome.
To see if this was true, I used this command:
$ pgrep -fau $(whoami) systemd
261 /usr/lib/systemd/systemd --user
Step 2: Making a service file
After some Googling, I figured out that I should "just" be able to start a tmux session with WeeChat from a systemd service file. This is straight forward after you know how to do it, but the docs are a bit hard to wade through.
In the end, I learned that I need a .service
unit file in my systemd user
configuration directory, that looks like this:
~/.config/systemd/user/weechat.service
[Unit]
Description=Weechat IRC Client (in tmux)
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/tmux -2 new-session -d -s irc /usr/bin/weechat
ExecStop=/usr/bin/tmux kill-session -t irc
[Install]
WantedBy=default.target
Some of this is self explanatory. The syntax is INI style, so the things in
square brackets are section titles. I'm not going to explain Description
,
ExecStart
, or ExecStop
. I hope they are obvious.
The others are a bit more subtle. From top to bottom:
Type=oneshot
: This tells systemd that this service file will run a command, and that command will run and do something and then exist. Because tmux will "own" the process outside of the systemd controlled cgroup. It will get executed by the tmux server. At least, as I understand it.RemainAfterExit=yes
: This tells systemd that the above is sort of a lie. In particular, it indicates that the service should be kept in the "running" state even though systemd can't tell that something is running. In particular, this lets us stop the service nicely using theExecStop
definition.WantedBy=default.target
: This simply tells systemd what to do when we "enable" this unit. In systemd parlance, enable usually means "start at boot", but it doesn't have to. In this case, it means start at boot.
The ExecStart
and ExecStop
lines start and stop a decently customized tmux
session to run WeeChat in, respectively.
Step 3: Headdesk
The above unit file took a while to figure out. For one, the -d
flag to tmux
isn't entirely obvious. That tells tmux to start the session detached, which
is important since systemd won't get it a TTY to put anything on.
The Type=oneshot
and RemainAfterExit=yes
was particularly hard to find. I
eventually found someone else's systemd user unit files that started a
program in tmux, and they used it. Yay for Googling.
The final piece in the puzzle was the most frustrating. I started the service
and systemd claimed it was running, and pgrep
agreed. But tmux refused to
connect with the error "no sessions".
I'm not sure why this is the case, but this post on a systemd mailing
list led me to try running $ sudo loginctl enable-linger $(whoami)
.
That worked! Now when I start the session, it creates a session I can actually
attach to. Win!
All together now
After putting all the above in place, I can now do:
# Enable the service to start at boot time.
$ systemctl --user enable weechat.service
Created symlink from /home/mythmon/.config/systemd/user/default.target.wants/weechat.service
to /home/mythmon/.config/systemd/user/weechat.service.
# Start the service.
$ systemctl --user start weechat.service
# Attach to the new tmux session.
$ tmux attach -t irc
I was presented with a brand new instance of WeeChat. Success!
I still have some automation to do within WeeChat, like connecting to some servers that require passwords and joining all the right channels, but this seemed like the harder part to me, so I'm glad to have it out of the way.