14

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

2 Answers 2

19

I can think of multiple ways to do this. Of course, each has its own caveats.

  1. 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:

    1. 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)
      
    2. 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 with chvt 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
      
    3. reptyr uses the ptrace(2) system call to attach to a remote program (via it's PID). This is a completely different approach than conspy & 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 find reptyr is a great, niche tool to move existing PIDs into a screen session or tmux window or pane.

      That said; I put this option here, albeit last, because it's still possible to use reptyr without screen or tmux. 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 for Restart, 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>
      
  2. 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 or reptyr. screen is available via most base package repositories, and can be used like this:

    screen -r systemd-interactive # CTRL-A+D to detach
    
  3. A similar approach to forking screen would be to fork tmux. The systemd service for tmux is almost the same as it is for screen. But, I'm not going to detail this because, well, it's late & I'm tired. Yes, I use tmux a lot more than screen (these days).

    In fact, I'm writing this in neovim pane in tmux right now. But, I've still used screen for a lot longer. In my experience and opinion, tmux is overkill for something like this. Sure tmux is newer, has more features, and is a MUCH better shell multiplexer than screen 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 with conspy.

  4. Depending on your program; named pipes ... systemd services support them, too! i.e.

    StandardInput=/path/to/named/pipe|
    

... and more.

7
  • 1
    first 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.
    – Rusi
    Nov 28, 2018 at 2:52
  • Thanks for pointing that out; I've dropped it from the example. Nov 28, 2018 at 15:05
  • 1
    Great post (upvote from me) but it's perhaps also worth mentioning detachtty. It's a super lightweight screenlike 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.
    – Quantum7
    Oct 8, 2019 at 9:37
  • 1
    Thanks @Quantum7. I've updated in the ExecStartPre for screen, to use -S rather than -t. Oct 8, 2019 at 14:58
0

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
1
  • 1
    Welcome 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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .