38

I am writing a shell script that I would like to run as a daemon on startup without using external tools like daemontools or daemonize.


Linux Daemon Writing HOWTO

According to the Linux Daemon Writing HOWTO, a proper daemon has the following characteristics:

  • forks from the parent process
  • closes all file descriptors (i.e., stdin, stdout, stderr)
  • opens logs for writing (if configured)
  • changes the working directory to one that is persistent (usually /)
  • resets the file mode mask (umask)
  • creates an unique Session ID (SID)

daemonize Introduction

The daemonize Introduction goes further, stating that a typical daemon also:

  • disassociates from its control terminal (if there is one) and ignores all terminal signals
  • disassociates from its process group
  • handles SIGCLD

How would I do all this in a sh, dash, or bash script with common Linux tools only?

The script should be able to run on as many distros as possible without additional software, although Debian is our primary focus.


NOTE: I know there are plenty of answers on the StackExchange network recommending the use of nohup or setsid, but neither of these methods tackles all of the requirements above.


EDIT: The daemon(7) manpage also gives some pointers, although there seem to be some differences between older-style SysV daemons and newer systemd ones. Since compatibility with a variety of distros is important, please ensure the answer makes clear any differences.


1
  • 1
    The "proper" way to craft your own shell script is to make it do its own logging, provide a method for launching it as a daemon, etc. Things like daemon and those other things are for running arbitrary shell scripts with no provision for running as a daemon. Since you're the author, fully in control of how that script is written, make it so it can just be launched from a systemd unitfile or rc.d script. You did specify "Proper"!
    – Rich
    Apr 11, 2018 at 1:06

3 Answers 3

40

Using systemd you should be able to run a script as a daemon by creating a simple unit. There are a lot of different options you can add but this is about as simple as you can get.

Say you have a script /usr/bin/mydaemon.

#!/bin/sh

while true; do
  date;
  sleep 60;
done

Don't forget to sudo chmod +x /usr/bin/mydaemon.

Create a unit /etc/systemd/system/mydaemon.service.

[Unit]
Description=My daemon

[Service]
ExecStart=/usr/bin/mydaemon
Restart=on-failure

[Install]
WantedBy=multi-user.target 

To start the daemon run

systemctl start mydaemon.service 

To start it at boot, enable it

systemctl enable mydaemon.service

If on a systemd based system, which a majority of Linux distributions are today, this isn't really an external tool. The negative is that it won't work everywhere.

4
  • 3
    While I like the systemd approach, the OP said "no external tools". There are Linux distributions which don't have systemd yet, or let you use choose between systemd and something else, e.g. OpenRC. Apr 4, 2018 at 3:05
  • 9
    For distros that use systemd, systemd is no more of an "external tool" than bash.
    – Alexander
    Apr 10, 2018 at 12:10
  • Using that example unit didn't work for me (receiving a 203/EXEC failue). I added /bin/sh before the ExecStart path Mar 5, 2020 at 1:04
  • 1
    @RobertoManfreda the script must be executable (see the chmod part of this answer)
    – roaima
    Jun 30, 2020 at 7:20
13
+500

I am probably missing something here; why exactly wouldn't nohup be appropriate? Of course it's not enough alone, but supplementing it seems straightforward.

#!/bin/bash

if [ "$1" = "DAEMON" ]; then
    # is this necessary? Add other signals at will (TTIN TTOU INT STOP TSTP)
    trap '' INT
    cd /tmp
    shift
    ### daemonized section ######
    for i in $( seq 1 10 ); do
        date
        sleep 5
    done
    #### end of daemonized section ####
    exit 0
fi

export PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin
umask 022
# You can add nice and ionice before nohup but they might not be installed
nohup setsid $0 DAEMON $* 2>/var/log/mydaemon.err >/var/log/mydaemon.log &

As far as I can see:

  • the output is appropriately redirected (use /dev/null if necessary)
  • the umask is inherited
  • stdin dies at the end of the parent script anyway
  • the daemon.sh script is reparented to init (or systemd)

I have a strong feeling I'm missing the obvious. Downvote, but please tell me what it is :-)

5
  • 2
    I was about to suggest something very similar. I use nohup with & and I/O redirection to launch several non - C daemon utilities with perhaps the added security of wrapping your nohup command inside of a su -c "nohup ... &" -s /bin/bash systemUser to run the daemon as a non-privileged user.
    – 111---
    Apr 4, 2018 at 18:31
  • when nohup met ssh host -t 'nohup ./some_long_run_command&' it will not work
    – bzhu
    Jun 5 at 2:23
  • @bzhu I'm sorry: how exactly did it "not work"? What happened, and what did you expect to happen? You may want to ask a new question and refer to this answer saying, "I tried this but...".
    – LSerni
    Jun 5 at 7:11
  • I mean the process some_long_run_commands on the server can exit , after the ssh host -t './some_long_run_commands&' exit locally
    – bzhu
    Jun 6 at 10:12
  • @bzhu I'm not sure what the problem is...? I have tested this and what happens is that the local ssh exits, and the remote process keeps running until it terminates. You might want to intercept stderr on the launch and reap it via ssh, in case the remote script does not start (e.g. because its wd is not what you think, and ./command finds nothing).
    – LSerni
    Jun 8 at 7:57
4

The Linux screen command contained in most distros can daemonize a shell script. I use it often. Here's a quick example to start, list, and quit a detached screen session...

# screen -dmS Session_Name  bash -c "while true; do date; sleep 60; done"

# screen -ls
There are screens on:
        8534.Session_Name       (04/04/2018 08:46:27 PM)        (Detached)

# screen -S Session_Name -X quit
1
  • 4
    screen doesn't deamonize shell script. It just run they in distinguished terminal and can detach from this terminal (like unplug keyboard from PC) without closing session. So, the program running in detached terminal is running in background. Therefore - detach pass program in background. Apr 4, 2018 at 21:17

You must log in to answer this question.

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