Welcome to little lamb

Code » anopa » commit 9d11a74

Add --log-file (to replace --double-output)

author Olivier Brunel
2018-01-19 19:54:36 UTC
committer Olivier Brunel
2018-01-21 19:41:52 UTC
parent 70e51fab97ab1c69fa4678dace7442b5eedadaa9

Add --log-file (to replace --double-output)

Works somewhat like --double-output, except it can be any file or fd,
it doesn't get ANSI stuff (even if it is a tty), and it doesn't mess
with stdout/stderr, which remain as they should be.

doc/aa-chroot.pod +8 -1
doc/aa-ctty.pod +8 -1
doc/aa-echo.pod +9 -1
doc/aa-enable.pod +11 -3
doc/aa-hiercopy.pod +8 -1
doc/aa-incmdline.pod +8 -1
doc/aa-kill.pod +8 -1
doc/aa-mount.pod +9 -2
doc/aa-pivot.pod +8 -1
doc/aa-reboot.pod +8 -1
doc/aa-reset.pod +9 -1
doc/aa-service.pod +8 -1
doc/aa-setready.pod +8 -1
doc/aa-start.pod +9 -2
doc/aa-status.pod +10 -2
doc/aa-stop.pod +9 -2
doc/aa-terminate.pod +8 -1
doc/aa-test.pod +9 -2
doc/aa-tty.pod +8 -1
doc/aa-umount.pod +8 -1
src/anopa/aa-enable.c +7 -1
src/anopa/aa-reset.c +7 -1
src/anopa/aa-start.c +7 -1
src/anopa/aa-status.c +7 -1
src/anopa/aa-stop.c +7 -1
src/include/anopa/output.h +2 -0
src/libanopa/output.c +69 -0
src/utils/aa-chroot.c +8 -2
src/utils/aa-ctty.c +8 -2
src/utils/aa-echo.c +7 -1
src/utils/aa-hiercopy.c +7 -1
src/utils/aa-incmdline.c +7 -1
src/utils/aa-kill.c +7 -1
src/utils/aa-mount.c +8 -2
src/utils/aa-pivot.c +8 -2
src/utils/aa-reboot.c +8 -2
src/utils/aa-service.c +8 -2
src/utils/aa-setready.c +8 -2
src/utils/aa-terminate.c +7 -1
src/utils/aa-test.c +8 -2
src/utils/aa-tty.c +7 -1
src/utils/aa-umount.c +8 -2

diff --git a/doc/aa-chroot.pod b/doc/aa-chroot.pod
index 1a5db74..89686a3 100644
--- a/doc/aa-chroot.pod
+++ b/doc/aa-chroot.pod
@@ -4,7 +4,7 @@ aa-chroot - Execute command within given chroot jail
 
 =head1 SYNOPSIS
 
-B<aa-chroot> [B<-D>] I<NEWROOT> I<COMMAND> [I<ARG...>]
+B<aa-chroot> [B<-D>] [B<-O> I<FILE|FD>] I<NEWROOT> I<COMMAND> [I<ARG...>]
 
 =head1 OPTIONS
 
@@ -21,6 +21,13 @@ shown on console and logged.
 
 Show help screen and exit.
 
+=item B<-O, --log-file> I<FILE|FD>
+
+Will duplicate all output (everything written to stdout or stderr) to the given
+file or file descriptor. I<FILE|FD> can either be a (previously opened for
+writing) file descriptor (must be > 2), or a file which will then be opened in
+append mode.
+
 =item B<-V, --version>
 
 Show version information and exit.
diff --git a/doc/aa-ctty.pod b/doc/aa-ctty.pod
index 217eefe..b68225c 100644
--- a/doc/aa-ctty.pod
+++ b/doc/aa-ctty.pod
@@ -4,7 +4,7 @@ aa-ctty - Helper for execline script to set controlling terminal
 
 =head1 SYNOPSIS
 
-B<aa-ctty> [B<-D>] [B<-f> I<FD>] [B<-s>] I<PROG...>
+B<aa-ctty> [B<-D>] [B<-O> I<FILE|FD>] [B<-f> I<FD>] [B<-s>] I<PROG...>
 
 =head1 OPTIONS
 
@@ -25,6 +25,13 @@ Use file descriptor I<FD> as terminal; Defaults to stdin (0).
 
 Show help screen and exit.
 
+=item B<-O, --log-file> I<FILE|FD>
+
+Will duplicate all output (everything written to stdout or stderr) to the given
+file or file descriptor. I<FILE|FD> can either be a (previously opened for
+writing) file descriptor (must be > 2), or a file which will then be opened in
+append mode.
+
 =item B<-s, --steal>
 
 Steal controlling terminal if already controlling terminal of a different
diff --git a/doc/aa-echo.pod b/doc/aa-echo.pod
index ec4f00a..ebd9084 100644
--- a/doc/aa-echo.pod
+++ b/doc/aa-echo.pod
@@ -4,7 +4,8 @@ aa-echo - Shows a message
 
 =head1 SYNOPSIS
 
-B<aa-echo> [B<-D>] [B<-B>] [B<-T> | B<-t> | B<-w> | B<-e> | B<-n>] I<MESSAGE...>
+B<aa-echo> [B<-D>] [B<-O> I<FILE|FD>]
+[B<-B>] [B<-T> | B<-t> | B<-w> | B<-e> | B<-n>] I<MESSAGE...>
 
 =head1 OPTIONS
 
@@ -36,6 +37,13 @@ Show I<MESSAGE...> as regular text, i.e. without any prefix or color. This can
 be used for simple echo, but make sure to see how arguments making up
 I<MESSAGE...> are processed.
 
+=item B<-O, --log-file> I<FILE|FD>
+
+Will duplicate all output (everything written to stdout or stderr) to the given
+file or file descriptor. I<FILE|FD> can either be a (previously opened for
+writing) file descriptor (must be > 2), or a file which will then be opened in
+append mode.
+
 =item B<-T, --title>
 
 Show I<MESSAGE...> as a main title. A green "==> " prefix will be printed before
diff --git a/doc/aa-enable.pod b/doc/aa-enable.pod
index a69af08..85d89d3 100644
--- a/doc/aa-enable.pod
+++ b/doc/aa-enable.pod
@@ -4,9 +4,10 @@ aa-enable - Enable services, i.e. copy servicedirs to repodir
 
 =head1 SYNOPSIS
 
-B<aa-enable> [B<-D>] [B<-r> I<REPODIR>] [B<-c> I<CRASH>] [B<-f> I<FINISH>]
-[B<-k> I<SERVICE>] [B<-S> I<SOURCEDIR>] [B<-s> I<SOURCEDIR>] [B<-l> I<LISTDIR>]
-[B<-N>] [B<-W>] [B<-u>] [B<-a>] [B<--no-supervise>] [B<-q>] [I<SERVICE...>]
+B<aa-enable> [B<-D>] [B<-O> I<FILE|FD>] [B<-r> I<REPODIR>] [B<-c> I<CRASH>]
+[B<-f> I<FINISH>] [B<-k> I<SERVICE>] [B<-S> I<SOURCEDIR>] [B<-s> I<SOURCEDIR>]
+[B<-l> I<LISTDIR>] [B<-N>] [B<-W>] [B<-u>] [B<-a>] [B<--no-supervise>] [B<-q>]
+[I<SERVICE...>]
 
 =head1 OPTIONS
 
@@ -68,6 +69,13 @@ I</etc/anopa/listdirs/>
 Don't auto-enable any services listed under directory I<needs> of a service
 being (auto-)enabled.
 
+=item B<-O, --log-file> I<FILE|FD>
+
+Will duplicate all output (everything written to stdout or stderr) to the given
+file or file descriptor. I<FILE|FD> can either be a (previously opened for
+writing) file descriptor (must be > 2), or a file which will then be opened in
+append mode.
+
 =item B<-q, --quiet>
 
 Don't print enabled service names.
diff --git a/doc/aa-hiercopy.pod b/doc/aa-hiercopy.pod
index 9006088..f072ee0 100644
--- a/doc/aa-hiercopy.pod
+++ b/doc/aa-hiercopy.pod
@@ -4,7 +4,7 @@ aa-hiercopy - Copy a directory structure recursively
 
 =head1 SYNOPSIS
 
-B<aa-hiercopy> [B<-D>] I<SOURCE> I<DESTINATION>
+B<aa-hiercopy> [B<-D>] [B<-O> I<FILE|FD>] I<SOURCE> I<DESTINATION>
 
 =head1 OPTIONS
 
@@ -21,6 +21,13 @@ shown on console and logged.
 
 Show help screen and exit.
 
+=item B<-O, --log-file> I<FILE|FD>
+
+Will duplicate all output (everything written to stdout or stderr) to the given
+file or file descriptor. I<FILE|FD> can either be a (previously opened for
+writing) file descriptor (must be > 2), or a file which will then be opened in
+append mode.
+
 =item B<-V, --version>
 
 Show version information and exit.
diff --git a/doc/aa-incmdline.pod b/doc/aa-incmdline.pod
index 2cfc832..160ea3d 100644
--- a/doc/aa-incmdline.pod
+++ b/doc/aa-incmdline.pod
@@ -4,7 +4,7 @@ aa-incmdline - Helper to parse kernel command line
 
 =head1 SYNOPSIS
 
-B<aa-incmdline> [B<-D>] [B<-q>] [B<-f> I<FILE>] [B<-r>] [B<-s>] I<NAME>
+B<aa-incmdline> [B<-D>] [B<-O> I<FILE|FD>] [B<-q>] [B<-f> I<FILE>] [B<-r>] [B<-s>] I<NAME>
 
 =head1 OPTIONS
 
@@ -25,6 +25,13 @@ Read command line from I<FILE> instead of I</proc/cmdline>
 
 Show help screen and exit.
 
+=item B<-O, --log-file> I<FILE|FD>
+
+Will duplicate all output (everything written to stdout or stderr) to the given
+file or file descriptor. I<FILE|FD> can either be a (previously opened for
+writing) file descriptor (must be > 2), or a file which will then be opened in
+append mode.
+
 =item B<-q, --quiet>
 
 Do not print value (if any) on stdout
diff --git a/doc/aa-kill.pod b/doc/aa-kill.pod
index ee61ba9..7f25ad5 100644
--- a/doc/aa-kill.pod
+++ b/doc/aa-kill.pod
@@ -4,7 +4,7 @@ aa-kill - Send signals to (almost) all processes
 
 =head1 SYNOPSIS
 
-B<aa-kill> [B<-D>] [B<-u>] [B<-t>] [B<-k>] [B<-s>]
+B<aa-kill> [B<-D>] [B<-O> I<FILE|FD>] [B<-u>] [B<-t>] [B<-k>] [B<-s>]
 
 =head1 OPTIONS
 
@@ -25,6 +25,13 @@ Show help screen and exit.
 
 Send SIGKILL
 
+=item B<-O, --log-file> I<FILE|FD>
+
+Will duplicate all output (everything written to stdout or stderr) to the given
+file or file descriptor. I<FILE|FD> can either be a (previously opened for
+writing) file descriptor (must be > 2), or a file which will then be opened in
+append mode.
+
 =item B<-s, --skip>
 
 Skip processes whose argv[0][0] is '@' This can be useful to ignore some
diff --git a/doc/aa-mount.pod b/doc/aa-mount.pod
index 11d5ff9..43de412 100644
--- a/doc/aa-mount.pod
+++ b/doc/aa-mount.pod
@@ -4,8 +4,8 @@ aa-mount - Mount a filesystem
 
 =head1 SYNOPSIS
 
-B<aa-mount> [B<-D>] [B<-B> | B<-M>] [B<-r> | B<-w>] [B<-d>] [B<-t> I<FSTYPE>]
-[B<-o> I<OPTIONS>] I<DEVICE> I<MOUNTPOINT>
+B<aa-mount> [B<-D>] [B<-O> I<FILE|FD>] [B<-B> | B<-M>] [B<-r> | B<-w>] [B<-d>]
+[B<-t> I<FSTYPE>] [B<-o> I<OPTIONS>] I<DEVICE> I<MOUNTPOINT>
 
 =head1 OPTIONS
 
@@ -42,6 +42,13 @@ Move subtree specified as I<DEVICE> to I<MOUNTPOINT> So the content will be
 atomically moved from its old location (I<DEVICE>) into the new one
 (I<MOUNTPOINT>).
 
+=item B<-O, --log-file> I<FILE|FD>
+
+Will duplicate all output (everything written to stdout or stderr) to the given
+file or file descriptor. I<FILE|FD> can either be a (previously opened for
+writing) file descriptor (must be > 2), or a file which will then be opened in
+append mode.
+
 =item B<-o, --options> I<OPTIONS>
 
 Set I<OPTIONS> as mount options to be used. They will be combined with any other
diff --git a/doc/aa-pivot.pod b/doc/aa-pivot.pod
index 811359a..48c3638 100644
--- a/doc/aa-pivot.pod
+++ b/doc/aa-pivot.pod
@@ -4,7 +4,7 @@ aa-pivot - Pivot root directory
 
 =head1 SYNOPSIS
 
-B<aa-pivot> [B<-D>] I<NEWROOT> I<OLDROOT>
+B<aa-pivot> [B<-D>] [B<-O> I<FILE|FD>] I<NEWROOT> I<OLDROOT>
 
 =head1 OPTIONS
 
@@ -21,6 +21,13 @@ shown on console and logged.
 
 Show help screen and exit.
 
+=item B<-O, --log-file> I<FILE|FD>
+
+Will duplicate all output (everything written to stdout or stderr) to the given
+file or file descriptor. I<FILE|FD> can either be a (previously opened for
+writing) file descriptor (must be > 2), or a file which will then be opened in
+append mode.
+
 =item B<-V, --version>
 
 Show version information and exit.
diff --git a/doc/aa-reboot.pod b/doc/aa-reboot.pod
index 1cff94f..8d8621a 100644
--- a/doc/aa-reboot.pod
+++ b/doc/aa-reboot.pod
@@ -4,7 +4,7 @@ aa-reboot - Reboots, powers off or halts the machine instantly
 
 =head1 SYNOPSIS
 
-B<aa-reboot> [B<-D>] B<-r> | B<-p> | B<-H>
+B<aa-reboot> [B<-D>] [B<-O> I<FILE|FD>] B<-r> | B<-p> | B<-H>
 
 =head1 OPTIONS
 
@@ -25,6 +25,13 @@ Halts the machine.
 
 Show help screen and exit.
 
+=item B<-O, --log-file> I<FILE|FD>
+
+Will duplicate all output (everything written to stdout or stderr) to the given
+file or file descriptor. I<FILE|FD> can either be a (previously opened for
+writing) file descriptor (must be > 2), or a file which will then be opened in
+append mode.
+
 =item B<-p, --poweroff>
 
 Powers off the machine.
diff --git a/doc/aa-reset.pod b/doc/aa-reset.pod
index 5d55f32..95e5d9c 100644
--- a/doc/aa-reset.pod
+++ b/doc/aa-reset.pod
@@ -4,7 +4,8 @@ aa-reset - Reset status of one-shot services
 
 =head1 SYNOPSIS
 
-B<aa-reset> [B<-D>] [B<-r> I<repodir>] [B<-A> | B<-a> | B<-o>] [I<service...>]
+B<aa-reset> [B<-D>] [B<-O> I<FILE|FD>] [B<-r> I<repodir>] [B<-A> | B<-a> |
+B<-o>] [I<service...>]
 
 =head1 OPTIONS
 
@@ -29,6 +30,13 @@ shown on console and logged.
 
 Show help screen and exit.
 
+=item B<-O, --log-file> I<FILE|FD>
+
+Will duplicate all output (everything written to stdout or stderr) to the given
+file or file descriptor. I<FILE|FD> can either be a (previously opened for
+writing) file descriptor (must be > 2), or a file which will then be opened in
+append mode.
+
 =item B<-o, --stopped>
 
 Reset to "Stopped"
diff --git a/doc/aa-service.pod b/doc/aa-service.pod
index c3ead1f..d9887bc 100644
--- a/doc/aa-service.pod
+++ b/doc/aa-service.pod
@@ -4,7 +4,7 @@ aa-service - Helper for execline script to get service name/instance
 
 =head1 SYNOPSIS
 
-B<aa-service> [B<-D>] [B<-l>] I<PROG...>
+B<aa-service> [B<-D>] [B<-O> I<FILE|FD>] [B<-l>] I<PROG...>
 
 =head1 OPTIONS
 
@@ -26,6 +26,13 @@ Show help screen and exit.
 To use for a service logger run script, i.e. when current folder will be
 subfolder I<log> of the servicedir.
 
+=item B<-O, --log-file> I<FILE|FD>
+
+Will duplicate all output (everything written to stdout or stderr) to the given
+file or file descriptor. I<FILE|FD> can either be a (previously opened for
+writing) file descriptor (must be > 2), or a file which will then be opened in
+append mode.
+
 =item B<-V, --version>
 
 Show version information and exit.
diff --git a/doc/aa-setready.pod b/doc/aa-setready.pod
index 014addc..bafa184 100644
--- a/doc/aa-setready.pod
+++ b/doc/aa-setready.pod
@@ -4,7 +4,7 @@ aa-setready - Set a service (un)ready
 
 =head1 SYNOPSIS
 
-B<aa-setready> [B<-D>] [B<-U> | B<-N>] I<SERVICEDIR>
+B<aa-setready> [B<-D>] [B<-O> I<FILE|FD>] [B<-U> | B<-N>] I<SERVICEDIR>
 
 =head1 OPTIONS
 
@@ -25,6 +25,13 @@ Show help screen and exit.
 
 Mark the service unready and emit event 'N' on I<event> fifodir.
 
+=item B<-O, --log-file> I<FILE|FD>
+
+Will duplicate all output (everything written to stdout or stderr) to the given
+file or file descriptor. I<FILE|FD> can either be a (previously opened for
+writing) file descriptor (must be > 2), or a file which will then be opened in
+append mode.
+
 =item B<-U, --ready>
 
 Mark the service ready and emit event 'U' on I<event> fifodir. This is the
diff --git a/doc/aa-start.pod b/doc/aa-start.pod
index c237114..7fea9cd 100644
--- a/doc/aa-start.pod
+++ b/doc/aa-start.pod
@@ -4,8 +4,8 @@ aa-start - Start services
 
 =head1 SYNOPSIS
 
-B<aa-start> [B<-D>] [B<-r> I<repodir>] [B<-l> I<listdir>] [B<-W>]
-[B<-t> I<timeout>] [B<-n>] [B<-v>] [I<service...>]
+B<aa-start> [B<-D>] [B<-O> I<FILE|FD>] [B<-r> I<repodir>] [B<-l> I<listdir>]
+[B<-W>] [B<-t> I<timeout>] [B<-n>] [B<-v>] [I<service...>]
 
 =head1 OPTIONS
 
@@ -37,6 +37,13 @@ Only print the name of the services, but do not start anything.
 Specify a second time to list services that need to be up instead of those that
 would be started, i.e. include those already up.
 
+=item B<-O, --log-file> I<FILE|FD>
+
+Will duplicate all output (everything written to stdout or stderr) to the given
+file or file descriptor. I<FILE|FD> can either be a (previously opened for
+writing) file descriptor (must be > 2), or a file which will then be opened in
+append mode.
+
 =item B<-r, --repodir> I<dir>
 
 Use I<dir> as repository directory. This is where servicedirs will be looked
diff --git a/doc/aa-status.pod b/doc/aa-status.pod
index da41546..b559e9d 100644
--- a/doc/aa-status.pod
+++ b/doc/aa-status.pod
@@ -4,8 +4,9 @@ aa-status - Show status of services
 
 =head1 SYNOPSIS
 
-B<aa-status> [B<-D>] [B<-r> I<repodir>] [B<-a>] [B<-f> I<filter>] [B<-L>]
-[B<-n>] [B<-l> I<listdir>] [B<-s> I<sort>] [B<-R>] [B<-N>] [I<service...>]
+B<aa-status> [B<-D>] [B<-O> I<FILE|FD>] [B<-r> I<repodir>] [B<-a>] [B<-f>
+I<filter>] [B<-L>] [B<-n>] [B<-l> I<listdir>] [B<-s> I<sort>] [B<-R>] [B<-N>]
+[I<service...>]
 
 =head1 OPTIONS
 
@@ -53,6 +54,13 @@ Sort services by name. Default is by time, see B<--sort> for more.
 
 Only show service names, one per line.
 
+=item B<-O, --log-file> I<FILE|FD>
+
+Will duplicate all output (everything written to stdout or stderr) to the given
+file or file descriptor. I<FILE|FD> can either be a (previously opened for
+writing) file descriptor (must be > 2), or a file which will then be opened in
+append mode.
+
 =item B<-R, --reverse>
 
 Reverse sort order.
diff --git a/doc/aa-stop.pod b/doc/aa-stop.pod
index a97f39d..bea65a0 100644
--- a/doc/aa-stop.pod
+++ b/doc/aa-stop.pod
@@ -4,8 +4,8 @@ aa-stop - Stop services
 
 =head1 SYNOPSIS
 
-B<aa-stop> [B<-D>] [B<-r> I<repodir>] [B<-l> I<listdir>] [B<-a>]
-[B<-k> I<service>] [B<-t> I<timeout>] [B<-n>] [B<-v>] [I<service...>]
+B<aa-stop> [B<-D>] [B<-O> I<FILE|FD>] [B<-r> I<repodir>] [B<-l> I<listdir>]
+[B<-a>] [B<-k> I<service>] [B<-t> I<timeout>] [B<-n>] [B<-v>] [I<service...>]
 
 =head1 OPTIONS
 
@@ -52,6 +52,13 @@ I</etc/anopa/listdirs/>
 
 Only print the name of the services, but do not stop anything.
 
+=item B<-O, --log-file> I<FILE|FD>
+
+Will duplicate all output (everything written to stdout or stderr) to the given
+file or file descriptor. I<FILE|FD> can either be a (previously opened for
+writing) file descriptor (must be > 2), or a file which will then be opened in
+append mode.
+
 =item B<-r, --repodir> I<dir>
 
 Use I<dir> as repository directory. This is where servicedirs will be looked
diff --git a/doc/aa-terminate.pod b/doc/aa-terminate.pod
index 1b25eb8..8c7cfef 100644
--- a/doc/aa-terminate.pod
+++ b/doc/aa-terminate.pod
@@ -4,7 +4,7 @@ aa-teminate - Tries to close/unmount everything it can
 
 =head1 SYNOPSIS
 
-B<aa-terminate> [B<-D>] [B<-l>] [B<-a>] [B<-q> | B<-v>]
+B<aa-terminate> [B<-D>] [B<-O> I<FILE|FD>] [B<-l>] [B<-a>] [B<-q> | B<-v>]
 
 =head1 OPTIONS
 
@@ -33,6 +33,13 @@ there are still active mount points, run the loop again performing lazy
 unmounts: making the mount point unavailable for new accesses, and actually
 performing the unmount when the mount point ceases to be busy.
 
+=item B<-O, --log-file> I<FILE|FD>
+
+Will duplicate all output (everything written to stdout or stderr) to the given
+file or file descriptor. I<FILE|FD> can either be a (previously opened for
+writing) file descriptor (must be > 2), or a file which will then be opened in
+append mode.
+
 =item B<-q, --quiet>
 
 Do not output anything (not even warnings of "left overs" when done; See
diff --git a/doc/aa-test.pod b/doc/aa-test.pod
index e212a82..179a3b7 100644
--- a/doc/aa-test.pod
+++ b/doc/aa-test.pod
@@ -4,8 +4,8 @@ aa-test - Test file types
 
 =head1 SYNOPSIS
 
-B<aa-test> [B<-D>] [B<-b> | B<-d> | B<-e> | B<-f> | B<-L> | B<-p> | B<-S> |
-B<-r> | B<-w> | B<-x>] [B<-R> [I<TIMES>]] I<FILE>
+B<aa-test> [B<-D>] [B<-O> I<FILE|FD>] [B<-b> | B<-d> | B<-e> | B<-f> | B<-L> |
+B<-p> | B<-S> | B<-r> | B<-w> | B<-x>] [B<-R> [I<TIMES>]] I<FILE>
 
 =head1 OPTIONS
 
@@ -42,6 +42,13 @@ Show help screen and exit.
 
 Test whether I<FILE> exists and is a symlink
 
+=item B<-O, --log-file> I<FILE|FD>
+
+Will duplicate all output (everything written to stdout or stderr) to the given
+file or file descriptor. I<FILE|FD> can either be a (previously opened for
+writing) file descriptor (must be > 2), or a file which will then be opened in
+append mode.
+
 =item B<-p, --pipe>
 
 Test whether I<FILE> exists and is a named pipe (FIFO)
diff --git a/doc/aa-tty.pod b/doc/aa-tty.pod
index ddbe543..1097407 100644
--- a/doc/aa-tty.pod
+++ b/doc/aa-tty.pod
@@ -4,7 +4,7 @@ aa-tty - Prints the device name of the active tty
 
 =head1 SYNOPSIS
 
-B<aa-tty> [B<-D>]
+B<aa-tty> [B<-D>] [B<-O> I<FILE|FD>]
 
 =head1 OPTIONS
 
@@ -21,6 +21,13 @@ shown on console and logged.
 
 Show help screen and exit.
 
+=item B<-O, --log-file> I<FILE|FD>
+
+Will duplicate all output (everything written to stdout or stderr) to the given
+file or file descriptor. I<FILE|FD> can either be a (previously opened for
+writing) file descriptor (must be > 2), or a file which will then be opened in
+append mode.
+
 =item B<-V, --version>
 
 Show version information and exit.
diff --git a/doc/aa-umount.pod b/doc/aa-umount.pod
index 70ee992..d9d036f 100644
--- a/doc/aa-umount.pod
+++ b/doc/aa-umount.pod
@@ -4,7 +4,7 @@ aa-umount - Unmount a filesystem
 
 =head1 SYNOPSIS
 
-B<aa-umount> [B<-D>] [B<-f> | B<-l>] I<MOUNTPOINT>
+B<aa-umount> [B<-D>] [B<-O> I<FILE|FD>] [B<-f> | B<-l>] I<MOUNTPOINT>
 
 =head1 OPTIONS
 
@@ -30,6 +30,13 @@ Show help screen and exit.
 Perform a lazy unmount: make the mount point unavailable for new accesses, and
 actually perform the unmount when the mount point ceases to be busy.
 
+=item B<-O, --log-file> I<FILE|FD>
+
+Will duplicate all output (everything written to stdout or stderr) to the given
+file or file descriptor. I<FILE|FD> can either be a (previously opened for
+writing) file descriptor (must be > 2), or a file which will then be opened in
+append mode.
+
 =item B<-V, --version>
 
 Show version information and exit.
diff --git a/src/anopa/aa-enable.c b/src/anopa/aa-enable.c
index dfd17b9..3132e31 100644
--- a/src/anopa/aa-enable.c
+++ b/src/anopa/aa-enable.c
@@ -199,6 +199,7 @@ dieusage (int rc)
 {
     aa_die_usage (rc, "[OPTION...] [service...]",
             " -D, --double-output           Enable double-output mode\n"
+            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
             " -r, --repodir DIR             Use DIR as repository directory\n"
             " -S, --reset-source DIR        Reset list of source directories to DIR\n"
             " -s, --source DIR              Add DIR as source directories\n"
@@ -245,6 +246,7 @@ main (int argc, char * const argv[])
             { "skip-down",          required_argument,  NULL,   'k' },
             { "listdir",            required_argument,  NULL,   'l' },
             { "no-needs",           no_argument,        NULL,   'N' },
+            { "log-file",           required_argument,  NULL,   'O' },
             { "quiet",              no_argument,        NULL,   'q' },
             { "repodir",            required_argument,  NULL,   'r' },
             { "reset-source",       required_argument,  NULL,   'S' },
@@ -257,7 +259,7 @@ main (int argc, char * const argv[])
         };
         int c;
 
-        c = getopt_long (argc, argv, "ac:Df:hk:l:Nqr:S:s:uVW", longopts, NULL);
+        c = getopt_long (argc, argv, "ac:Df:hk:l:NO:qr:S:s:uVW", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -294,6 +296,10 @@ main (int argc, char * const argv[])
                 flags &= ~AA_FLAG_AUTO_ENABLE_NEEDS;
                 break;
 
+            case 'O':
+                aa_set_log_file_or_die (optarg);
+                break;
+
             case 'q':
                 quiet = 1;
                 break;
diff --git a/src/anopa/aa-reset.c b/src/anopa/aa-reset.c
index b6993d0..95c0748 100644
--- a/src/anopa/aa-reset.c
+++ b/src/anopa/aa-reset.c
@@ -135,6 +135,7 @@ dieusage (int rc)
 {
     aa_die_usage (rc, "[OPTION...] [service...]",
             " -D, --double-output           Enable double-output mode\n"
+            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
             " -r, --repodir DIR             Use DIR as repository directory\n"
             " -A, --auto                    Automatic mode\n"
             " -a, --started                 Reset to Started\n"
@@ -160,6 +161,7 @@ main (int argc, char * const argv[])
             { "started",            no_argument,        NULL,   'a' },
             { "double-output",      no_argument,        NULL,   'D' },
             { "help",               no_argument,        NULL,   'h' },
+            { "log-file",           required_argument,  NULL,   'O' },
             { "stopped",            no_argument,        NULL,   'o' },
             { "repodir",            required_argument,  NULL,   'r' },
             { "version",            no_argument,        NULL,   'V' },
@@ -167,7 +169,7 @@ main (int argc, char * const argv[])
         };
         int c;
 
-        c = getopt_long (argc, argv, "AaDhor:V", longopts, NULL);
+        c = getopt_long (argc, argv, "AaDhO:or:V", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -187,6 +189,10 @@ main (int argc, char * const argv[])
             case 'h':
                 dieusage (0);
 
+            case 'O':
+                aa_set_log_file_or_die (optarg);
+                break;
+
             case 'o':
                 mode = MODE_STOPPED;
                 break;
diff --git a/src/anopa/aa-start.c b/src/anopa/aa-start.c
index d9a33a2..5ed7d70 100644
--- a/src/anopa/aa-start.c
+++ b/src/anopa/aa-start.c
@@ -222,6 +222,7 @@ dieusage (int rc)
 {
     aa_die_usage (rc, "[OPTION...] [service...]",
             " -D, --double-output           Enable double-output mode\n"
+            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
             " -r, --repodir DIR             Use DIR as repository directory\n"
             " -l, --listdir DIR             Use DIR to list services to start\n"
             " -W, --no-wants                Don't auto-start services from 'wants'\n"
@@ -255,6 +256,7 @@ main (int argc, char * const argv[])
             { "help",               no_argument,        NULL,   'h' },
             { "listdir",            required_argument,  NULL,   'l' },
             { "dry-list",           no_argument,        NULL,   'n' },
+            { "log-file",           required_argument,  NULL,   'O' },
             { "repodir",            required_argument,  NULL,   'r' },
             { "timeout",            required_argument,  NULL,   't' },
             { "version",            no_argument,        NULL,   'V' },
@@ -264,7 +266,7 @@ main (int argc, char * const argv[])
         };
         int c;
 
-        c = getopt_long (argc, argv, "Dhl:nr:t:VvW", longopts, NULL);
+        c = getopt_long (argc, argv, "Dhl:nO:r:t:VvW", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -288,6 +290,10 @@ main (int argc, char * const argv[])
                     mode |= AA_MODE_IS_DRY;
                 break;
 
+            case 'O':
+                aa_set_log_file_or_die (optarg);
+                break;
+
             case 'r':
                 unslash (optarg);
                 path_repo = optarg;
diff --git a/src/anopa/aa-status.c b/src/anopa/aa-status.c
index 446f657..521a0d5 100644
--- a/src/anopa/aa-status.c
+++ b/src/anopa/aa-status.c
@@ -795,6 +795,7 @@ dieusage (int rc)
 {
     aa_die_usage (rc, "[OPTION...] [service...]",
             " -D, --double-output           Enable double-output mode\n"
+            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
             " -r, --repodir DIR             Use DIR as repository directory\n"
             " -l, --listdir DIR             Use DIR to list services to get status of\n"
             " -a, --all                     Show status of all services\n"
@@ -832,6 +833,7 @@ main (int argc, char * const argv[])
             { "list",               no_argument,        NULL,   'L' },
             { "name",               no_argument,        NULL,   'N' },
             { "dry-list",           no_argument,        NULL,   'n' },
+            { "log-file",           required_argument,  NULL,   'O' },
             { "reverse",            no_argument,        NULL,   'R' },
             { "repodir",            required_argument,  NULL,   'r' },
             { "sort",               required_argument,  NULL,   's' },
@@ -840,7 +842,7 @@ main (int argc, char * const argv[])
         };
         int c;
 
-        c = getopt_long (argc, argv, "aDf:hl:LNnRr:s:V", longopts, NULL);
+        c = getopt_long (argc, argv, "aDf:hl:LNnO:Rr:s:V", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -878,6 +880,10 @@ main (int argc, char * const argv[])
                 cfg.mode = MODE_DRY_LIST;
                 break;
 
+            case 'O':
+                aa_set_log_file_or_die (optarg);
+                break;
+
             case 'R':
                 sort_order = SORT_DESC;
                 break;
diff --git a/src/anopa/aa-stop.c b/src/anopa/aa-stop.c
index 2dc3f25..ebaa258 100644
--- a/src/anopa/aa-stop.c
+++ b/src/anopa/aa-stop.c
@@ -264,6 +264,7 @@ dieusage (int rc)
 {
     aa_die_usage (rc, "[OPTION...] [service...]",
             " -D, --double-output           Enable double-output mode\n"
+            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
             " -r, --repodir DIR             Use DIR as repository directory\n"
             " -l, --listdir DIR             Use DIR to list services to stop\n"
             " -k, --skip SERVICE            Skip (do not stop) SERVICE\n"
@@ -324,6 +325,7 @@ main (int argc, char * const argv[])
             { "skip",               required_argument,  NULL,   'k' },
             { "listdir",            required_argument,  NULL,   'l' },
             { "dry-list",           no_argument,        NULL,   'n' },
+            { "log-file",           required_argument,  NULL,   'O' },
             { "repodir",            required_argument,  NULL,   'r' },
             { "timeout",            required_argument,  NULL,   't' },
             { "version",            no_argument,        NULL,   'V' },
@@ -332,7 +334,7 @@ main (int argc, char * const argv[])
         };
         int c;
 
-        c = getopt_long (argc, argv, "aDhk:l:nr:t:Vv", longopts, NULL);
+        c = getopt_long (argc, argv, "aDhk:l:nO:r:t:Vv", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -364,6 +366,10 @@ main (int argc, char * const argv[])
                 mode |= AA_MODE_IS_DRY;
                 break;
 
+            case 'O':
+                aa_set_log_file_or_die (optarg);
+                break;
+
             case 'r':
                 unslash (optarg);
                 path_repo = optarg;
diff --git a/src/include/anopa/output.h b/src/include/anopa/output.h
index d77cfb9..ed16fcf 100644
--- a/src/include/anopa/output.h
+++ b/src/include/anopa/output.h
@@ -39,6 +39,8 @@ extern const char *PROG;
 #define AA_ERR      1
 
 extern void aa_set_double_output (int enabled);
+extern int  aa_set_log_file (const char *file_or_fd);
+extern void aa_set_log_file_or_die (const char *file_or_fd);
 extern void aa_bb (int where, const char *s, size_t len);
 extern void aa_bb_flush (int where, const char *s, size_t len);
 #define aa_bs(w,s)  aa_bb ((w), (s), strlen (s))
diff --git a/src/libanopa/output.c b/src/libanopa/output.c
index 10d18bf..fb8cc06 100644
--- a/src/libanopa/output.c
+++ b/src/libanopa/output.c
@@ -23,16 +23,25 @@
 #include <unistd.h> /* isatty() */
 #include <skalibs/bytestr.h>
 #include <skalibs/buffer.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/types.h>
 #include <anopa/output.h>
+#include <anopa/err.h> /* ERR_IO */
 
 static int istty[2] = { -1, 0 };
 static int double_output = 0;
 
+static char buf_log[BUFFER_OUTSIZE];
+static buffer_t buffer_log = BUFFER_ZERO;
+
 #define is_tty(n)           (istty[0] > -1 || chk_tty ()) && istty[n]
 
 #define putb(w,s,l)         buffer_put ((w) ? buffer_2 : buffer_1small, s, l)
 #define putb_flush(w,s,l)   buffer_putflush ((w) ? buffer_2 : buffer_1small, s, l)
 
+#define logb(s,l)           buffer_put (&buffer_log, s, l)
+#define logb_flush(s,l)     buffer_putflush (&buffer_log, s, l)
+
 static int
 chk_tty (void)
 {
@@ -47,12 +56,70 @@ aa_set_double_output (int enabled)
     double_output = !!enabled;
 }
 
+int aa_set_log_file (const char *file_or_fd)
+{
+    if (buffer_log.fd >= 0)
+    {
+        fd_close (buffer_log.fd);
+        buffer_log.fd = -1;
+    }
+
+    if (!file_or_fd)
+        return 0;
+
+    if (*file_or_fd >= '0' && *file_or_fd <= '9')
+    {
+        /* Note: we don't allow to use a fd <= 2 */
+        errno = 0;
+        if (!int0_scan (file_or_fd, &buffer_log.fd) || buffer_log.fd <= 2)
+        {
+            if (errno == 0)
+                errno = EINVAL;
+            buffer_log.fd = -1;
+            return -1;
+        }
+    }
+    else
+    {
+        buffer_log.fd = open_append (file_or_fd);
+        if (buffer_log.fd < 0)
+            return -2;
+    }
+
+    if (!buffer_init (&buffer_log, &fd_writev, buffer_log.fd, buf_log, sizeof (buf_log)))
+    {
+        int e = errno;
+        fd_close (buffer_log.fd);
+        errno = e;
+        buffer_log.fd = -1;
+        return -3;
+    }
+
+    return 0;
+}
+
+void aa_set_log_file_or_die (const char *file_or_fd)
+{
+    int r;
+
+    r = aa_set_log_file (file_or_fd);
+    if (r < 0)
+    {
+        if (r == -1)
+            aa_strerr_diefu3sys (1, "set logfile to FD '", file_or_fd, "'");
+        else
+            aa_strerr_diefu3sys (ERR_IO, "set logfile to '", file_or_fd, "'");
+    }
+}
+
 void
 aa_bb (int where, const char *s, size_t len)
 {
     putb (where, s, len);
     if (double_output)
         putb (!where, s, len);
+    if (buffer_log.fd >= 0)
+        logb (s, len);
 }
 
 void
@@ -61,6 +128,8 @@ aa_bb_flush (int where, const char *s, size_t len)
     putb_flush (where, s, len);
     if (double_output)
         putb_flush (!where, s, len);
+    if (buffer_log.fd >= 0)
+        logb_flush (s, len);
 }
 
 void
diff --git a/src/utils/aa-chroot.c b/src/utils/aa-chroot.c
index 58b63fb..8461bd5 100644
--- a/src/utils/aa-chroot.c
+++ b/src/utils/aa-chroot.c
@@ -2,7 +2,7 @@
  * anopa - Copyright (C) 2015-2017 Olivier Brunel
  *
  * aa-chroot.c
- * Copyright (C) 2015 Olivier Brunel <jjk@jjacky.com>
+ * Copyright (C) 2015-2018 Olivier Brunel <jjk@jjacky.com>
  *
  * This file is part of anopa.
  *
@@ -33,6 +33,7 @@ dieusage (int rc)
 {
     aa_die_usage (rc, "NEWROOT COMMAND [ARG...]",
             " -D, --double-output           Enable double-output mode\n"
+            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
             " -h, --help                    Show this help screen and exit\n"
             " -V, --version                 Show version information and exit\n"
             );
@@ -48,12 +49,13 @@ main (int argc, char * const argv[], char * const envp[])
         struct option longopts[] = {
             { "double-output",      no_argument,        NULL,   'D' },
             { "help",               no_argument,        NULL,   'h' },
+            { "log-file",           required_argument,  NULL,   'O' },
             { "version",            no_argument,        NULL,   'V' },
             { NULL, 0, 0, 0 }
         };
         int c;
 
-        c = getopt_long (argc, argv, "DhV", longopts, NULL);
+        c = getopt_long (argc, argv, "DhO:V", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -65,6 +67,10 @@ main (int argc, char * const argv[], char * const envp[])
             case 'h':
                 dieusage (0);
 
+            case 'O':
+                aa_set_log_file_or_die (optarg);
+                break;
+
             case 'V':
                 aa_die_version ();
 
diff --git a/src/utils/aa-ctty.c b/src/utils/aa-ctty.c
index 746bcf8..6c72230 100644
--- a/src/utils/aa-ctty.c
+++ b/src/utils/aa-ctty.c
@@ -2,7 +2,7 @@
  * anopa - Copyright (C) 2015-2017 Olivier Brunel
  *
  * aa-ctty.c
- * Copyright (C) 2015-2017 Olivier Brunel <jjk@jjacky.com>
+ * Copyright (C) 2015-2018 Olivier Brunel <jjk@jjacky.com>
  *
  * This file is part of anopa.
  *
@@ -33,6 +33,7 @@ dieusage (int rc)
 {
     aa_die_usage (rc, "[OPTION...] PROG...",
             " -D, --double-output           Enable double-output mode\n"
+            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
             " -f, --fd=FD                   Use FD as terminal (Default: 0)\n"
             " -s, --steal                   Steal terminal from other session if needed\n"
             " -h, --help                    Show this help screen and exit\n"
@@ -53,13 +54,14 @@ main (int argc, char * const argv[], char const * const *envp)
             { "double-output",      no_argument,        NULL,   'D' },
             { "fd",                 required_argument,  NULL,   'f' },
             { "help",               no_argument,        NULL,   'h' },
+            { "log-file",           required_argument,  NULL,   'O' },
             { "steal",              no_argument,        NULL,   's' },
             { "version",            no_argument,        NULL,   'V' },
             { NULL, 0, 0, 0 }
         };
         int c;
 
-        c = getopt_long (argc, argv, "+Df:hsV", longopts, NULL);
+        c = getopt_long (argc, argv, "+Df:hO:sV", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -76,6 +78,10 @@ main (int argc, char * const argv[], char const * const *envp)
             case 'h':
                 dieusage (0);
 
+            case 'O':
+                aa_set_log_file_or_die (optarg);
+                break;
+
             case 's':
                 steal = 1;
                 break;
diff --git a/src/utils/aa-echo.c b/src/utils/aa-echo.c
index bef87e3..674df42 100644
--- a/src/utils/aa-echo.c
+++ b/src/utils/aa-echo.c
@@ -44,6 +44,7 @@ dieusage (int rc)
 {
     aa_die_usage (rc, "[OPTION...] MESSAGE...",
             " -D, --double-output           Enable double-output mode\n"
+            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
             " -B, --blank-first             Print a blank line (LF) first\n"
             " -T, --title                   Show a main title (default)\n"
             " -t, --title2                  Show a secondary title\n"
@@ -82,6 +83,7 @@ main (int argc, char * const argv[])
             { "error",              no_argument,        NULL,   'e' },
             { "help",               no_argument,        NULL,   'h' },
             { "normal",             no_argument,        NULL,   'n' },
+            { "log-file",           required_argument,  NULL,   'O' },
             { "title",              no_argument,        NULL,   'T' },
             { "title2",             no_argument,        NULL,   't' },
             { "version",            no_argument,        NULL,   'V' },
@@ -90,7 +92,7 @@ main (int argc, char * const argv[])
         };
         int c;
 
-        c = getopt_long (argc, argv, "BDehnTtVw", longopts, NULL);
+        c = getopt_long (argc, argv, "BDehnO:TtVw", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -116,6 +118,10 @@ main (int argc, char * const argv[])
                 where = AA_OUT;
                 break;
 
+            case 'O':
+                aa_set_log_file_or_die (optarg);
+                break;
+
             case 'T':
                 put = put_title;
                 where = AA_OUT;
diff --git a/src/utils/aa-hiercopy.c b/src/utils/aa-hiercopy.c
index 5865c45..816f4e6 100644
--- a/src/utils/aa-hiercopy.c
+++ b/src/utils/aa-hiercopy.c
@@ -36,6 +36,7 @@ dieusage (int rc)
 {
     aa_die_usage (rc, "SRC DST",
             " -D, --double-output           Enable double-output mode\n"
+            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
             " -h, --help                    Show this help screen and exit\n"
             " -V, --version                 Show version information and exit\n"
             );
@@ -51,12 +52,13 @@ main (int argc, char * const argv[])
         struct option longopts[] = {
             { "double-output",      no_argument,        NULL,   'D' },
             { "help",               no_argument,        NULL,   'h' },
+            { "log-file",           required_argument,  NULL,   'O' },
             { "version",            no_argument,        NULL,   'V' },
             { NULL, 0, 0, 0 }
         };
         int c;
 
-        c = getopt_long (argc, argv, "DhV", longopts, NULL);
+        c = getopt_long (argc, argv, "DhO:V", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -68,6 +70,10 @@ main (int argc, char * const argv[])
             case 'h':
                 dieusage (0);
 
+            case 'O':
+                aa_set_log_file_or_die (optarg);
+                break;
+
             case 'V':
                 aa_die_version ();
 
diff --git a/src/utils/aa-incmdline.c b/src/utils/aa-incmdline.c
index 74b6dbb..40cb83b 100644
--- a/src/utils/aa-incmdline.c
+++ b/src/utils/aa-incmdline.c
@@ -33,6 +33,7 @@ dieusage (int rc)
 {
     aa_die_usage (rc, "[OPTION] NAME",
             " -D, --double-output           Enable double-output mode\n"
+            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
             " -f, --file FILE               Use FILE instead of /proc/cmdline\n"
             " -q, --quiet                   Don't write value (if any) to stdout\n"
             " -s, --safe[=C]                Ignore argument if value contain C (default: '/')\n"
@@ -62,6 +63,7 @@ main (int argc, char * const argv[])
             { "file",               no_argument,        NULL,   'f' },
             { "help",               no_argument,        NULL,   'h' },
             { "quiet",              no_argument,        NULL,   'q' },
+            { "log-file",           required_argument,  NULL,   'O' },
             { "required",           no_argument,        NULL,   'r' },
             { "safe",               optional_argument,  NULL,   's' },
             { "version",            no_argument,        NULL,   'V' },
@@ -69,7 +71,7 @@ main (int argc, char * const argv[])
         };
         int c;
 
-        c = getopt_long (argc, argv, "Df:hqrs::V", longopts, NULL);
+        c = getopt_long (argc, argv, "Df:hO:qrs::V", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -85,6 +87,10 @@ main (int argc, char * const argv[])
             case 'h':
                 dieusage (0);
 
+            case 'O':
+                aa_set_log_file_or_die (optarg);
+                break;
+
             case 'q':
                 quiet = 1;
                 break;
diff --git a/src/utils/aa-kill.c b/src/utils/aa-kill.c
index 7b26e8c..991c2b0 100644
--- a/src/utils/aa-kill.c
+++ b/src/utils/aa-kill.c
@@ -116,6 +116,7 @@ dieusage (int rc)
 {
     aa_die_usage (rc, "[OPTION...]",
             " -D, --double-output           Enable double-output mode\n"
+            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
             " -u, --hup                     Send SIGHUP\n"
             " -t, --term                    Send SIGTERM then SIGCONT\n"
             " -k, --kill                    Send SIGKILL\n"
@@ -136,6 +137,7 @@ main (int argc, char * const argv[])
             { "double-output",      no_argument,        NULL,   'D' },
             { "help",               no_argument,        NULL,   'h' },
             { "kill",               no_argument,        NULL,   'k' },
+            { "log-file",           required_argument,  NULL,   'O' },
             { "skip-at",            no_argument,        NULL,   's' },
             { "term",               no_argument,        NULL,   't' },
             { "hup",                no_argument,        NULL,   'u' },
@@ -144,7 +146,7 @@ main (int argc, char * const argv[])
         };
         int c;
 
-        c = getopt_long (argc, argv, "DhkstuV", longopts, NULL);
+        c = getopt_long (argc, argv, "DhkO:stuV", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -160,6 +162,10 @@ main (int argc, char * const argv[])
                 send.kill = 1;
                 break;
 
+            case 'O':
+                aa_set_log_file_or_die (optarg);
+                break;
+
             case 's':
                 send.skip_at = 1;
                 break;
diff --git a/src/utils/aa-mount.c b/src/utils/aa-mount.c
index f9be8c1..3d1c93d 100644
--- a/src/utils/aa-mount.c
+++ b/src/utils/aa-mount.c
@@ -2,7 +2,7 @@
  * anopa - Copyright (C) 2015-2017 Olivier Brunel
  *
  * aa-mount.c
- * Copyright (C) 2015-2017 Olivier Brunel <jjk@jjacky.com>
+ * Copyright (C) 2015-2018 Olivier Brunel <jjk@jjacky.com>
  *
  * This file is part of anopa.
  *
@@ -126,6 +126,7 @@ dieusage (int rc)
 {
     aa_die_usage (rc, "[OPTION...] DEVICE MOUNTPOINT",
             " -D, --double-output           Enable double-output mode\n"
+            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
             " -B, --bind                    Remount a subtree somewhere else\n"
             " -M, --move                    Move a subtree to some other place\n"
             " -t, --type FSTYPE             Use FSTYPE as type of filesystem\n"
@@ -155,6 +156,7 @@ main (int argc, char * const argv[])
             { "mkdir",              no_argument,        NULL,   'd' },
             { "help",               no_argument,        NULL,   'h' },
             { "move",               no_argument,        NULL,   'M' },
+            { "log-file",           required_argument,  NULL,   'O' },
             { "options",            required_argument,  NULL,   'o' },
             { "read-only",          no_argument,        NULL,   'r' },
             { "type",               required_argument,  NULL,   't' },
@@ -164,7 +166,7 @@ main (int argc, char * const argv[])
         };
         int c;
 
-        c = getopt_long (argc, argv, "BDdhMo:rt:Vw", longopts, NULL);
+        c = getopt_long (argc, argv, "BDdhMO:o:rt:Vw", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -190,6 +192,10 @@ main (int argc, char * const argv[])
                     aa_strerr_diefu1sys (2, "build user options");
                 break;
 
+            case 'O':
+                aa_set_log_file_or_die (optarg);
+                break;
+
             case 'o':
                 if (!add_option (&sa, &flags, optarg))
                     aa_strerr_diefu1sys (2, "build user options");
diff --git a/src/utils/aa-pivot.c b/src/utils/aa-pivot.c
index 132473c..c5e7d3e 100644
--- a/src/utils/aa-pivot.c
+++ b/src/utils/aa-pivot.c
@@ -2,7 +2,7 @@
  * anopa - Copyright (C) 2015-2017 Olivier Brunel
  *
  * aa-pivot.c
- * Copyright (C) 2015 Olivier Brunel <jjk@jjacky.com>
+ * Copyright (C) 2015-2018 Olivier Brunel <jjk@jjacky.com>
  *
  * This file is part of anopa.
  *
@@ -35,6 +35,7 @@ dieusage (int rc)
 {
     aa_die_usage (rc, "NEWROOT OLDROOT",
             " -D, --double-output           Enable double-output mode\n"
+            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
             " -h, --help                    Show this help screen and exit\n"
             " -V, --version                 Show version information and exit\n"
             );
@@ -50,12 +51,13 @@ main (int argc, char * const argv[])
         struct option longopts[] = {
             { "double-output",      no_argument,        NULL,   'D' },
             { "help",               no_argument,        NULL,   'h' },
+            { "log-file",           required_argument,  NULL,   'O' },
             { "version",            no_argument,        NULL,   'V' },
             { NULL, 0, 0, 0 }
         };
         int c;
 
-        c = getopt_long (argc, argv, "DhV", longopts, NULL);
+        c = getopt_long (argc, argv, "DhO:V", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -67,6 +69,10 @@ main (int argc, char * const argv[])
             case 'h':
                 dieusage (0);
 
+            case 'O':
+                aa_set_log_file_or_die (optarg);
+                break;
+
             case 'V':
                 aa_die_version ();
 
diff --git a/src/utils/aa-reboot.c b/src/utils/aa-reboot.c
index b530172..a23862d 100644
--- a/src/utils/aa-reboot.c
+++ b/src/utils/aa-reboot.c
@@ -2,7 +2,7 @@
  * anopa - Copyright (C) 2015-2017 Olivier Brunel
  *
  * aa-reboot.c
- * Copyright (C) 2015 Olivier Brunel <jjk@jjacky.com>
+ * Copyright (C) 2015-2018 Olivier Brunel <jjk@jjacky.com>
  *
  * This file is part of anopa.
  *
@@ -31,6 +31,7 @@ dieusage (int rc)
 {
     aa_die_usage (rc, "OPTION",
             " -D, --double-output           Enable double-output mode\n"
+            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
             " -r, --reboot                  Reboot the machine NOW\n"
             " -H, --halt                    Halt the machine NOW\n"
             " -p, --poweroff                Power off the machine NOW\n"
@@ -60,6 +61,7 @@ main (int argc, char * const argv[])
             { "double-output",      no_argument,        NULL,   'D' },
             { "halt",               no_argument,        NULL,   'H' },
             { "help",               no_argument,        NULL,   'h' },
+            { "log-file",           required_argument,  NULL,   'O' },
             { "poweroff",           no_argument,        NULL,   'p' },
             { "reboot",             no_argument,        NULL,   'r' },
             { "version",            no_argument,        NULL,   'V' },
@@ -67,7 +69,7 @@ main (int argc, char * const argv[])
         };
         int c;
 
-        c = getopt_long (argc, argv, "DHhprV", longopts, NULL);
+        c = getopt_long (argc, argv, "DHhO:prV", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -83,6 +85,10 @@ main (int argc, char * const argv[])
             case 'h':
                 dieusage (0);
 
+            case 'O':
+                aa_set_log_file_or_die (optarg);
+                break;
+
             case 'p':
                 i = 1;
                 break;
diff --git a/src/utils/aa-service.c b/src/utils/aa-service.c
index 4635949..37240d9 100644
--- a/src/utils/aa-service.c
+++ b/src/utils/aa-service.c
@@ -2,7 +2,7 @@
  * anopa - Copyright (C) 2015-2017 Olivier Brunel
  *
  * aa-service.c
- * Copyright (C) 2015-2017 Olivier Brunel <jjk@jjacky.com>
+ * Copyright (C) 2015-2018 Olivier Brunel <jjk@jjacky.com>
  *
  * This file is part of anopa.
  *
@@ -163,6 +163,7 @@ dieusage (int rc)
 {
     aa_die_usage (rc, "[OPTION] PROG...",
             " -D, --double-output           Enable double-output mode\n"
+            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
             " -l, --log                     Use parent directory as servicedir\n"
             " -h, --help                    Show this help screen and exit\n"
             " -V, --version                 Show version information and exit\n"
@@ -184,12 +185,13 @@ main (int argc, char const **argv, char const *const *envp)
             { "double-output",      no_argument,        NULL,   'D' },
             { "help",               no_argument,        NULL,   'h' },
             { "log",                no_argument,        NULL,   'l' },
+            { "log-file",           required_argument,  NULL,   'O' },
             { "version",            no_argument,        NULL,   'V' },
             { NULL, 0, 0, 0 }
         };
         int c;
 
-        c = getopt_long (argc, (char * const *) argv, "+DhlV", longopts, NULL);
+        c = getopt_long (argc, (char * const *) argv, "+DhlO:V", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -205,6 +207,10 @@ main (int argc, char const **argv, char const *const *envp)
                 islog = 1;
                 break;
 
+            case 'O':
+                aa_set_log_file_or_die (optarg);
+                break;
+
             case 'V':
                 aa_die_version ();
 
diff --git a/src/utils/aa-setready.c b/src/utils/aa-setready.c
index f8e2eb8..8055103 100644
--- a/src/utils/aa-setready.c
+++ b/src/utils/aa-setready.c
@@ -2,7 +2,7 @@
  * anopa - Copyright (C) 2015-2017 Olivier Brunel
  *
  * aa-setready.c
- * Copyright (C) 2015-2017 Olivier Brunel <jjk@jjacky.com>
+ * Copyright (C) 2015-2018 Olivier Brunel <jjk@jjacky.com>
  *
  * This file is part of anopa.
  *
@@ -35,6 +35,7 @@ dieusage (int rc)
 {
     aa_die_usage (rc, "[OPTION] SERVICEDIR",
             " -D, --double-output           Enable double-output mode\n"
+            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
             " -U, --ready                   Mark service ready; This is the default.\n"
             " -N, --unready                 Mark service not ready\n"
             "\n"
@@ -55,13 +56,14 @@ main (int argc, char * const argv[])
             { "double-output",      no_argument,        NULL,   'D' },
             { "help",               no_argument,        NULL,   'h' },
             { "unready",            no_argument,        NULL,   'N' },
+            { "log-file",           required_argument,  NULL,   'O' },
             { "ready",              no_argument,        NULL,   'U' },
             { "version",            no_argument,        NULL,   'V' },
             { NULL, 0, 0, 0 }
         };
         int c;
 
-        c = getopt_long (argc, argv, "DhNUV", longopts, NULL);
+        c = getopt_long (argc, argv, "DhNO:UV", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -77,6 +79,10 @@ main (int argc, char * const argv[])
                 ready = 0;
                 break;
 
+            case 'O':
+                aa_set_log_file_or_die (optarg);
+                break;
+
             case 'U':
                 ready = 1;
                 break;
diff --git a/src/utils/aa-terminate.c b/src/utils/aa-terminate.c
index b1ad0ec..d9e7e22 100644
--- a/src/utils/aa-terminate.c
+++ b/src/utils/aa-terminate.c
@@ -258,6 +258,7 @@ dieusage (int rc)
 {
     aa_die_usage (rc, "[OPTION...]",
             " -D, --double-output           Enable double-output mode\n"
+            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
             " -v, --verbose                 Show what was done\n"
             " -q, --quiet                   No warnings for what's left\n"
             " -l, --lazy-umounts            Try lazy umount as last resort\n"
@@ -287,6 +288,7 @@ main (int argc, char * const argv[])
             { "double-output",      no_argument,        NULL,   'D' },
             { "help",               no_argument,        NULL,   'h' },
             { "lazy-umounts",       no_argument,        NULL,   'l' },
+            { "log-file",           required_argument,  NULL,   'O' },
             { "quiet",              no_argument,        NULL,   'q' },
             { "version",            no_argument,        NULL,   'V' },
             { "verbose",            no_argument,        NULL,   'v' },
@@ -294,7 +296,7 @@ main (int argc, char * const argv[])
         };
         int c;
 
-        c = getopt_long (argc, argv, "aDhlqVv", longopts, NULL);
+        c = getopt_long (argc, argv, "aDhlO:qVv", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -314,6 +316,10 @@ main (int argc, char * const argv[])
                 lazy = 1;
                 break;
 
+            case 'O':
+                aa_set_log_file_or_die (optarg);
+                break;
+
             case 'q':
                 level = 0;
                 break;
diff --git a/src/utils/aa-test.c b/src/utils/aa-test.c
index bcb3911..adda3f2 100644
--- a/src/utils/aa-test.c
+++ b/src/utils/aa-test.c
@@ -2,7 +2,7 @@
  * anopa - Copyright (C) 2015-2017 Olivier Brunel
  *
  * aa-test.c
- * Copyright (C) 2015-2017 Olivier Brunel <jjk@jjacky.com>
+ * Copyright (C) 2015-2018 Olivier Brunel <jjk@jjacky.com>
  *
  * This file is part of anopa.
  *
@@ -53,6 +53,7 @@ dieusage (int rc)
 {
     aa_die_usage (rc, "OPTION FILE",
             " -D, --double-output           Enable double-output mode\n"
+            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
             " -b, --block                   Test whether FILE is a block special\n"
             " -d, --directory               Test whether FILE is a directory\n"
             " -e, --exists                  Test whether FILE exists\n"
@@ -90,6 +91,7 @@ main (int argc, char * const argv[])
             { "file",               no_argument,        NULL,   'f' },
             { "help",               no_argument,        NULL,   'h' },
             { "symlink",            no_argument,        NULL,   'L' },
+            { "log-file",           required_argument,  NULL,   'O' },
             { "pipe",               no_argument,        NULL,   'p' },
             { "read",               no_argument,        NULL,   'r' },
             { "repeat",             optional_argument,  NULL,   'R' },
@@ -101,7 +103,7 @@ main (int argc, char * const argv[])
         };
         int c;
 
-        c = getopt_long (argc, argv, "bDdefhLprR::SVwx", longopts, NULL);
+        c = getopt_long (argc, argv, "bDdefhLO:prR::SVwx", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -126,6 +128,10 @@ main (int argc, char * const argv[])
             case 'h':
                 dieusage (0);
 
+            case 'O':
+                aa_set_log_file_or_die (optarg);
+                break;
+
             case 'R':
                 if (optarg && !uint0_scan (optarg, &repeat))
                     aa_strerr_diefu2sys (1, "set repeat counter to ", optarg);
diff --git a/src/utils/aa-tty.c b/src/utils/aa-tty.c
index 8e599bb..91e4ae1 100644
--- a/src/utils/aa-tty.c
+++ b/src/utils/aa-tty.c
@@ -35,6 +35,7 @@ dieusage (int rc)
 {
     aa_die_usage (rc, "",
             " -D, --double-output           Enable double-output mode\n"
+            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
             " -h, --help                    Show this help screen and exit\n"
             " -V, --version                 Show version information and exit\n"
             );
@@ -55,12 +56,13 @@ main (int argc, char * const argv[])
         struct option longopts[] = {
             { "double-output",      no_argument,        NULL,   'D' },
             { "help",               no_argument,        NULL,   'h' },
+            { "log-file",           required_argument,  NULL,   'O' },
             { "version",            no_argument,        NULL,   'V' },
             { NULL, 0, 0, 0 }
         };
         int c;
 
-        c = getopt_long (argc, argv, "DhV", longopts, NULL);
+        c = getopt_long (argc, argv, "DhO:V", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -72,6 +74,10 @@ main (int argc, char * const argv[])
             case 'h':
                 dieusage (0);
 
+            case 'O':
+                aa_set_log_file_or_die (optarg);
+                break;
+
             case 'V':
                 aa_die_version ();
 
diff --git a/src/utils/aa-umount.c b/src/utils/aa-umount.c
index b0f5d7d..521a987 100644
--- a/src/utils/aa-umount.c
+++ b/src/utils/aa-umount.c
@@ -2,7 +2,7 @@
  * anopa - Copyright (C) 2015-2017 Olivier Brunel
  *
  * aa-umount.c
- * Copyright (C) 2015 Olivier Brunel <jjk@jjacky.com>
+ * Copyright (C) 2015-2018 Olivier Brunel <jjk@jjacky.com>
  *
  * This file is part of anopa.
  *
@@ -34,6 +34,7 @@ dieusage (int rc)
 {
     aa_die_usage (rc, "[OPTIONS...] MOUNTPOINT",
             " -D, --double-output           Enable double-output mode\n"
+            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
             " -f, --force                   Force unmount even if busy (NFS only)\n"
             " -l, --lazy                    Perform lazy unmounting\n"
             " -h, --help                    Show this help screen and exit\n"
@@ -54,12 +55,13 @@ main (int argc, char * const argv[])
             { "force",              no_argument,        NULL,   'f' },
             { "help",               no_argument,        NULL,   'h' },
             { "lazy",               no_argument,        NULL,   'l' },
+            { "log-file",           required_argument,  NULL,   'O' },
             { "version",            no_argument,        NULL,   'V' },
             { NULL, 0, 0, 0 }
         };
         int c;
 
-        c = getopt_long (argc, argv, "DfhlV", longopts, NULL);
+        c = getopt_long (argc, argv, "DfhlO:V", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -79,6 +81,10 @@ main (int argc, char * const argv[])
                 flags = MNT_DETACH;
                 break;
 
+            case 'O':
+                aa_set_log_file_or_die (optarg);
+                break;
+
             case 'V':
                 aa_die_version ();