122

I am using an init script to run a simple process, which is started with:

start-stop-daemon --start --quiet --chuid $DAEMONUSER    \
    --make-pidfile --pidfile $PIDFILE --background       \
    --exec $DAEMON $DAEMON_ARGS

The process called $DAEMON usually prints log information to its standard output. As far as I can tell this data is not being stored anywhere.

I would like to write or append the stdout of $DAEMON to a file somewhere.

The only solution I know is to tell start-stop-daemon to call a shellscript instead of $DAEMON directly; the script then calls $DAEMON and writes to the logfile. But that requires an extra script which, like modifying the daemon itself, seems the wrong way to solve such a common task.

joeytwiddle
  • 24,338
  • 11
  • 107
  • 91

11 Answers11

130

To expand on ypocat's answer, since it won't let me comment:

start-stop-daemon --start --quiet --chuid $DAEMONUSER    \
 --make-pidfile --pidfile $PIDFILE --background       \
 --startas /bin/bash -- -c "exec $DAEMON $DAEMON_ARGS > /var/log/some.log 2>&1"

Using exec to run the daemon allows stop to correctly stop the child process instead of just the bash parent.

Using --startas instead of --exec ensures that the process will be correctly detected by its pid and won't erroneously start multiple instances of the daemon if start is called multiple times. Otherwise, start-stop-daemon will look for a /bin/bash process and ignore the actual child process running the daemon.

falconepl
  • 398
  • 1
  • 3
  • 15
stormbeta
  • 1,424
  • 1
  • 10
  • 3
  • 2
    This is a far better solution than the one of @ypocat mainly because shutting the daemon down again by replacing `--start` with `--stop` actually works. – aef May 15 '14 at 14:16
  • I tried running this command from rc.local instead of init.d... I dont seem to be getting the same results. However when running it from a shell through SSH it works like a charm! – nemo Jul 03 '14 at 00:36
  • Thanks mate! Now the pidfile refers to the daemon process itselt, not the Bash parent process! – joonas.fi Jul 12 '14 at 12:27
  • 1
    How would the accompanying `start-stop-daemon --test (...)` look like? – Abdull Feb 12 '16 at 15:32
  • Does this overwrite the file `/var/log/some.log` or append to it? – MattCochrane Sep 23 '16 at 07:54
  • @stormbeta you're using --chuid and -c in the same command. is that right? also, if how can i make sure the process is run as a non root user? this command only runs the process as root – Ubuntuser Mar 23 '17 at 09:24
  • brilliant, your method enlightened me. – Meow Oct 21 '17 at 12:25
  • 2
    @MattClimbs It overwrites the file after every start. use `>>` instead of `>` for appending. – Meow Oct 21 '17 at 12:27
  • 2
    Before you freak out (like me) because your log was empty, be aware this is buffered! You can use "exec stdbuf -oL -eL $DAEMON $DAEMONARGS > $LOGFILE 2>&1" to force the output to flush each line (from https://blog.lanyonm.org/articles/2015/01/11/raspberry-pi-init-script-python.html ) – piers7 Dec 06 '17 at 14:51
47

You need to do:

start-stop-daemon --start --quiet --chuid $DAEMONUSER    \
    --make-pidfile --pidfile $PIDFILE --background       \
    --exec /bin/bash -- -c "$DAEMON $DAEMON_ARGS > /var/log/some.log 2>&1"

Also if you use --chuid or --user, make sure the user can write to /var/log or the existing /var/log/some.log. The best way is to have that user own a /var/log/subdir/ though.

vidstige
  • 11,161
  • 7
  • 58
  • 95
youurayy
  • 1,499
  • 1
  • 15
  • 11
  • 1
    Fantastic, thank you ypocat. Today, as well as saving the log, I needed to run a non-binary script which --exec does not allow but your trick works around! – joeytwiddle Oct 11 '12 at 19:04
  • 8
    The down side ... stopping the service kills bash, but not the child process bash started! (In my case, DAEMON=coffee). – joeytwiddle Oct 12 '12 at 14:40
  • 1
    I worked around that by killing all child processes of the bash process at the top of do_stop. `bashPID=$(cat $PIDFILE); [ -n "$bashPID" ] && pkill -P "$bashPID"` – joeytwiddle Nov 12 '12 at 05:50
  • 1
    I used touch and chown in the initscript, to ensure the process will be able to write to the logfile. – joeytwiddle Nov 20 '12 at 15:22
  • 5
    Good to know, and the `pkill` solution too. Wondering what would `... -c "exec $DAEMON..."` (adding the "exec") do. Don't have this on the plate right now so can't try it. – youurayy Nov 20 '12 at 20:14
  • Btw. in my case the original works OK, I'm using to start/stop elasticsearch with it (a java app). I guess it depends how the app is handling signals. – youurayy Nov 21 '12 at 14:07
  • 12
    @ypocat I just verified that it works with -c "exec $DAEMON...". This means no pkill hacks required. – overthink Nov 23 '12 at 16:09
  • Using this method, will bash always keep the file descriptor for the log file open? Will this work with logrotate and/or is there any way to keep the log file from growing forever without restarting the daemon? – nairbv Jan 21 '13 at 20:32
  • @Brian, I think if you need log rotation, you be better off piping the output to syslog or some other daemon that will handle that for you. – Harvey Mar 26 '13 at 20:10
  • @Harvey, sure, but I don't always want to syslog... I might want a separate log file containing just the output of one service in a location of my choosing. – nairbv Mar 26 '13 at 21:27
  • @Brian, there are syslogs that can do this for you (divert different incoming log messages to different files). I think there are also some logging utilities that do this as well where you pipe your output to the utility and it will rotate, etc. like syslog. – Harvey Mar 29 '13 at 20:57
  • @ypocat, be careful if you use a named pipe instead of a logfile. I used that trick to do `mkfifo pipe; logger < pipe &; start-stop... sh... -c "exec ... > pipe 2>&1"; rm pipe`. It's a nice trick, but I found that the `rm pipe` will on some systems execute before the executed daemon opens the pipe. So a slight delay is required. I can't find the other question I got the pipe idea from, but that combined with your answer helped me. Thanks. – Harvey Mar 29 '13 at 21:02
  • @Brian You can rotate the log using logrotated's `copytruncate` option to empty the file. This is not atomic, so carries a small risk of losing some log lines. – joeytwiddle Jul 02 '13 at 16:29
  • 1
    I think the answer of @stormbeta is a better approach. Even if this might work in some situation, the approach of ypocat never worked for me in any case. – aef May 15 '14 at 14:20
41

It seems you should be able to use now the --no-close parameter when starting start-stop-daemon to capture the daemon output. This new feature is available in the dpkg package since version 1.16.5 on Debian:

Add new --no-close option to disable closing fds on --background.

This enabled the caller to see process messages for debugging purposes, or to be able to redirect file descriptors to log files, syslog or similar.

Stéphane
  • 3,643
  • 1
  • 28
  • 27
  • 8
    It's a shame it's not available in Ubuntu 12.04 :( – Leon Radley Jan 25 '13 at 13:11
  • I can't seem to get that --no-close to work... the output is still going to the shell that i'm executing the init.d script from :( – stantonk Mar 17 '13 at 01:05
  • +1 Works perfectly on Debian squeeze with a daemonized node.js service. – speakr Aug 05 '14 at 12:05
  • 2
    @stantonk Did you also pipe stdout/stderr to a file? The complete command line looks like follows. And make sure that the logfile can be written by user $USER: start-stop-daemon --start --chuid $USER --pidfile $PIDFILE --background --no-close --make-pidfile --exec $DAEMON -- $DAEMONARGS >> /var/log/xxxxx.log 2>&1 – nharrer Aug 07 '14 at 13:26
  • 1
    This is not available with the openrc `start-stop-daemon`. However the openrc version has the `-1` and `-2` options to redirect the stdout and stderr respectively. – little-dude Apr 21 '16 at 14:50
  • This saved my life. Thanks! – Avision Apr 17 '18 at 08:53
  • Awesome! Thanks! – Magnus Feb 26 '19 at 23:56
14

With openrc (which is the default on gentoo or alpine linux for instance) start-stop-daemon has the -1 and -2 options:

-1, --stdout Redirect stdout to file

-2, --stderr Redirect stderr to file

So you can just write:

start-stop-daemon --start --quiet --chuid $DAEMONUSER    \
    --make-pidfile --pidfile $PIDFILE --background       \
    --exec $DAEMON $DAEMON_ARGS -1 $LOGFILE -2 $LOGFILE
Community
  • 1
  • 1
little-dude
  • 1,336
  • 2
  • 15
  • 28
10

It is not too hard to capture daemon's output and save it to file:

start-stop-daemon --start --background \
  --pidfile $PIDFILE --make-pidfile \
  --chuid $DAEMON_USER \
  --startas $DAEMON --no-close \
  -- $DAEMON_ARGS >> $LOGFILE 2>&1

However this solution may be suboptimal for logrotate.

It might be better to capture output to syslog. On Debian this would match behaviour of systemd services. The following straightforward attempt to rewrite the above example is wrong because it leaves behind two parent-less ("zombie") processes (logger and daemon) after stopping the daemon because start-stop-daemon terminates only its child but not all descendants:

## Do not use this!
start-stop-daemon --start --background \
  --pidfile $PIDFILE --make-pidfile \
  --chuid $DAEMON_USER \
  --startas /bin/sh \
  -- -c """exec $DAEMON $DAEMON_ARGS | /usr/bin/logger --tag $NAME"""

To make it work we need a wrapper that terminates its children upon receiving SIGTERM from start-stop-daemon. There are some:

duende:
start-stop-daemon --start --background \
  --pidfile $PIDFILE \
  --startas /usr/sbin/duende \
  -- --pid $PIDFILE --chroot=/ --uid 65534 --ident $NAME \
  /bin/su --login $DAEMON_USER --shell /bin/sh --command """exec ${DAEMON} $DAEMON_ARGS"""

Note: uid=65534 is a user nobody.

Pros: it works and it is relatively easy.
Cons: 4 processes (supervisor duende, its fork with dropped privileges (logger), su and daemon itself); mandatory --chroot; If daemon terminates right away (e.g. invalid command) status_of_proc -p $PIDFILE "$DAEMON" "$NAME" report it as started successfully.

daemon:
start-stop-daemon --start --pidfile $PIDFILE \
  --startas /usr/bin/daemon \
  -- --noconfig --name $NAME --stderr=syslog.info --stdout=syslog.info \
  -- /bin/su --login $DAEMON_USER --shell /bin/sh --command """exec $DAEMON $DAEMON_ARGS"""

Pros: 3 processes (supervisor daemon, su and daemon itself).
Cons: Difficult to manage $PIDFILE due to confusing daemon's command line options; If daemon terminates right away (e.g. invalid command) status_of_proc -p $PIDFILE "$DAEMON" "$NAME" report it as started successfully.

pipexec (the winner):

start-stop-daemon --start --background \
  --pidfile $PIDFILE --make-pidfile \
  --chuid $DAEMON_USER \
  --startas /usr/bin/pipexec -- -k \
   -- [ D $DAEMON $DAEMON_ARGS ] [ L /usr/bin/logger --tag $NAME ] '{D:2>D:1}' '{D:1>L:0}'

Pros: 3 processes (supervisor pipexec, logger and daemon itself); If daemon terminates right away (e.g. invalid command) status_of_proc -p $PIDFILE "$DAEMON" "$NAME" correctly report failure.
Cons: none.

This is the winner -- the easiest, neat solution that seems to be working well.

Onlyjob
  • 5,052
  • 1
  • 30
  • 34
  • To use a separate file to log into, see Ansgar Wiechers answer under https://stackoverflow.com/questions/15045946/write-to-custom-log-file-from-a-bash-script – Adrian Zaugg Jan 03 '21 at 21:25
8

Usually start-stop-daemon closes the standard file descriptors when running in the background. From the man page of start-stop-daemon:

-C, --no-close
Do not close any file descriptor when forcing the daemon into the background. Used for debugging purposes to see the process output, or to redirect file descriptors to log the process output. Only relevant when using --background.

This one worked for me:

    start-stop-daemon -b -C -o -c \ 
         $DAEMON_USER -S -x $DAEMON > $DAEMON_LOG 2>&1
4

Quoting an old mailing list:

https://lists.ubuntu.com/archives/ubuntu-uk/2005-June/000037.html

An easy -- and if you want to use start-stop-daemon perhaps the only -- way around it is to create a small script containing:

#!/bin/sh
exec /home/boinc/boinc/boinc > /home/boinc/log/boinc.log

and then use that script as the argument to start-stop-daemon.

Perhaps the real question however is whether it is really necessary to use start-stop-daemon in the first place?

joeytwiddle
  • 24,338
  • 11
  • 107
  • 91
3

I'm not sure if "$DAEMON $DAEMON_ARGS > /var/log/some.log 2>&1" will ever close the file descriptor for the log file... which means if your daemon runs forever, I'm not sure that logrotate or other mechanisms for cleaning up disk space would work. Since it's > instead of >>, the suggested command would also truncate existing logs on restart. If you want to see why the daemon crashed, and it restarts automatically, that might not be very helpful.

Another option might be "$DAEMON | logger". logger is a command that will log to syslog (/var/log/messages). If you need stderr too, I think you could use "$DAEMON 1>&2 | logger"

nairbv
  • 3,618
  • 1
  • 21
  • 25
  • You are correct, using `>>` is generally more appropriate for daemons, although it implies you should now create a logrotate rule! – joeytwiddle Nov 04 '13 at 05:55
  • As for disk-space, methods which **truncate** the file will get the space back immediately (at least under ext filesystems). But beware methods which simply **delete** a file which is still being written to: the space will not be reclaimed until the handle is released, and you can no longer find the file node to manually truncate it! – joeytwiddle Nov 04 '13 at 05:58
  • @joeytwiddle part of my point here is that there are situations where logrotate will fail to rotate logs if the file handle is never closed. – nairbv Nov 04 '13 at 18:12
  • `--no-close ... | logger` does not work for me (Debian 7.3, start-stop-daemon 1.16.12). The start-stop-daemon script doesn't come back, though /var/log/messages is filled :-). I tried it with and without `1>&2`. – hgoebl Jan 30 '14 at 15:16
  • hgoebl you need to have the "cmd | logger" expression in quotes, so the interpreter will know it's "cmd" you are piping to logger, not the start-stop-daemon expression. – Wexxor Aug 19 '15 at 22:01
2

there is an option --no-close for start-stop-daemon, which means "don't close any fd when it's run background."

start-stop-daemon --start --quiet --chuid $DAEMONUSER    \
    --make-pidfile --pidfile $PIDFILE --background       \
    --exec $DAEMON --no-close \
    -- $DAEMON_ARGS > /var/log/some.log 2>&1

this redirect the stdout/stderr of process start-stop-daemon to the file. and your executable inherits stdout/stderr from its parent process start-stop-daemon.

Zim
  • 61
  • 4
2

Assuming it's bash (although some other shells may allow this as well), the line:

exec >>/tmp/myDaemon.log

will send all future standard output to that file. That's because exec without a program name just does some redirection magic. From the bash man page:

If command is not specified, any redirections take effect in the current shell.

Management of said file is another issue of course.

paxdiablo
  • 772,407
  • 210
  • 1,477
  • 1,841
  • can you clarify where this line is supposed to be placed? Right after the `start-stop-daemon` line which the initial question mentioned? – Abdull Feb 15 '16 at 23:48
1

How about:

sudo -u myuser -i start-stop-daemon ...
jakub.piasecki
  • 536
  • 1
  • 5
  • 12