I have a systemd service that is a console application, meaning that it is controlled by sending commands to its stdin and it outputs information to sdout. How can I set up the systemd service so that I can connect to its stdin and give it commands at any point, then detach from this, and repeat when necessary?
2 Answers
I can think of multiple ways to do this. Of course, each has its own caveats.
Probably the most straightforward approach would be to create a simple service with a dedicated tty, similar to this:
# /etc/systemd/system/systemd-interactive-simple-tty.service [Unit] Description=Example systemd interactive simple tty service After=getty.service [Service] # https://www.freedesktop.org/software/systemd/man/systemd.exec.html ExecStart=/usr/local/sbin/systemd-interactive.bash StandardInput=tty-force TTYVHangup=yes TTYPath=/dev/tty20 TTYReset=yes # https://www.freedesktop.org/software/systemd/man/systemd.service.html Type=simple RemainAfterExit=false Restart=always RestartSec=5s [Install] WantedBy=default.target
The following options will work with the above simple service:
conspy takes (remote) control of a text mode virtual console. This is probably your best bet (with the above tty service). It's available via most extended package repositories and is simple to use, like this:
conspy 20 # hit ESC+ESC+ESC (3 times quickly, to exit)
chvt works similarly to
conspy
but makes /dev/ttyN the foreground (local) terminal. It's part of the kbd collection and is installed by default on virtually every modern Linux distribution. That's why I thought it was worth a mention. The major caveat withchvt
is that it requires you to use the attached keyboard, which is probably not what you want. For the above service example,chvt
could be used like this:chvt 20 # ALT+F1 to return to /dev/tty1
reptyr uses the
ptrace(2)
system call to attach to a remote program (via it's PID). This is a completely different approach thanconspy
&chvt
, but would work with the above service definition too.Just keep in mind that
reptyr
, by itself, doesn't really support 'detaching'. Its termcap support isn't very robust, either. Typically,reptyr
is used in conjunction with screen and/or tmux because they provide a more seamless way to 'detach'; I findreptyr
is a great, niche tool to move existing PIDs into ascreen
session ortmux
window or pane.That said; I put this option here, albeit last, because it's still possible to use
reptyr
withoutscreen
ortmux
. The major caveat is if you break out of the process (e.g ^C), rather than reptyr'ing it (again) to another tty/pty (via another shell). Sending a break to the process may cause it to abort and I'm sure you know the rest.Maybe that's OK, especially if the process isn't critical and the systemd service is configured to
Restart=always
as I've shown above. If the process 'breaks' then systemd will automatically restart it (another cool feature of systemd!). There are different values forRestart
, too. YMMV.reptyr
is available via most extended package repositories and can be used, like this:reptyr $(systemctl status systemd-interactive-simple-tty.service | grep Main\ PID | awk '{print $3}') # or just reptyr <pid>
Another (more complex [meaning there's more that could fail]) approach would be to create a forking service using screen, similar to this:
# /etc/systemd/system/systemd-interactive-forking-screen.service [Unit] Description=Example systemd interactive forking screen service [Service] # https://www.freedesktop.org/software/systemd/man/systemd.exec.html ExecStartPre=-/usr/bin/screen -X -S ${SCREEN_TITLE} kill # [optional] prevent multiple screens with the same name ExecStart=/usr/bin/screen -dmS ${SCREEN_TITLE} -O -l /usr/bin/bash -c /usr/local/sbin/systemd-interactive.bash # https://www.freedesktop.org/software/systemd/man/systemd.service.html Type=forking Environment=SCREEN_TITLE=systemd-interactive RemainAfterExit=false Restart=always RestartSec=5s SuccessExitStatus=1 [Install] WantedBy=default.target
screen is a full-screen window manager that multiplexes a physical terminal between several processes. It's quite a bit more complex than anything listed in the first, simple option. Personally, I've been using screen for a long, long time and feel comfortable enough to trust it with most things. It's an invaluable tool.
The primary advantage over the above is decent termcap support (though not as good as tmux's). That just means that your backspace key, arrows, etc. will work better than with
conspy
orreptyr
.screen
is available via most base package repositories, and can be used like this:screen -r systemd-interactive # CTRL-A+D to detach
A similar approach to forking screen would be to fork
tmux
. The systemd service fortmux
is almost the same as it is forscreen
. But, I'm not going to detail this because, well, it's late & I'm tired. Yes, I usetmux
a lot more thanscreen
(these days).In fact, I'm writing this in neovim pane in
tmux
right now. But, I've still usedscreen
for a lot longer. In my experience and opinion,tmux
is overkill for something like this. Suretmux
is newer, has more features, and is a MUCH better shell multiplexer thanscreen
but ... it's even more complex. Along with that extra complexity comes some additional instability.More important, to me at least, is that
tmux
crashes more often than screen. I listed screen as #2 because, if it were me, for something like that I'd probably just use #1 withconspy
.Depending on your program; named pipes ... systemd services support them, too! i.e.
StandardInput=/path/to/named/pipe|
... and more.
-
1first version viz the
type=simple
one: As the systemd.service link you have kindly provided says,GuessMainPID
is ignored if type is simple and so may be dropped.– RusiNov 28, 2018 at 2:52 -
Thanks for pointing that out; I've dropped it from the example. Nov 28, 2018 at 15:05
-
1Great post (upvote from me) but it's perhaps also worth mentioning
detachtty
. It's a super lightweightscreen
like app, but with none of the additional functionality (just support for detaching and reattaching to ttys). Sep 19, 2019 at 5:00 -
I like the screen answer. There's a small typo in the ExecStartPre script though: it should be
-S
for the session name instead of-t
. In ExecStart, make sure the bash path is correct for your system too (I have/bin/bash
not/usr/bin/bash
). Errors starting the screen session may cause it to fail silently.– Quantum7Oct 8, 2019 at 9:37 -
1Thanks @Quantum7. I've updated in the ExecStartPre for screen, to use -S rather than -t. Oct 8, 2019 at 14:58
Even simpler solution for screen that works for me:
Location:
/home/someUser/.config/systemd/user/some.service
Content:
[Unit]
Description=someServer
Wants=network.target
After=network.target
[Service]
ExecStart=screen -S screenName -D -m /home/someUser/startServer.sh
[Install]
WantedBy=default.target
-
1Welcome to U&L. Can you please edit the answer to explain how exactly this addresses the question? Looks like this isn't really an answer to the question, but rather comment on the accepted answer; and answers aren't for commenting. Please check the Asking- and Answering -sections in the Help and take the Tour to get a picture on how Q&A sites work and differ from discussion boards. Feb 20 at 23:01