Welcome to little lamb

Code » anopa » commit 16c8860

Unify & document return codes all around

author Olivier Brunel
2018-01-21 19:00:49 UTC
committer Olivier Brunel
2018-01-21 19:49:08 UTC
parent 36ad5f4dd67707b8347d08e4017a54fb59b36047

Unify & document return codes all around

All commands & utils will now have common rules when it comes to return
codes:

- If the least significant bit is set (odd rc), it is a fatal error. The
  list of all possible fatal error codes is shared all throughout anopa
- Else (even rc), the return codes are binary-specific; Having said that,
  all commands share the same bitwise combination of flags for better
  consistency

.gitignore +1 -0
doc/aa-chroot.pod +7 -0
doc/aa-ctty.pod +7 -0
doc/aa-echo.pod +7 -0
doc/aa-enable.pod +2 -29
doc/aa-hiercopy.pod +6 -6
doc/aa-incmdline.pod +8 -1
doc/aa-kill.pod +7 -0
doc/aa-mount.pod +7 -0
doc/aa-pivot.pod +7 -0
doc/aa-reboot.pod +7 -0
doc/aa-reset.pod +5 -0
doc/aa-service.pod +14 -0
doc/aa-setready.pod +9 -10
doc/aa-start.pod +5 -0
doc/aa-status.pod +5 -0
doc/aa-stop.pod +5 -0
doc/aa-sync.pod +7 -0
doc/aa-terminate.pod +26 -4
doc/aa-test.pod +14 -5
doc/aa-tty.pod +7 -0
doc/aa-umount.pod +7 -0
doc/anopa-rc.pod +61 -0
package/targets.mak +1 -0
src/anopa/aa-enable.c +19 -15
src/anopa/aa-reset.c +16 -6
src/anopa/aa-start.c +17 -10
src/anopa/aa-status.c +19 -11
src/anopa/aa-stop.c +13 -8
src/anopa/common.h +11 -1
src/anopa/start-stop.c +10 -9
src/include/anopa/common.h +3 -1
src/include/anopa/rc.h +49 -0
src/libanopa/exec_oneshot.c +5 -4
src/libanopa/output.c +3 -2
src/utils/aa-chroot.c +7 -7
src/utils/aa-ctty.c +5 -5
src/utils/aa-echo.c +4 -4
src/utils/aa-hiercopy.c +12 -6
src/utils/aa-incmdline.c +17 -11
src/utils/aa-kill.c +6 -6
src/utils/aa-mount.c +12 -12
src/utils/aa-pivot.c +5 -5
src/utils/aa-reboot.c +5 -5
src/utils/aa-service.c +13 -11
src/utils/aa-setready.c +18 -9
src/utils/aa-sync.c +2 -2
src/utils/aa-terminate.c +36 -10
src/utils/aa-test.c +23 -17
src/utils/aa-tty.c +6 -6
src/utils/aa-umount.c +5 -5

diff --git a/.gitignore b/.gitignore
index 6cda3ad..da256b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,5 +11,6 @@
 /config.mak
 /src/include/anopa/config.h
 /anopa.1
+/anopa-rc.1
 /aa-*
 /libanopa.a
diff --git a/doc/aa-chroot.pod b/doc/aa-chroot.pod
index 6691166..e649983 100644
--- a/doc/aa-chroot.pod
+++ b/doc/aa-chroot.pod
@@ -45,3 +45,10 @@ which must exists, goes into this directory, chroots into it and chdir into the
 with the given I<ARG> (if any).
 
 Note that B<aa-chroot>(1)'s parent process if unaffected by the change.
+
+=head1 RETURN CODES
+
+Return codes are somewhat unified inside B<anopa>. Odd return codes represent
+fatal errors, and are detailled in B<anopa-rc>(1)
+
+B<aa-chroot>(1) does not have even return codes.
diff --git a/doc/aa-ctty.pod b/doc/aa-ctty.pod
index 66ecbbf..96920d4 100644
--- a/doc/aa-ctty.pod
+++ b/doc/aa-ctty.pod
@@ -54,3 +54,10 @@ B<execline> fashion, it then executes into the rest of its command line.
 
 If the ioctl call fails, a warning is printed and it still executes into the
 rest of its command line. If it fails to do so, it returns 111.
+
+=head1 RETURN CODES
+
+Return codes are somewhat unified inside B<anopa>. Odd return codes represent
+fatal errors, and are detailled in B<anopa-rc>(1)
+
+B<aa-ctty>(1) does not have even return codes.
diff --git a/doc/aa-echo.pod b/doc/aa-echo.pod
index 51fc91d..55f4cad 100644
--- a/doc/aa-echo.pod
+++ b/doc/aa-echo.pod
@@ -111,3 +111,10 @@ To print +TEXT
 =back
 
 For example: `aa-echo -w "The file " +r "/foo/bar" +n " doesn't exist"`
+
+=head1 RETURN CODES
+
+Return codes are somewhat unified inside B<anopa>. Odd return codes represent
+fatal errors, and are detailled in B<anopa-rc>(1)
+
+B<aa-echo>(1) does not have even return codes.
diff --git a/doc/aa-enable.pod b/doc/aa-enable.pod
index 8f0914d..6015694 100644
--- a/doc/aa-enable.pod
+++ b/doc/aa-enable.pod
@@ -279,35 +279,8 @@ that those specified:
 
 =head1 RETURN CODES
 
-The possible return codes are:
-
-=over
-
-=item B<0>
-Success
-
-=item B<1>
-Usage error (invalid option, etc)
-
-=item B<2>
-Memory error
-
-=item B<3>
-Internal state error (should not happen, else it's a bug to be reported)
-
-=item B<4>
-IO error
-
-=item B<5>
-Failed to init repository
-
-=item B<6>
-Failed to alarm B<s6-svscan>
-
-=item B<23>
-Failed to enable at least one service
-
-=back
+Return codes are somewhat unified inside B<anopa>. Return codes are unified for
+all commands, and are documented in B<anopa-rc>(1)
 
 =head1 ENVIRONMENT VARIABLES
 
diff --git a/doc/aa-hiercopy.pod b/doc/aa-hiercopy.pod
index f62dabe..a1bc8d1 100644
--- a/doc/aa-hiercopy.pod
+++ b/doc/aa-hiercopy.pod
@@ -50,20 +50,20 @@ B<s6-hiercopy> would simply copy the symlink itself.
 
 =head1 RETURN CODES
 
-Possible errors are:
+Return codes are somewhat unified inside B<anopa>. Odd return codes represent
+fatal errors, and are detailled in B<anopa-rc>(1)
 
-=over
+Possible even return codes for B<aa-hiercopy>(1) are:
 
-=item B<1>
-Usage error
+=over
 
 =item B<2>
 Failed to lstat I<SOURCE>
 
-=item B<3>
+=item B<4>
 Failed to resolve symlink I<SOURCE>
 
-=item B<4>
+=item B<6>
 Error during the copy
 
 =back
diff --git a/doc/aa-incmdline.pod b/doc/aa-incmdline.pod
index 6453066..b58e65d 100644
--- a/doc/aa-incmdline.pod
+++ b/doc/aa-incmdline.pod
@@ -62,7 +62,14 @@ I<NAME> was specified, it exits 1.
 
 If there's no such argument on the command line, or B<--safe> was used and the
 argument's value contains I<C>, or there's no value and B<--required> was used,
-it exits 3; Else it exits 0.
+it exits 4; Else it exits 0.
 
 If the argument had a value specified it will be printed on stdout unless
 B<--quiet> was used.
+
+=head1 RETURN CODES
+
+Return codes are somewhat unified inside B<anopa>. Odd return codes represent
+fatal errors, and are detailled in B<anopa-rc>(1)
+
+Possible even return codes for B<aa-incmdline>(1) are as described above.
diff --git a/doc/aa-kill.pod b/doc/aa-kill.pod
index 6e3a6ef..a9714bd 100644
--- a/doc/aa-kill.pod
+++ b/doc/aa-kill.pod
@@ -74,3 +74,10 @@ skip any that starts with an '@' Also skipped will be process without cmdline
 
 Note that B<aa-kill>(1) will ignore the signals it sends when B<--skip> isn't
 used, though that won't do much against SIGKILL.
+
+=head1 RETURN CODES
+
+Return codes are somewhat unified inside B<anopa>. Odd return codes represent
+fatal errors, and are detailled in B<anopa-rc>(1)
+
+B<aa-kill>(1) does not have even return codes.
diff --git a/doc/aa-mount.pod b/doc/aa-mount.pod
index d1a3afe..05c0aba 100644
--- a/doc/aa-mount.pod
+++ b/doc/aa-mount.pod
@@ -91,3 +91,10 @@ nodev, diratime, nodiratime, dirsync, exec, noexec, mand, nomand, relatime,
 norelatime, strictatime, nostrictatime, suid, nosuid, remount, sync.
 
 Any other options will be given to the kernel as-is.
+
+=head1 RETURN CODES
+
+Return codes are somewhat unified inside B<anopa>. Odd return codes represent
+fatal errors, and are detailled in B<anopa-rc>(1)
+
+B<aa-mount>(1) does not have even return codes.
diff --git a/doc/aa-pivot.pod b/doc/aa-pivot.pod
index 1b26a50..15af9f7 100644
--- a/doc/aa-pivot.pod
+++ b/doc/aa-pivot.pod
@@ -50,3 +50,10 @@ into the current directory, e.g. using B<aa-chroot>(1). For example:
     cd NEWROOT
     aa-pivot . OLDROOT
     aa-chroot . COMMAND
+
+=head1 RETURN CODES
+
+Return codes are somewhat unified inside B<anopa>. Odd return codes represent
+fatal errors, and are detailled in B<anopa-rc>(1)
+
+B<aa-pivot>(1) does not have even return codes.
diff --git a/doc/aa-reboot.pod b/doc/aa-reboot.pod
index 3907d3a..05b0401 100644
--- a/doc/aa-reboot.pod
+++ b/doc/aa-reboot.pod
@@ -57,3 +57,10 @@ You should never trigger it manually/directly. Instead, use B<aa-shutdown>(1),
 that will send the appropriate commands to B<s6-svscan> (PID 1) in order to
 properly shut down the system, before (probably) ending with a call to
 B<aa-reboot>(1) at the end of stage 4.
+
+=head1 RETURN CODES
+
+Return codes are somewhat unified inside B<anopa>. Odd return codes represent
+fatal errors, and are detailled in B<anopa-rc>(1)
+
+B<aa-reboot>(1) does not have even return codes.
diff --git a/doc/aa-reset.pod b/doc/aa-reset.pod
index 3604969..98eff2f 100644
--- a/doc/aa-reset.pod
+++ b/doc/aa-reset.pod
@@ -72,6 +72,11 @@ Note that a service either "Starting" or "Stopping" will never be reset.
 You can use B<-> as service name to read actual service names from stdin, where
 there must be one name per line.
 
+=head1 RETURN CODES
+
+Return codes are somewhat unified inside B<anopa>. Return codes are unified for
+all commands, and are documented in B<anopa-rc>(1)
+
 =head1 ENVIRONMENT VARIABLES
 
 The following environment variables are used :
diff --git a/doc/aa-service.pod b/doc/aa-service.pod
index af36545..c1df52a 100644
--- a/doc/aa-service.pod
+++ b/doc/aa-service.pod
@@ -69,3 +69,17 @@ string or I<foo>)
 This can be useful to get the service/instance name dynamicly; e.g. a service
 could use I<${INSTANCE}> as argument, and a generic logger could then use
 I</var/log/${SERVICE_NAME}> as logdir.
+
+=head1 RETURN CODES
+
+Return codes are somewhat unified inside B<anopa>. Odd return codes represent
+fatal errors, and are detailled in B<anopa-rc>(1)
+
+Possible even return codes for B<aa-service>(1) are:
+
+=over
+
+=item B<2>
+Failed to perform variable substitution
+
+=back
diff --git a/doc/aa-setready.pod b/doc/aa-setready.pod
index 583e88b..13a1ce0 100644
--- a/doc/aa-setready.pod
+++ b/doc/aa-setready.pod
@@ -59,33 +59,32 @@ state (e.g. connection dropped).
 Obviously you need to have the appropriate permissions to perform all the needed
 tasks.
 
-=head1 RETURN VALUE
+=head1 RETURN CODES
 
-B<aa-setready>(1) will return 0 on success, or one of the following on error:
+Return codes are somewhat unified inside B<anopa>. Odd return codes represent
+fatal errors, and are detailled in B<anopa-rc>(1)
 
-=over
-
-=item B<1>
+Possible even return codes for B<aa-setready>(1) are:
 
-Syntax error (e.g. unknown option)
+=over
 
 =item B<2>
 
 Failed to read s6 status file
 
-=item B<3>
+=item B<4>
 
 Service is not up
 
-=item B<4>
+=item B<6>
 
 Failed to init timestamp (needed for readiness timestamp, when marking ready)
 
-=item B<5>
+=item B<8>
 
 Failed to write s6 status file
 
-=item B<6>
+=item B<10>
 
 Failed to sent the event on I<event> fifodir
 
diff --git a/doc/aa-start.pod b/doc/aa-start.pod
index 46b8ac5..257314c 100644
--- a/doc/aa-start.pod
+++ b/doc/aa-start.pod
@@ -241,6 +241,11 @@ Also note that once B<aa-start>(1) received a demand of password input, it will
 disable the service's timeout, restoring it once it has been processed (e.g.
 user input has been written to the service's stdin) and resetting its timer.
 
+=head1 RETURN CODES
+
+Return codes are somewhat unified inside B<anopa>. Return codes are unified for
+all commands, and are documented in B<anopa-rc>(1)
+
 =head1 ENVIRONMENT VARIABLES
 
 The following environment variables are used :
diff --git a/doc/aa-status.pod b/doc/aa-status.pod
index 4711141..e173379 100644
--- a/doc/aa-status.pod
+++ b/doc/aa-status.pod
@@ -272,6 +272,11 @@ later in shown.
 
 =back
 
+=head1 RETURN CODES
+
+Return codes are somewhat unified inside B<anopa>. Return codes are unified for
+all commands, and are documented in B<anopa-rc>(1)
+
 =head1 ENVIRONMENT VARIABLES
 
 The following environment variables are used :
diff --git a/doc/aa-stop.pod b/doc/aa-stop.pod
index 9b7305e..7a082cc 100644
--- a/doc/aa-stop.pod
+++ b/doc/aa-stop.pod
@@ -152,6 +152,11 @@ was already (being) done.
 Obviously, the script used is I<stop> and not I<start>. Other than that, the
 process is much the same, so you can refer to B<aa-start>(1) for more.
 
+=head1 RETURN CODES
+
+Return codes are somewhat unified inside B<anopa>. Return codes are unified for
+all commands, and are documented in B<anopa-rc>(1)
+
 =head1 ENVIRONMENT VARIABLES
 
 The following environment variables are used :
diff --git a/doc/aa-sync.pod b/doc/aa-sync.pod
index ef5963e..4a5baf2 100644
--- a/doc/aa-sync.pod
+++ b/doc/aa-sync.pod
@@ -26,3 +26,10 @@ B<aa-sync>(1) causes all buffered modifications to file metadata and data to be
 written to the underlying filesystems.
 
 This is just a wrapper for B<sync>(2)
+
+=head1 RETURN CODES
+
+Return codes are somewhat unified inside B<anopa>. Odd return codes represent
+fatal errors, and are detailled in B<anopa-rc>(1)
+
+B<aa-sync>(1) does not have even return codes.
diff --git a/doc/aa-terminate.pod b/doc/aa-terminate.pod
index 5150b4c..f70b620 100644
--- a/doc/aa-terminate.pod
+++ b/doc/aa-terminate.pod
@@ -113,8 +113,30 @@ reads I</proc/swaps> to list active swaps and I</proc/mounts> for mount points;
 It also reads I</dev> for loop/block devices, and uses I</dev/mapper/control> to
 remove block devices.
 
-=head1 RETURN VALUES
+=head1 RETURN CODES
 
-B<aa-terminate>(1) returns 0 on success (no "left-overs"), 1 on syntax error
-(e.g. invalid option) and 2 if there is at least one left-over (excluding API
-file systems).
+Return codes are somewhat unified inside B<anopa>. Odd return codes represent
+fatal errors, and are detailed in B<anopa-rc>(1)
+
+Possible even return codes for B<aa-terminate>(1) are a bitwise combination of
+the following flags:
+
+=over
+
+=item B<2>
+
+At least 1 swap was left over
+
+=item B<4>
+
+At least 1 mount was left over
+
+=item B<8>
+
+At least 1 loop device was left over
+
+=item B<16>
+
+At least 1 DM block device was left over
+
+=back
diff --git a/doc/aa-test.pod b/doc/aa-test.pod
index d4bdcc9..4ce18f0 100644
--- a/doc/aa-test.pod
+++ b/doc/aa-test.pod
@@ -86,12 +86,21 @@ Test whether I<FILE> exists and execute (search) permission is granted
 =head1 DESCRIPTION
 
 B<aa-test>(1) is a simple tool to check file types/permissions as specified, and
-return 0 when true, else:
+exits with a return code indicating whether the test passed or not.
 
-1: Syntax error (e.g. invalid option)
+=head1 RETURN CODES
 
-2: System error (e.g. permission denied)
+Return codes are somewhat unified inside B<anopa>. Odd return codes represent
+fatal errors, and are detailled in B<anopa-rc>(1)
 
-3: I<FILE> doesn't exist
+Possible even return codes for B<aa-test>(1) are:
 
-4: I<File> exists, but the rest of the test failed (e.g. wrong type)
+=over
+
+=item B<2>
+I<FILE> doesn't exist
+
+=item B<4>
+I<FILE> exists, but the rest of the test failed (e.g. wrong type)
+
+=back
diff --git a/doc/aa-tty.pod b/doc/aa-tty.pod
index 464e446..1f4a966 100644
--- a/doc/aa-tty.pod
+++ b/doc/aa-tty.pod
@@ -42,3 +42,10 @@ Show version information and exit.
 B<aa-tty>(1) is a small tool that will print to device name of the active tty.
 It determines it by reading I<active> files in I</sys/class/tty> starting with
 I</sys/class/tty/console/active> and "going up" as much as possible.
+
+=head1 RETURN CODES
+
+Return codes are somewhat unified inside B<anopa>. Odd return codes represent
+fatal errors, and are detailled in B<anopa-rc>(1)
+
+B<aa-tty>(1) does not have even return codes.
diff --git a/doc/aa-umount.pod b/doc/aa-umount.pod
index 4af56db..7470f99 100644
--- a/doc/aa-umount.pod
+++ b/doc/aa-umount.pod
@@ -52,3 +52,10 @@ B<aa-umount>(1) unmounts the specified mount point. It isn't possible to unmount
 a filesystem if it is busy, e.g. there are still open files. This could even be
 caused by B<aa-umount>(1) itself; It is possible to avoid this using the
 B<--lazy> option.
+
+=head1 RETURN CODES
+
+Return codes are somewhat unified inside B<anopa>. Odd return codes represent
+fatal errors, and are detailled in B<anopa-rc>(1)
+
+B<aa-umount>(1) does not have even return codes.
diff --git a/doc/anopa-rc.pod b/doc/anopa-rc.pod
new file mode 100644
index 0000000..72e53c7
--- /dev/null
+++ b/doc/anopa-rc.pod
@@ -0,0 +1,61 @@
+=head1 RETURN CODES
+
+Every command or utility in B<anopa> follows certain rules when it comes to
+return codes. Return codes are classified under two categories, based on whether
+the least significant bit (LSB) is set or not.
+
+If the LSB is set, i.e. the return code is odd, it represents a fatal error. The
+remaining 7bit integer is a fatal error code, common to all software in
+B<anopa>.
+
+If the LSB isn't set, i.e. the return code is even, it is an error code that is
+specific to this command/utility. Although for easiness/consistency, all
+commands do use the same ones, as described below.
+
+=head2 Odd return codes: fatal errors
+
+All commands & utilities in B<anopa> share the same fatal return codes, all
+listed in the following table:
+
+    +--------+-------+------------------------------+-----------------------+
+    | return | 7-bit | Meaning                      | Notes                 |
+    |  code  | value |                              |                       |
+    +--------+-------+------------------------------+-----------------------+
+    |    1   |    0  | Usage error                  |                       |
+    +--------+-------+------------------------------+-----------------------+
+    |    3   |    1  | Failed to init repository    | Only for commands     |
+    +--------+-------+------------------------------+-----------------------+
+    |    5   |    2  | I/O error                    |                       |
+    +--------+-------+------------------------------+-----------------------+
+    |    7   |    3  | Memory error                 |                       |
+    +--------+-------+------------------------------+-----------------------+
+    |    9   |    4  | Failed to alarm s6           | Only for aa-enable    |
+    +--------+-------+------------------------------+-----------------------+
+    |  111   |   55  | Failed to exec               |                       |
+    +--------+-------+------------------------------+-----------------------+
+    |  255   |  127  | Internal error               | Shouldn't happen      |
+    +--------+-------+------------------------------+-----------------------+
+
+=head2 Even return codes
+
+As explained, even return codes aren't common all through B<anopa>, though for
+easiness & consistency all commands use the same ones. For even return codes of
+utilities, please refer to its specific man page.
+
+For commands (B<aa-start>(1), B<aa-stop>(1), B<aa-enable>(1), B<aa-status>(1),
+B<aa-reset>(1)), the remaining 7bit integer is actually a bitwise combination
+of the following flags :
+
+    +------+------+-------------------------------------------+---------------+
+    |  RC  | 7bit | Meaning                                   | Notes         |
+    | flag | flag |                                           |               |
+    +------+------+-------------------------------------------+---------------+
+    |   2  |    1 | At least one service was unknown          |               |
+    +------+------+-------------------------------------------+---------------+
+    |   4  |    2 | At least one service was skipped          | aa-start only |
+    +------+------+-------------------------------------------+---------------+
+    |   8  |    4 | At least one service failed               |               |
+    +------+------+-------------------------------------------+---------------+
+    |  16  |    8 | At least one failed service was essential | aa-start only |
+    +------+------+-------------------------------------------+---------------+
+
diff --git a/package/targets.mak b/package/targets.mak
index 20a1e7a..b638dd3 100644
--- a/package/targets.mak
+++ b/package/targets.mak
@@ -33,6 +33,7 @@ aa-stage4
 
 DOC_TARGETS := \
 anopa.1 \
+anopa-rc.1 \
 aa-chroot.1 \
 aa-ctty.1 \
 aa-echo.1 \
diff --git a/src/anopa/aa-enable.c b/src/anopa/aa-enable.c
index b743190..4175f17 100644
--- a/src/anopa/aa-enable.c
+++ b/src/anopa/aa-enable.c
@@ -231,9 +231,9 @@ main (int argc, char * const argv[])
     int r;
 
     if (!stralloc_catb (&aa_sa_sources, SOURCE_ETC, sizeof (SOURCE_ETC)))
-        aa_strerr_diefu1sys (2, "stralloc_catb");
+        aa_strerr_diefu1sys (RC_FATAL_MEMORY, "stralloc_catb");
     if (!stralloc_catb (&aa_sa_sources, SOURCE_USR, sizeof (SOURCE_USR)))
-        aa_strerr_diefu1sys (2, "stralloc_catb");
+        aa_strerr_diefu1sys (RC_FATAL_MEMORY, "stralloc_catb");
 
     for (;;)
     {
@@ -282,7 +282,7 @@ main (int argc, char * const argv[])
                 break;
 
             case 'h':
-                dieusage (0);
+                dieusage (RC_OK);
 
             case 'k':
                 skip = optarg;
@@ -317,7 +317,7 @@ main (int argc, char * const argv[])
             case 's':
                 unslash (optarg);
                 if (!stralloc_catb (&aa_sa_sources, optarg, strlen (optarg) + 1))
-                    aa_strerr_diefu1sys (2, "stralloc_catb");
+                    aa_strerr_diefu1sys (RC_FATAL_MEMORY, "stralloc_catb");
                 break;
 
             case 'u':
@@ -335,30 +335,30 @@ main (int argc, char * const argv[])
                 if (extra == 1)
                     flags |= AA_FLAG_NO_SUPERVISE;
                 else
-                    aa_strerr_dief1x (3, "internal error processing options");
+                    aa_strerr_dief1x (RC_FATAL_INTERNAL, "internal error processing options");
                 extra = 0;
                 break;
 
             default:
-                dieusage (1);
+                dieusage (RC_FATAL_USAGE);
         }
     }
     argc -= optind;
     argv += optind;
 
     if (!path_list && argc < 1)
-        dieusage (1);
+        dieusage (RC_FATAL_USAGE);
 
     r = aa_init_repo (path_repo,
             (flags & AA_FLAG_UPGRADE_SERVICEDIR) ? AA_REPO_WRITE : AA_REPO_CREATE);
     if (r < 0)
     {
         if (r == -ERR_IO_REPODIR)
-            aa_strerr_diefu2sys (5, "create repository ", path_repo);
+            aa_strerr_diefu2sys (RC_FATAL_INIT_REPO, "create repository ", path_repo);
         else if (r == -ERR_IO_SCANDIR)
-            aa_strerr_diefu3sys (5, "create scandir ", path_repo, "/" AA_SCANDIR_DIRNAME);
+            aa_strerr_diefu3sys (RC_FATAL_INIT_REPO, "create scandir ", path_repo, "/" AA_SCANDIR_DIRNAME);
         else
-            aa_strerr_diefu2sys (5, "init repository ", path_repo);
+            aa_strerr_diefu2sys (RC_FATAL_INIT_REPO, "init repository ", path_repo);
     }
 
     /* process listdir (path_list) first, to ensure if the service was also
@@ -372,7 +372,7 @@ main (int argc, char * const argv[])
         r = aa_scan_dir (&sa_pl, 0, it_list, NULL);
         if (r < 0)
             /* -r == ERR_IO, since it_list always returns 0 */
-            aa_strerr_diefu3sys (-r, "read list directory ",
+            aa_strerr_diefu3sys (RC_FATAL_IO, "read list directory ",
                     (*path_list != '/' && *path_list != '.') ? LISTDIR_PREFIX : path_list,
                     (*path_list != '/' && *path_list != '.') ? path_list : "");
     }
@@ -381,7 +381,7 @@ main (int argc, char * const argv[])
         if (str_equal (argv[i], "-"))
         {
             if (process_names_from_stdin ((names_cb) enable_service, NULL) < 0)
-                aa_strerr_diefu1sys (ERR_IO, "process names from stdin");
+                aa_strerr_diefu1sys (RC_FATAL_IO, "process names from stdin");
         }
         else
             enable_service (argv[i], 0);
@@ -443,12 +443,16 @@ main (int argc, char * const argv[])
     {
         r = s6_svc_writectl (AA_SCANDIR_DIRNAME, S6_SVSCAN_CTLDIR, "a", 1);
         if (r < 0)
-            aa_strerr_diefu1sys (6, "alarm s6-svscan");
+            aa_strerr_diefu1sys (RC_FATAL_ALARM_S6, "alarm s6-svscan");
         else if (r == 0)
-            aa_strerr_diefu1x (6, "alarm s6-svscan: supervisor not listening");
+            aa_strerr_diefu1x (RC_FATAL_ALARM_S6, "alarm s6-svscan: supervisor not listening");
     }
 
-    r = (ga_failed.len == 0) ? 0 : 23;
+    r = RC_OK;
+    if (ga_failed.len > 0)
+        r |= RC_ST_FAILED;
+    if (ga_unknown.len > 0)
+        r |= RC_ST_UNKNOWN;
 
     genalloc_free (size_t, &ga_failed);
     genalloc_free (size_t, &ga_unknown);
diff --git a/src/anopa/aa-reset.c b/src/anopa/aa-reset.c
index 73bd368..7250184 100644
--- a/src/anopa/aa-reset.c
+++ b/src/anopa/aa-reset.c
@@ -37,6 +37,7 @@
 #include <anopa/service_status.h>
 #include <anopa/err.h>
 #include "util.h"
+#include "common.h"
 
 enum
 {
@@ -46,6 +47,8 @@ enum
     MODE_STOPPED
 };
 
+static int rc = RC_OK;
+
 static void
 reset_service (const char *name, intptr_t mode)
 {
@@ -59,6 +62,7 @@ reset_service (const char *name, intptr_t mode)
     if (r < 0)
     {
         aa_put_err (name, errmsg[-r], 1);
+        rc |= (r == -ERR_UNKNOWN) ? RC_ST_UNKNOWN : RC_ST_FAILED;
         return;
     }
 
@@ -66,6 +70,7 @@ reset_service (const char *name, intptr_t mode)
     if (r < 0)
     {
         aa_put_err (name, errmsg[-r], 1);
+        rc |= RC_ST_FAILED;
         return;
     }
 
@@ -77,12 +82,15 @@ reset_service (const char *name, intptr_t mode)
         aa_put_err (name, "Failed to read service status file: ", 0);
         aa_bs (AA_ERR, strerror (e));
         aa_end_err ();
+
+        rc |= RC_ST_FAILED;
         return;
     }
 
     if (s->st.type == AA_TYPE_LONGRUN)
     {
         aa_put_err (name, "Can only reset one-shot services", 1);
+        rc |= RC_ST_FAILED;
         return;
     }
 
@@ -117,6 +125,8 @@ reset_service (const char *name, intptr_t mode)
         aa_put_err (name, "Failed to write service status file: ", 0);
         aa_bs (AA_ERR, strerror (e));
         aa_end_err ();
+
+        rc |= RC_ST_FAILED;
     }
     else
     {
@@ -187,7 +197,7 @@ main (int argc, char * const argv[])
                 break;
 
             case 'h':
-                dieusage (0);
+                dieusage (RC_OK);
 
             case 'O':
                 aa_set_log_file_or_die (optarg);
@@ -206,27 +216,27 @@ main (int argc, char * const argv[])
                 aa_die_version ();
 
             default:
-                dieusage (1);
+                dieusage (RC_FATAL_USAGE);
         }
     }
     argc -= optind;
     argv += optind;
 
     if (argc < 1 || mode == MODE_NONE)
-        dieusage (1);
+        dieusage (RC_FATAL_USAGE);
 
     r = aa_init_repo (path_repo, AA_REPO_READ);
     if (r < 0)
-        aa_strerr_diefu2sys (2, "init repository ", path_repo);
+        aa_strerr_diefu2sys (RC_FATAL_INIT_REPO, "init repository ", path_repo);
 
     for (i = 0; i < argc; ++i)
         if (str_equal (argv[i], "-"))
         {
             if (process_names_from_stdin ((names_cb) reset_service, (void *) mode) < 0)
-                aa_strerr_diefu1sys (ERR_IO, "process names from stdin");
+                aa_strerr_diefu1sys (RC_FATAL_IO, "process names from stdin");
         }
         else
             reset_service (argv[i], mode);
 
-    return 0;
+    return rc;
 }
diff --git a/src/anopa/aa-start.c b/src/anopa/aa-start.c
index 5ed7d70..4a494b7 100644
--- a/src/anopa/aa-start.c
+++ b/src/anopa/aa-start.c
@@ -67,12 +67,12 @@ static genalloc ga_io = GENALLOC_ZERO;
 static aa_mode mode = AA_MODE_START;
 static int no_wants = 0;
 static int verbose = 0;
-static int rc = 0;
+static int rc = RC_OK;
 
 void
 check_essential (int si)
 {
-    if (rc == 0)
+    if (!(rc & RC_ST_ESSENTIAL))
     {
         struct stat st;
         const char *name = aa_service_name (aa_service (si));
@@ -93,7 +93,7 @@ check_essential (int si)
             }
         }
         else
-            rc = 1;
+            rc |= RC_ST_ESSENTIAL;
     }
 }
 
@@ -276,7 +276,7 @@ main (int argc, char * const argv[])
                 break;
 
             case 'h':
-                dieusage (0);
+                dieusage (RC_OK);
 
             case 'l':
                 unslash (optarg);
@@ -301,7 +301,7 @@ main (int argc, char * const argv[])
 
             case 't':
                 if (!uint0_scan (optarg, &aa_secs_timeout))
-                    aa_strerr_diefu2sys (ERR_IO, "set default timeout to ", optarg);
+                    aa_strerr_diefu2sys (RC_FATAL_USAGE, "set default timeout to ", optarg);
                 break;
 
             case 'V':
@@ -316,7 +316,7 @@ main (int argc, char * const argv[])
                 break;
 
             default:
-                dieusage (1);
+                dieusage (RC_FATAL_USAGE);
         }
     }
     argc -= optind;
@@ -326,10 +326,10 @@ main (int argc, char * const argv[])
     is_utf8 = is_locale_utf8 ();
 
     if (!path_list && argc < 1)
-        dieusage (1);
+        dieusage (RC_FATAL_USAGE);
 
     if (aa_init_repo (path_repo, (mode & AA_MODE_IS_DRY) ? AA_REPO_READ : AA_REPO_WRITE) < 0)
-        aa_strerr_diefu2sys (ERR_IO, "init repository ", path_repo);
+        aa_strerr_diefu2sys (RC_FATAL_INIT_REPO, "init repository ", path_repo);
 
     if (path_list)
     {
@@ -342,7 +342,7 @@ main (int argc, char * const argv[])
         r = aa_scan_dir (&sa, 1, it_start, NULL);
         stralloc_free (&sa);
         if (r < 0)
-            aa_strerr_diefu3sys (-r, "read list directory ",
+            aa_strerr_diefu3sys (RC_FATAL_IO, "read list directory ",
                     (*path_list != '/' && *path_list != '.') ? LISTDIR_PREFIX : path_list,
                     (*path_list != '/' && *path_list != '.') ? path_list : "");
     }
@@ -353,7 +353,7 @@ main (int argc, char * const argv[])
         if (str_equal (argv[i], "-"))
         {
             if (process_names_from_stdin ((names_cb) add_service, NULL) < 0)
-                aa_strerr_diefu1sys (ERR_IO, "process names from stdin");
+                aa_strerr_diefu1sys (RC_FATAL_IO, "process names from stdin");
         }
         else
             add_service (argv[i], NULL);
@@ -374,6 +374,13 @@ main (int argc, char * const argv[])
         aa_show_stat_names (aa_names.s, &ga_skipped, "Skipped", ANSI_HIGHLIGHT_YELLOW_ON);
     }
 
+    if (ga_timedout.len + ga_failed.len + ga_depend.len + ga_io.len > 0)
+        rc |= RC_ST_FAILED;
+    if (ga_unknown.len > 0)
+        rc |= RC_ST_UNKNOWN;
+    if (ga_skipped.len > 0)
+        rc |= RC_ST_SKIPPED;
+
     genalloc_free (int, &ga_timedout);
     genalloc_free (int, &ga_failed);
     genalloc_free (int, &ga_depend);
diff --git a/src/anopa/aa-status.c b/src/anopa/aa-status.c
index 521a0d5..acb575e 100644
--- a/src/anopa/aa-status.c
+++ b/src/anopa/aa-status.c
@@ -103,6 +103,8 @@ static unsigned int filter_type = FILTER_NONE;
 static unsigned int filter_status = FILTER_NONE;
 static unsigned int sort_order = SORT_ASC;
 
+static int rc = RC_OK;
+
 static size_t put_s_max (const char *s, size_t max, int pad);
 
 static void
@@ -613,6 +615,7 @@ load_service (const char *name, struct config *cfg)
     if (r < 0)
     {
         aa_put_err (name, errmsg[-r], 1);
+        rc |= (r == -ERR_UNKNOWN) ? RC_ST_UNKNOWN : RC_ST_FAILED;
         return -1;
     }
 
@@ -620,6 +623,7 @@ load_service (const char *name, struct config *cfg)
     if (r < 0)
     {
         aa_put_err (name, errmsg[-r], 1);
+        rc |= RC_ST_FAILED;
         return -1;
     }
 
@@ -631,6 +635,8 @@ load_service (const char *name, struct config *cfg)
         aa_put_err (name, "Failed to read service status file: ", 0);
         aa_bs (AA_ERR, strerror (e));
         aa_end_err ();
+
+        rc |= RC_ST_FAILED;
         return -1;
     }
 
@@ -651,6 +657,8 @@ load_service (const char *name, struct config *cfg)
                 aa_put_err (name, "Unable to read s6 status: ", 0);
                 aa_bs (AA_ERR, strerror (e));
                 aa_end_err ();
+
+                rc |= RC_ST_FAILED;
                 return -1;
             }
         }
@@ -706,7 +714,7 @@ it_all (direntry *d, void *data)
             stralloc_catb (&satmp, "/log/run", strlen ("/log/run") + 1);
             r = access (satmp.s + l, F_OK);
             if (r < 0 && (errno != ENOTDIR && errno != ENOENT))
-                aa_strerr_diefu3sys (ERR_IO, "load service: access(", satmp.s + l, ")");
+                aa_strerr_diefu3sys (RC_FATAL_IO, "load service: access(", satmp.s + l, ")");
             else if (r == 0)
             {
                 satmp.s[satmp.len - 5] = '\0';
@@ -857,11 +865,11 @@ main (int argc, char * const argv[])
 
             case 'f':
                 if (!set_filter (optarg))
-                    aa_strerr_diefu3sys (1, "set filter '", optarg, "'");
+                    aa_strerr_diefu3sys (RC_FATAL_USAGE, "set filter '", optarg, "'");
                 break;
 
             case 'h':
-                dieusage (0);
+                dieusage (RC_OK);
 
             case 'l':
                 unslash (optarg);
@@ -903,7 +911,7 @@ main (int argc, char * const argv[])
                 else
                 {
                     errno = EINVAL;
-                    aa_strerr_diefu3sys (1, "set sort order '", optarg, "'");
+                    aa_strerr_diefu3sys (RC_FATAL_USAGE, "set sort order '", optarg, "'");
                 }
                 break;
 
@@ -911,18 +919,18 @@ main (int argc, char * const argv[])
                 aa_die_version ();
 
             default:
-                dieusage (1);
+                dieusage (RC_FATAL_USAGE);
         }
     }
     argc -= optind;
     argv += optind;
 
     if (!all && !path_list && argc < 1)
-        dieusage (1);
+        dieusage (RC_FATAL_USAGE);
 
     r = aa_init_repo (path_repo, AA_REPO_READ);
     if (r < 0)
-        aa_strerr_diefu2sys (2, "init repository ", path_repo);
+        aa_strerr_diefu2sys (RC_FATAL_INIT_REPO, "init repository ", path_repo);
 
     if (cfg.mode == MODE_LIST)
     {
@@ -946,7 +954,7 @@ main (int argc, char * const argv[])
         stralloc_catb (&sa, ".", 2);
         r = aa_scan_dir (&sa, 0, it_all, &cfg);
         if (r < 0)
-            aa_strerr_diefu2sys (-r, "scan repo directory ", path_repo);
+            aa_strerr_diefu2sys (RC_FATAL_IO, "scan repo directory ", path_repo);
     }
     else if (path_list)
     {
@@ -959,7 +967,7 @@ main (int argc, char * const argv[])
         r = aa_scan_dir (&sa, 1, it_listdir, &cfg);
         stralloc_free (&sa);
         if (r < 0)
-            aa_strerr_diefu3sys (-r, "read list directory ",
+            aa_strerr_diefu3sys (RC_FATAL_IO, "read list directory ",
                     (*path_list != '/' && *path_list != '.') ? LISTDIR_PREFIX : path_list,
                     (*path_list != '/' && *path_list != '.') ? path_list : "");
     }
@@ -968,7 +976,7 @@ main (int argc, char * const argv[])
             if (str_equal (argv[i], "-"))
             {
                 if (process_names_from_stdin ((names_cb) load_service, &cfg) < 0)
-                    aa_strerr_diefu1sys (ERR_IO, "process names from stdin");
+                    aa_strerr_diefu1sys (RC_FATAL_IO, "process names from stdin");
             }
             else
                 load_service (argv[i], &cfg);
@@ -981,5 +989,5 @@ main (int argc, char * const argv[])
         status_service (&genalloc_s (struct serv, &ga_serv)[i], &cfg);
 
     aa_bb_flush (AA_OUT, "", 0);
-    return 0;
+    return rc;
 }
diff --git a/src/anopa/aa-stop.c b/src/anopa/aa-stop.c
index ebaa258..ede40ea 100644
--- a/src/anopa/aa-stop.c
+++ b/src/anopa/aa-stop.c
@@ -58,7 +58,7 @@ static genalloc ga_depend = GENALLOC_ZERO;
 static genalloc ga_io = GENALLOC_ZERO;
 static aa_mode mode = AA_MODE_STOP;
 static int verbose = 0;
-static int rc = 0;
+static int rc = RC_OK;
 static const char *skip = NULL;
 
 void
@@ -131,7 +131,7 @@ preload_service (const char *name)
             stralloc_catb (&satmp, "/log/run", strlen ("/log/run") + 1);
             r = access (satmp.s + l, F_OK);
             if (r < 0 && (errno != ENOTDIR && errno != ENOENT))
-                aa_strerr_diefu3sys (ERR_IO, "preload services: access(", satmp.s + l, ")");
+                aa_strerr_diefu3sys (RC_FATAL_IO, "preload services: access(", satmp.s + l, ")");
             else if (r == 0)
             {
                 satmp.s[satmp.len - 5] = '\0';
@@ -211,7 +211,7 @@ add_service (const char *name, void *data)
             if (aa_service (si)->st.code != ERR_NOT_UP)
             {
                 errno = EINVAL;
-                aa_strerr_diefu2sys (ERR_IO, "add service ", name);
+                aa_strerr_diefu2sys (RC_FATAL_IO, "add service ", name);
             }
 
             if (!(mode & AA_MODE_IS_DRY))
@@ -377,7 +377,7 @@ main (int argc, char * const argv[])
 
             case 't':
                 if (!uint0_scan (optarg, &aa_secs_timeout))
-                    aa_strerr_diefu2sys (ERR_IO, "set default timeout to ", optarg);
+                    aa_strerr_diefu2sys (RC_FATAL_USAGE, "set default timeout to ", optarg);
                 break;
 
             case 'V':
@@ -407,7 +407,7 @@ main (int argc, char * const argv[])
     }
 
     if (aa_init_repo (path_repo, (mode & AA_MODE_IS_DRY) ? AA_REPO_READ : AA_REPO_WRITE) < 0)
-        aa_strerr_diefu2sys (ERR_IO, "init repository ", path_repo);
+        aa_strerr_diefu2sys (RC_FATAL_INIT_REPO, "init repository ", path_repo);
 
     /* let's "preload" every services from the repo. This will have everything
      * in tmp list, either LOAD_DONE when up, or LOAD_FAIL when not
@@ -424,7 +424,7 @@ main (int argc, char * const argv[])
         r = aa_scan_dir (&sa, 0, it_preload, NULL);
         stralloc_free (&sa);
         if (r < 0)
-            aa_strerr_diefu1sys (-r, "read repository directory");
+            aa_strerr_diefu1sys (RC_FATAL_IO, "read repository directory");
     }
 
     if (all)
@@ -477,7 +477,7 @@ main (int argc, char * const argv[])
         r = aa_scan_dir (&sa, 1, it_stop, NULL);
         stralloc_free (&sa);
         if (r < 0)
-            aa_strerr_diefu3sys (-r, "read list directory ",
+            aa_strerr_diefu3sys (RC_FATAL_IO, "read list directory ",
                     (*path_list != '/' && *path_list != '.') ? LISTDIR_PREFIX : path_list,
                     (*path_list != '/' && *path_list != '.') ? path_list : "");
     }
@@ -486,7 +486,7 @@ main (int argc, char * const argv[])
             if (str_equal (argv[i], "-"))
             {
                 if (process_names_from_stdin ((names_cb) add_service, NULL) < 0)
-                    aa_strerr_diefu1sys (ERR_IO, "process names from stdin");
+                    aa_strerr_diefu1sys (RC_FATAL_IO, "process names from stdin");
             }
             else
                 add_service (argv[i], NULL);
@@ -508,6 +508,11 @@ main (int argc, char * const argv[])
         aa_show_stat_names (aa_names.s, &ga_unknown, "Unknown", ANSI_HIGHLIGHT_RED_ON);
     }
 
+    if (ga_timedout.len + ga_failed.len + ga_depend.len + ga_io.len > 0)
+        rc |= RC_ST_FAILED;
+    if (ga_unknown.len > 0)
+        rc |= RC_ST_UNKNOWN;
+
     genalloc_free (int, &ga_timedout);
     genalloc_free (int, &ga_failed);
     genalloc_free (int, &ga_depend);
diff --git a/src/anopa/common.h b/src/anopa/common.h
index 51241af..2745b12 100644
--- a/src/anopa/common.h
+++ b/src/anopa/common.h
@@ -2,7 +2,7 @@
  * anopa - Copyright (C) 2015-2017 Olivier Brunel
  *
  * common.h
- * Copyright (C) 2015 Olivier Brunel <jjk@jjacky.com>
+ * Copyright (C) 2015-2018 Olivier Brunel <jjk@jjacky.com>
  *
  * This file is part of anopa.
  *
@@ -23,6 +23,16 @@
 #ifndef _AA_COMMON_H
 #define _AA_COMMON_H
 
+#include <anopa/rc.h>
+
 #define LISTDIR_PREFIX      "/etc/anopa/listdirs/"
 
+enum
+{
+    RC_ST_UNKNOWN       = 1 << 1,   /* at least 1 service unknown */
+    RC_ST_SKIPPED       = 1 << 2,   /* at least 1 service skipped (start) */
+    RC_ST_FAILED        = 1 << 3,   /* at least 1 service failed */
+    RC_ST_ESSENTIAL     = 1 << 4    /* at least 1 failed service was essential */
+};
+
 #endif /* _AA_COMMON_H */
diff --git a/src/anopa/start-stop.c b/src/anopa/start-stop.c
index ae87d59..b50b5d3 100644
--- a/src/anopa/start-stop.c
+++ b/src/anopa/start-stop.c
@@ -40,6 +40,7 @@
 #include <anopa/ga_int_list.h>
 #include <anopa/output.h>
 #include <anopa/err.h>
+#include <anopa/rc.h>
 #include "start-stop.h"
 
 genalloc ga_iop = GENALLOC_ZERO;
@@ -882,7 +883,7 @@ handle_signals (aa_mode mode)
         switch (c)
         {
             case -1:
-                aa_strerr_diefu1sys (ERR_IO, "selfpipe_read");
+                aa_strerr_diefu1sys (RC_FATAL_IO, "selfpipe_read");
 
             case 0:
                 return r;
@@ -918,7 +919,7 @@ handle_signals (aa_mode mode)
                 break;
 
             default:
-                aa_strerr_dief1x (ERR_IO, "internal error: invalid selfpipe_read value");
+                aa_strerr_dief1x (RC_FATAL_INTERNAL, "internal error: invalid selfpipe_read value");
         }
     }
 }
@@ -1196,11 +1197,11 @@ mainloop (aa_mode mode, aa_scan_cb scan_cb)
     size_t i;
 
     if (!genalloc_ready_tuned (iopause_fd, &ga_iop, 2, 0, 0, 1))
-        aa_strerr_diefu1sys (ERR_IO, "allocate iopause_fd");
+        aa_strerr_diefu1sys (RC_FATAL_IO, "allocate iopause_fd");
 
     iop.fd = selfpipe_init ();
     if (iop.fd == -1)
-        aa_strerr_diefu1sys (ERR_IO, "init selfpipe");
+        aa_strerr_diefu1sys (RC_FATAL_IO, "init selfpipe");
     iop.events = IOPAUSE_READ;
     genalloc_append (iopause_fd, &ga_iop, &iop);
 
@@ -1209,7 +1210,7 @@ mainloop (aa_mode mode, aa_scan_cb scan_cb)
     sig_ignore (SIGINT);
     iop.fd = aa_prepare_mainlist (prepare_cb, exec_cb);
     if (iop.fd < 0)
-        aa_strerr_diefu1sys (ERR_IO, "prepare mainlist");
+        aa_strerr_diefu1sys (RC_FATAL_IO, "prepare mainlist");
     else if (iop.fd == 0)
         iop.fd = -1;
     genalloc_append (iopause_fd, &ga_iop, &iop);
@@ -1221,7 +1222,7 @@ mainloop (aa_mode mode, aa_scan_cb scan_cb)
     sigaddset (&set, SIGINT);
     sigaddset (&set, SIGWINCH);
     if (selfpipe_trapset (&set) < 0)
-        aa_strerr_diefu1sys (ERR_IO, "trap signals");
+        aa_strerr_diefu1sys (RC_FATAL_IO, "trap signals");
 
     /* start what we can */
     for (i = 0; i < genalloc_len (int, &aa_main_list); ++i)
@@ -1267,7 +1268,7 @@ mainloop (aa_mode mode, aa_scan_cb scan_cb)
         nb_iop = genalloc_len (iopause_fd, &ga_iop);
         r = iopause_g (genalloc_s (iopause_fd, &ga_iop), nb_iop, &iol_deadline);
         if (r < 0)
-            aa_strerr_diefu1sys (ERR_IO, "iopause");
+            aa_strerr_diefu1sys (RC_FATAL_IO, "iopause");
         else if (r == 0)
         {
             if (ms1 < 0 || ms2 < ms1)
@@ -1301,7 +1302,7 @@ mainloop (aa_mode mode, aa_scan_cb scan_cb)
             if (iofd->revents & IOPAUSE_READ)
                 scan += handle_signals (mode);
             else if (iofd->revents & IOPAUSE_EXCEPT)
-                aa_strerr_diefu1sys (ERR_IO, "iopause: selfpipe error");
+                aa_strerr_diefu1sys (RC_FATAL_IO, "iopause: selfpipe error");
 
             iofd = &genalloc_s (iopause_fd, &ga_iop)[1];
             if (iofd->fd <= 0)
@@ -1333,7 +1334,7 @@ mainloop (aa_mode mode, aa_scan_cb scan_cb)
                 }
             }
             else if (iofd->revents & IOPAUSE_EXCEPT)
-                aa_strerr_diefu1sys (ERR_IO, "iopause: longrun pipe error");
+                aa_strerr_diefu1sys (RC_FATAL_IO, "iopause: longrun pipe error");
 
 scan:
             if (scan > 0)
diff --git a/src/include/anopa/common.h b/src/include/anopa/common.h
index 58ec32c..4b08d3b 100644
--- a/src/include/anopa/common.h
+++ b/src/include/anopa/common.h
@@ -2,7 +2,7 @@
  * anopa - Copyright (C) 2015-2017 Olivier Brunel
  *
  * common.h
- * Copyright (C) 2015 Olivier Brunel <jjk@jjacky.com>
+ * Copyright (C) 2015-2018 Olivier Brunel <jjk@jjacky.com>
  *
  * This file is part of anopa.
  *
@@ -23,6 +23,8 @@
 #ifndef AA_COMMON_H
 #define AA_COMMON_H
 
+#include <anopa/rc.h>
+
 #define AA_SCANDIR_DIRNAME      ".scandir"
 
 void aa_die_usage (int rc, const char *usage, const char *details);
diff --git a/src/include/anopa/rc.h b/src/include/anopa/rc.h
new file mode 100644
index 0000000..d6d1f92
--- /dev/null
+++ b/src/include/anopa/rc.h
@@ -0,0 +1,49 @@
+/*
+ * anopa - Copyright (C) 2015-2017 Olivier Brunel
+ *
+ * rc.h
+ * Copyright (C) 2018 Olivier Brunel <jjk@jjacky.com>
+ *
+ * This file is part of anopa.
+ *
+ * anopa is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * anopa is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * anopa. If not, see http://www.gnu.org/licenses/
+ */
+
+#ifndef AA_RC_H
+#define AA_RC_H
+
+/* we have 127 values for fatal errors, common to all binaries, listed below.
+ * They'll be returned moved 1 bit to the right with the first bit always set,
+ * indicating a fatal error.
+ * Each binary can then have 127 values to report different "statuses". Those
+ * will be returned moved 1 bit to the right, first bit not set. They can be
+ * binary-specific enums, or bitwise combinations.
+ */
+
+enum
+{
+    RC_OK = 0,
+
+    RC_FATAL_USAGE      = (0 << 1 | 1),
+    RC_FATAL_INIT_REPO  = (1 << 1 | 1),     /* only by "commands" */
+    RC_FATAL_IO         = (2 << 1 | 1),
+    RC_FATAL_MEMORY     = (3 << 1 | 1),
+    RC_FATAL_ALARM_S6   = (4 << 1 | 1),     /* only by aa-enable */
+
+    RC_FATAL_EXEC       = (55 << 1 | 1),    /* so rc=111; execline style */
+
+    RC_FATAL_INTERNAL   = (127 << 1 | 1)    /* shouldn't happen: broken state, etc */
+};
+
+#endif /* AA_RC_H */
diff --git a/src/libanopa/exec_oneshot.c b/src/libanopa/exec_oneshot.c
index 292f74e..900c0cc 100644
--- a/src/libanopa/exec_oneshot.c
+++ b/src/libanopa/exec_oneshot.c
@@ -2,7 +2,7 @@
  * anopa - Copyright (C) 2015-2017 Olivier Brunel
  *
  * exec_oneshot.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 @@
 #include <anopa/service.h>
 #include <anopa/err.h>
 #include <anopa/output.h>
+#include <anopa/rc.h>
 #include "service_internal.h"
 
 int
@@ -226,7 +227,7 @@ _exec_oneshot (int si, aa_mode mode)
             fd_write (p_int[1], "p", 1);
             uint32_pack (buf_e, e);
             fd_write (p_int[1], buf_e, UINT32_FMT);
-            aa_strerr_diefu1sys (ERR_IO, "set up pipes");
+            aa_strerr_diefu1sys (RC_FATAL_IO, "set up pipes");
         }
 
         if (chdir (PROG) < 0)
@@ -235,7 +236,7 @@ _exec_oneshot (int si, aa_mode mode)
             fd_write (p_int[1], "c", 1);
             uint32_pack (buf_e, e);
             fd_write (p_int[1], buf_e, UINT32_FMT);
-            aa_strerr_diefu1sys (ERR_IO, "get into service directory");
+            aa_strerr_diefu1sys (RC_FATAL_IO, "get into service directory");
         }
 
         buf[l_sn - 1] = '.';
@@ -245,7 +246,7 @@ _exec_oneshot (int si, aa_mode mode)
         fd_write (p_int[1], "e", 1);
         uint32_pack (buf_e, e);
         fd_write (p_int[1], buf_e, UINT32_FMT);
-        aa_strerr_dieexec (ERR_IO, filename);
+        aa_strerr_dieexec (RC_FATAL_EXEC, filename);
     }
 
     fd_close (p_int[1]);
diff --git a/src/libanopa/output.c b/src/libanopa/output.c
index aaaa9e7..ea9050a 100644
--- a/src/libanopa/output.c
+++ b/src/libanopa/output.c
@@ -26,6 +26,7 @@
 #include <skalibs/djbunix.h>
 #include <skalibs/types.h>
 #include <anopa/output.h>
+#include <anopa/rc.h>
 #include <anopa/err.h> /* ERR_IO */
 
 static int istty[2] = { -1, 0 };
@@ -107,9 +108,9 @@ void aa_set_log_file_or_die (const char *file_or_fd)
     if (r < 0)
     {
         if (r == -1)
-            aa_strerr_diefu3sys (1, "set logfile to FD '", file_or_fd, "'");
+            aa_strerr_diefu3sys (RC_FATAL_USAGE, "set logfile to FD '", file_or_fd, "'");
         else
-            aa_strerr_diefu3sys (ERR_IO, "set logfile to '", file_or_fd, "'");
+            aa_strerr_diefu3sys (RC_FATAL_IO, "set logfile to '", file_or_fd, "'");
     }
 }
 
diff --git a/src/utils/aa-chroot.c b/src/utils/aa-chroot.c
index 8461bd5..9d45e2e 100644
--- a/src/utils/aa-chroot.c
+++ b/src/utils/aa-chroot.c
@@ -65,7 +65,7 @@ main (int argc, char * const argv[], char * const envp[])
                 break;
 
             case 'h':
-                dieusage (0);
+                dieusage (RC_OK);
 
             case 'O':
                 aa_set_log_file_or_die (optarg);
@@ -75,21 +75,21 @@ main (int argc, char * const argv[], char * const envp[])
                 aa_die_version ();
 
             default:
-                dieusage (1);
+                dieusage (RC_FATAL_USAGE);
         }
     }
     argc -= optind;
     argv += optind;
 
     if (argc < 2)
-        dieusage (1);
+        dieusage (RC_FATAL_USAGE);
 
     if (chdir (argv[0]) < 0)
-        aa_strerr_diefu2sys (2, "chdir to ", argv[0]);
+        aa_strerr_diefu2sys (RC_FATAL_IO, "chdir to ", argv[0]);
     if (chroot (".") < 0)
-        aa_strerr_diefu1sys (3, "chroot");
+        aa_strerr_diefu1sys (RC_FATAL_IO, "chroot");
     if (chdir ("/") < 0)
-        aa_strerr_diefu1sys (3, "chdir to new root");
+        aa_strerr_diefu1sys (RC_FATAL_IO, "chdir to new root");
     pathexec_run (argv[1], (char const * const *) argv + 1, (char const * const *) envp);
-    aa_strerr_dieexec (4, argv[1]);
+    aa_strerr_dieexec (RC_FATAL_EXEC, argv[1]);
 }
diff --git a/src/utils/aa-ctty.c b/src/utils/aa-ctty.c
index 6c72230..83b4517 100644
--- a/src/utils/aa-ctty.c
+++ b/src/utils/aa-ctty.c
@@ -72,11 +72,11 @@ main (int argc, char * const argv[], char const * const *envp)
 
             case 'f':
                 if (!uint0_scan (optarg, (unsigned int *) &fd))
-                    aa_strerr_diefu1sys (1, "set fd");
+                    aa_strerr_diefu1sys (RC_FATAL_USAGE, "set fd");
                 break;
 
             case 'h':
-                dieusage (0);
+                dieusage (RC_OK);
 
             case 'O':
                 aa_set_log_file_or_die (optarg);
@@ -90,18 +90,18 @@ main (int argc, char * const argv[], char const * const *envp)
                 aa_die_version ();
 
             default:
-                dieusage (1);
+                dieusage (RC_FATAL_USAGE);
         }
     }
     argc -= optind;
     argv += optind;
 
     if (argc == 0)
-        dieusage (1);
+        dieusage (RC_FATAL_USAGE);
 
     if (ioctl (fd, TIOCSCTTY, steal) < 0)
         aa_strerr_warnu1sys ("set controlling terminal");
 
     pathexec_run (argv[0], (char const * const *) argv, envp);
-    aa_strerr_dieexec (111, argv[0]);
+    aa_strerr_dieexec (RC_FATAL_EXEC, argv[0]);
 }
diff --git a/src/utils/aa-echo.c b/src/utils/aa-echo.c
index 674df42..9fbbf33 100644
--- a/src/utils/aa-echo.c
+++ b/src/utils/aa-echo.c
@@ -111,7 +111,7 @@ main (int argc, char * const argv[])
                 break;
 
             case 'h':
-                dieusage (0);
+                dieusage (RC_OK);
 
             case 'n':
                 put = NULL;
@@ -141,14 +141,14 @@ main (int argc, char * const argv[])
                 break;
 
             default:
-                dieusage (1);
+                dieusage (RC_FATAL_USAGE);
         }
     }
     argc -= optind;
     argv += optind;
 
     if (argc < 1)
-        dieusage (1);
+        dieusage (RC_FATAL_USAGE);
 
     if (blank)
         aa_bs (where, "\n");
@@ -178,5 +178,5 @@ main (int argc, char * const argv[])
     }
     aa_bs_end (where);
 
-    return 0;
+    return RC_OK;
 }
diff --git a/src/utils/aa-hiercopy.c b/src/utils/aa-hiercopy.c
index 816f4e6..ceb8aaa 100644
--- a/src/utils/aa-hiercopy.c
+++ b/src/utils/aa-hiercopy.c
@@ -31,6 +31,12 @@
 #define NULL    (void *) 0
 #endif
 
+enum {
+    RC_ST_LSTAT     = 1 << 1,
+    RC_ST_RESOLVE   = 2 << 1,
+    RC_ST_COPY      = 3 << 1
+};
+
 static void
 dieusage (int rc)
 {
@@ -68,7 +74,7 @@ main (int argc, char * const argv[])
                 break;
 
             case 'h':
-                dieusage (0);
+                dieusage (RC_OK);
 
             case 'O':
                 aa_set_log_file_or_die (optarg);
@@ -78,14 +84,14 @@ main (int argc, char * const argv[])
                 aa_die_version ();
 
             default:
-                dieusage (1);
+                dieusage (RC_FATAL_USAGE);
         }
     }
     argc -= optind;
     argv += optind;
 
     if (argc != 2)
-        dieusage (1);
+        dieusage (RC_FATAL_USAGE);
 
     {
         stralloc sa = STRALLOC_ZERO;
@@ -93,16 +99,16 @@ main (int argc, char * const argv[])
         char *sce = argv[0];
 
         if (lstat (sce, &st) < 0)
-            aa_strerr_diefu2sys (2, "lstat ", sce);
+            aa_strerr_diefu2sys (RC_ST_LSTAT, "lstat ", sce);
         if (S_ISLNK (st.st_mode))
         {
             if (sarealpath (&sa, sce) < 0)
-                aa_strerr_diefu2x (3, "resolve symlink ", sce);
+                aa_strerr_diefu2x (RC_ST_RESOLVE, "resolve symlink ", sce);
             sce = sa.s;
         }
 
         if (!hiercopy(sce, argv[1]))
-            aa_strerr_diefu4sys (4, "copy ", sce, " into ", argv[1]);
+            aa_strerr_diefu4sys (RC_ST_COPY, "copy ", sce, " into ", argv[1]);
         if (sa.a > 0)
             stralloc_free (&sa);
     }
diff --git a/src/utils/aa-incmdline.c b/src/utils/aa-incmdline.c
index 40cb83b..f24c4d6 100644
--- a/src/utils/aa-incmdline.c
+++ b/src/utils/aa-incmdline.c
@@ -28,6 +28,12 @@
 #include <anopa/common.h>
 #include <anopa/output.h>
 
+enum
+{
+    RC_ST_READ      = 1 << 1,
+    RC_ST_FAIL      = 2 << 1
+};
+
 static void
 dieusage (int rc)
 {
@@ -85,7 +91,7 @@ main (int argc, char * const argv[])
                 break;
 
             case 'h':
-                dieusage (0);
+                dieusage (RC_OK);
 
             case 'O':
                 aa_set_log_file_or_die (optarg);
@@ -103,7 +109,7 @@ main (int argc, char * const argv[])
                 if (!optarg)
                     safe = '/';
                 else if (!*optarg || optarg[1])
-                    dieusage (1);
+                    dieusage (RC_FATAL_USAGE);
                 else
                     safe = *optarg;
                 break;
@@ -112,17 +118,17 @@ main (int argc, char * const argv[])
                 aa_die_version ();
 
             default:
-                dieusage (1);
+                dieusage (RC_FATAL_USAGE);
         }
     }
     argc -= optind;
     argv += optind;
 
     if (argc < 1)
-        dieusage (1);
+        dieusage (RC_FATAL_USAGE);
 
     if (!openslurpclose (&sa, file))
-            aa_strerr_diefu2sys (2, "read ", file);
+            aa_strerr_diefu2sys (RC_ST_READ, "read ", file);
 
     len_arg = strlen (argv[0]);
     for (start = i = 0; i < sa.len; ++i)
@@ -136,12 +142,12 @@ main (int argc, char * const argv[])
             if (sa.s[i] != '=')
             {
                 if (found)
-                    return (req) ? 3 : 0;
+                    return (req) ? RC_ST_FAIL : RC_OK;
                 start = ++i;
                 goto next;
             }
             else if (found && quiet && !safe)
-                return (req) ? 3 : 0;
+                return (req) ? RC_ST_FAIL : RC_OK;
 
             start = ++i;
             if (sa.s[start] != '"')
@@ -161,15 +167,15 @@ main (int argc, char * const argv[])
                 if (len == sa.len - start)
                     --len;
                 if (safe && byte_chr (sa.s + start, len, safe) < len)
-                    return 3;
+                    return RC_ST_FAIL;
                 if (req && len == 0)
-                    return 3;
+                    return RC_ST_FAIL;
                 else if (!quiet)
                 {
                     aa_bb (AA_OUT, sa.s + start, len);
                     aa_bs_flush (AA_OUT, "\n");
                 }
-                return 0;
+                return RC_OK;
             }
 
             start += len;
@@ -180,5 +186,5 @@ next:
         }
     }
 
-    return 3;
+    return RC_ST_FAIL;
 }
diff --git a/src/utils/aa-kill.c b/src/utils/aa-kill.c
index 991c2b0..2677a52 100644
--- a/src/utils/aa-kill.c
+++ b/src/utils/aa-kill.c
@@ -156,7 +156,7 @@ main (int argc, char * const argv[])
                 break;
 
             case 'h':
-                dieusage (0);
+                dieusage (RC_OK);
 
             case 'k':
                 send.kill = 1;
@@ -182,14 +182,14 @@ main (int argc, char * const argv[])
                 aa_die_version ();
 
             default:
-                dieusage (1);
+                dieusage (RC_FATAL_USAGE);
         }
     }
     argc -= optind;
     argv += optind;
 
     if (argc > 0 || (!send.hup && !send.term && !send.kill))
-        dieusage (1);
+        dieusage (RC_FATAL_USAGE);
 
     if (send.skip_at)
     {
@@ -200,9 +200,9 @@ main (int argc, char * const argv[])
         ownpid[uint_fmt (ownpid, u)] = '\0';
 
         if (!stralloc_catb (&sa, "/proc", sizeof ("/proc")))
-            aa_strerr_diefu1sys (1, "stralloc_catb");
+            aa_strerr_diefu1sys (RC_FATAL_MEMORY, "stralloc_catb");
         if (aa_scan_dir (&sa, 0, it_kill, &sa) < 0)
-            aa_strerr_diefu1sys (1, "scan /proc");
+            aa_strerr_diefu1sys (RC_FATAL_IO, "scan /proc");
         stralloc_free (&sa);
     }
     else
@@ -224,5 +224,5 @@ main (int argc, char * const argv[])
             _kill (-1, SIGKILL);
     }
 
-    return 0;
+    return RC_OK;
 }
diff --git a/src/utils/aa-mount.c b/src/utils/aa-mount.c
index 3d1c93d..4836986 100644
--- a/src/utils/aa-mount.c
+++ b/src/utils/aa-mount.c
@@ -173,7 +173,7 @@ main (int argc, char * const argv[])
         {
             case 'B':
                 if (!add_option (&sa, &flags, "bind"))
-                    aa_strerr_diefu1sys (2, "build user options");
+                    aa_strerr_diefu1sys (RC_FATAL_MEMORY, "build user options");
                 break;
 
             case 'D':
@@ -185,11 +185,11 @@ main (int argc, char * const argv[])
                 break;
 
             case 'h':
-                dieusage (0);
+                dieusage (RC_OK);
 
             case 'M':
                 if (!add_option (&sa, &flags, "move"))
-                    aa_strerr_diefu1sys (2, "build user options");
+                    aa_strerr_diefu1sys (RC_FATAL_MEMORY, "build user options");
                 break;
 
             case 'O':
@@ -198,12 +198,12 @@ main (int argc, char * const argv[])
 
             case 'o':
                 if (!add_option (&sa, &flags, optarg))
-                    aa_strerr_diefu1sys (2, "build user options");
+                    aa_strerr_diefu1sys (RC_FATAL_MEMORY, "build user options");
                 break;
 
             case 'r':
                 if (!add_option (&sa, &flags, "ro"))
-                    aa_strerr_diefu1sys (2, "build user options");
+                    aa_strerr_diefu1sys (RC_FATAL_MEMORY, "build user options");
                 break;
 
             case 't':
@@ -215,25 +215,25 @@ main (int argc, char * const argv[])
 
             case 'w':
                 if (!add_option (&sa, &flags, "rw"))
-                    aa_strerr_diefu1sys (2, "build user options");
+                    aa_strerr_diefu1sys (RC_FATAL_MEMORY, "build user options");
                 break;
 
             default:
-                dieusage (1);
+                dieusage (RC_FATAL_USAGE);
         }
     }
     argc -= optind;
     argv += optind;
 
     if (argc < 2)
-        dieusage (1);
+        dieusage (RC_FATAL_USAGE);
 
     if (!stralloc_0 (&sa))
-        aa_strerr_diefu1sys (2, "build user options");
+        aa_strerr_diefu1sys (RC_FATAL_MEMORY, "build user options");
     if (mk && mkdir (argv[1], 0755) < 0 && errno != EEXIST)
-        aa_strerr_diefu4sys (2, "mkdir ", argv[1], " to mount ", argv[0]);
+        aa_strerr_diefu4sys (RC_FATAL_IO, "mkdir ", argv[1], " to mount ", argv[0]);
     if (mount (argv[0], argv[1], fstype, flags, sa.s) < 0)
-        aa_strerr_diefu4sys (3, "mount ", argv[0], " on ", argv[1]);
+        aa_strerr_diefu4sys (RC_FATAL_IO, "mount ", argv[0], " on ", argv[1]);
 
-    return 0;
+    return RC_OK;
 }
diff --git a/src/utils/aa-pivot.c b/src/utils/aa-pivot.c
index c5e7d3e..08f0646 100644
--- a/src/utils/aa-pivot.c
+++ b/src/utils/aa-pivot.c
@@ -67,7 +67,7 @@ main (int argc, char * const argv[])
                 break;
 
             case 'h':
-                dieusage (0);
+                dieusage (RC_OK);
 
             case 'O':
                 aa_set_log_file_or_die (optarg);
@@ -77,16 +77,16 @@ main (int argc, char * const argv[])
                 aa_die_version ();
 
             default:
-                dieusage (1);
+                dieusage (RC_FATAL_USAGE);
         }
     }
     argc -= optind;
     argv += optind;
 
     if (argc < 2)
-        dieusage (1);
+        dieusage (RC_FATAL_USAGE);
 
     if (pivot_root (argv[0], argv[1]) < 0)
-        aa_strerr_diefu2sys (2, "pivot into ", argv[0]);
-    return 0;
+        aa_strerr_diefu2sys (RC_FATAL_IO, "pivot into ", argv[0]);
+    return RC_OK;
 }
diff --git a/src/utils/aa-reboot.c b/src/utils/aa-reboot.c
index a23862d..0b6a3fd 100644
--- a/src/utils/aa-reboot.c
+++ b/src/utils/aa-reboot.c
@@ -83,7 +83,7 @@ main (int argc, char * const argv[])
                 break;
 
             case 'h':
-                dieusage (0);
+                dieusage (RC_OK);
 
             case 'O':
                 aa_set_log_file_or_die (optarg);
@@ -101,18 +101,18 @@ main (int argc, char * const argv[])
                 aa_die_version ();
 
             default:
-                dieusage (1);
+                dieusage (RC_FATAL_USAGE);
         }
     }
     argc -= optind;
     argv += optind;
 
     if (argc != 0 || i < 0)
-        dieusage (1);
+        dieusage (RC_FATAL_USAGE);
 
     if (reboot (cmd[i].cmd) < 0)
-        aa_strerr_diefu2sys (2, cmd[i].desc, " the machine");
+        aa_strerr_diefu2sys (RC_FATAL_IO, cmd[i].desc, " the machine");
 
     /* unlikely :p */
-    return 0;
+    return RC_OK;
 }
diff --git a/src/utils/aa-service.c b/src/utils/aa-service.c
index 37240d9..2940732 100644
--- a/src/utils/aa-service.c
+++ b/src/utils/aa-service.c
@@ -32,6 +32,8 @@
 #include <anopa/common.h>
 #include <anopa/output.h>
 
+#define RC_ST_FAIL      (1 << 1)
+
 typedef struct exlsn_s exlsn_t;
 struct exlsn_s
 {
@@ -201,7 +203,7 @@ main (int argc, char const **argv, char const *const *envp)
                 break;
 
             case 'h':
-                dieusage (0);
+                dieusage (RC_OK);
 
             case 'l':
                 islog = 1;
@@ -215,7 +217,7 @@ main (int argc, char const **argv, char const *const *envp)
                 aa_die_version ();
 
             default:
-                dieusage (1);
+                dieusage (RC_FATAL_USAGE);
         }
     }
     argc -= optind;
@@ -226,29 +228,29 @@ main (int argc, char const **argv, char const *const *envp)
         switch (-r)
         {
             case ERR_NOT_LOG:
-                aa_strerr_dief1x (2, "option --log used while not in a subfolder 'log'");
+                aa_strerr_dief1x (RC_FATAL_USAGE, "option --log used while not in a subfolder 'log'");
 
             case ERR_DIRNAME:
-                aa_strerr_diefu1x (3, "get current dirname");
+                aa_strerr_diefu1x (RC_FATAL_IO, "get current dirname");
 
             case ERR_BAD_KEY:
-                aa_strerr_dief1x (4, "bad substitution key");
+                aa_strerr_dief1x (RC_ST_FAIL, "bad substitution key");
 
             case ERR_ADDVAR:
-                aa_strerr_diefu1sys (5, "complete addvar function");
+                aa_strerr_diefu1sys (RC_ST_FAIL, "complete addvar function");
 
             default:
-                aa_strerr_diefu2x (5, "complete addvar function", ": unknown error");
+                aa_strerr_diefu2x (RC_ST_FAIL, "complete addvar function", ": unknown error");
         }
 
     if (!env_string (&sa, argv, (unsigned int) argc))
-        aa_strerr_diefu1sys (5, "env_string");
+        aa_strerr_diefu1sys (RC_FATAL_MEMORY, "env_string");
 
     r = el_substitute (&dst, sa.s, sa.len, info.vars.s, info.values.s,
             genalloc_s (elsubst_t const, &info.data),
             genalloc_len (elsubst_t const, &info.data));
     if (r < 0)
-        aa_strerr_diefu1sys (5, "el_substitute");
+        aa_strerr_diefu1sys (RC_ST_FAIL, "el_substitute");
     else if (r == 0)
         _exit (0);
 
@@ -258,10 +260,10 @@ main (int argc, char const **argv, char const *const *envp)
         char const *v[r + 1];
 
         if (!env_make (v, r, dst.s, dst.len))
-            aa_strerr_diefu1sys (5, "env_make");
+            aa_strerr_diefu1sys (RC_ST_FAIL, "env_make");
         v[r] = 0;
         pathexec_r (v, envp, env_len (envp), info.modifs.s, info.modifs.len);
     }
 
-    aa_strerr_dieexec (6, dst.s);
+    aa_strerr_dieexec (RC_FATAL_EXEC, dst.s);
 }
diff --git a/src/utils/aa-setready.c b/src/utils/aa-setready.c
index a3ec911..be44ba8 100644
--- a/src/utils/aa-setready.c
+++ b/src/utils/aa-setready.c
@@ -30,6 +30,15 @@
 #include <anopa/common.h>
 #include <anopa/output.h>
 
+enum
+{
+    RC_ST_READ      = 1 << 1,
+    RC_ST_NOT_UP    = 2 << 1,
+    RC_ST_CLOCK     = 3 << 1,
+    RC_ST_WRITE     = 4 << 1,
+    RC_ST_EVENT     = 5 << 1
+};
+
 static void
 dieusage (int rc)
 {
@@ -73,7 +82,7 @@ main (int argc, char * const argv[])
                 break;
 
             case 'h':
-                dieusage (0);
+                dieusage (RC_OK);
 
             case 'N':
                 ready = 0;
@@ -91,14 +100,14 @@ main (int argc, char * const argv[])
                 aa_die_version ();
 
             default:
-                dieusage (1);
+                dieusage (RC_FATAL_USAGE);
         }
     }
     argc -= optind;
     argv += optind;
 
     if (argc != 1)
-        dieusage (1);
+        dieusage (RC_FATAL_USAGE);
 
     {
         size_t l = strlen (argv[0]);
@@ -110,25 +119,25 @@ main (int argc, char * const argv[])
         byte_copy (fifodir + l + 1, sizeof (S6_SUPERVISE_EVENTDIR), S6_SUPERVISE_EVENTDIR);
 
         if (!s6_svstatus_read (argv[0], &st6))
-            aa_strerr_diefu1sys (2, "read s6 status");
+            aa_strerr_diefu1sys (RC_ST_READ, "read s6 status");
         if (!(st6.pid && !st6.flagfinishing))
-            aa_strerr_dief1x (3, "service is not up");
+            aa_strerr_dief1x (RC_ST_NOT_UP, "service is not up");
 
         if (ready)
         {
             st6.flagready = 1;
             if (!tain_now (&st6.readystamp))
-                aa_strerr_diefu1sys (4, "init timestamp");
+                aa_strerr_diefu1sys (RC_ST_CLOCK, "init timestamp");
         }
         else
             st6.flagready = 0;
 
         if (!s6_svstatus_write (argv[0], &st6))
-            aa_strerr_diefu1sys (5, "write s6 status");
+            aa_strerr_diefu1sys (RC_ST_WRITE, "write s6 status");
 
         if (ftrigw_notify (fifodir, (ready) ? 'U' : 'N') < 0)
-            aa_strerr_diefu4sys (6, "send event ", (ready) ? "U": "N" , " via ", fifodir);
+            aa_strerr_diefu4sys (RC_ST_EVENT, "send event ", (ready) ? "U": "N" , " via ", fifodir);
     }
 
-    return 0;
+    return RC_OK;
 }
diff --git a/src/utils/aa-sync.c b/src/utils/aa-sync.c
index 1dcc9c2..8727ae9 100644
--- a/src/utils/aa-sync.c
+++ b/src/utils/aa-sync.c
@@ -43,10 +43,10 @@ main (int argc, char * const argv[])
     if (argc == 1)
     {
         sync ();
-        return 0;
+        return RC_OK;
     }
 
     if (argc == 2 && (str_equal (argv[1], "-V") || str_equal (argv[1], "--version")))
         aa_die_version ();
-    dieusage ((argc == 2 && (str_equal (argv[1], "-h") || str_equal (argv[1], "--help"))) ? 0 : 1);
+    dieusage ((argc == 2 && (str_equal (argv[1], "-h") || str_equal (argv[1], "--help"))) ? RC_OK : RC_FATAL_USAGE);
 }
diff --git a/src/utils/aa-terminate.c b/src/utils/aa-terminate.c
index d9e7e22..147c05c 100644
--- a/src/utils/aa-terminate.c
+++ b/src/utils/aa-terminate.c
@@ -47,6 +47,14 @@ typedef int (*do_fn) (const char *path);
 static int umnt_flags = 0;
 static int level = 1;
 
+enum
+{
+    RC_ST_SWAP      = 1 << 1,
+    RC_ST_MOUNT     = 1 << 2,
+    RC_ST_LOOP      = 1 << 3,
+    RC_ST_DM        = 1 << 4
+};
+
 static void
 verbose_do (const char *s1, const char *s2)
 {
@@ -200,7 +208,7 @@ it_loops_dms (direntry *d, void *data)
     l = sas[i]->len;
     if (!stralloc_cats (sas[i], "/dev/") || !stralloc_cats (sas[i], d->d_name)
             || !stralloc_0 (sas[i]))
-        aa_strerr_diefu1sys (2, "stralloc_catb");
+        aa_strerr_diefu1sys (RC_FATAL_MEMORY, "stralloc_catb");
 
     /* /dev/loop* always exists, let's find the actual ones */
     if (i == 0)
@@ -310,7 +318,7 @@ main (int argc, char * const argv[])
                 break;
 
             case 'h':
-                dieusage (0);
+                dieusage (RC_OK);
 
             case 'l':
                 lazy = 1;
@@ -332,14 +340,14 @@ main (int argc, char * const argv[])
                 break;
 
             default:
-                dieusage (1);
+                dieusage (RC_FATAL_USAGE);
         }
     }
     argc -= optind;
     argv += optind;
 
     if (argc > 0)
-        dieusage (1);
+        dieusage (RC_FATAL_USAGE);
 
 again:
     for (;;)
@@ -352,7 +360,7 @@ again:
 
         /* read swaps */
         if (!openslurpclose (&sa, "/proc/swaps"))
-            aa_strerr_diefu1sys (2, "read /proc/swaps");
+            aa_strerr_diefu1sys (RC_FATAL_IO, "read /proc/swaps");
 
         if (sa.len > 0)
         {
@@ -369,7 +377,7 @@ again:
                 {
                     if (!stralloc_catb (&sa_swaps, sa.s + l, e)
                             || !stralloc_0 (&sa_swaps))
-                        aa_strerr_diefu1sys (2, "stralloc_catb");
+                        aa_strerr_diefu1sys (RC_FATAL_MEMORY, "stralloc_catb");
                 }
                 l += byte_chr (sa.s + l, sa.len - l, '\n') + 1;
             }
@@ -384,7 +392,7 @@ again:
 
             mounts = setmntent ("/proc/mounts", "r");
             if (!mounts)
-                aa_strerr_diefu1sys (2, "read /proc/mounts");
+                aa_strerr_diefu1sys (RC_FATAL_IO, "read /proc/mounts");
 
             while ((mnt = getmntent (mounts)))
             {
@@ -396,7 +404,7 @@ again:
                     continue;
 
                 if (!stralloc_catb (&sa_mounts, mnt->mnt_dir, strlen (mnt->mnt_dir) + 1))
-                    aa_strerr_diefu1sys (2, "stralloc_catb");
+                    aa_strerr_diefu1sys (RC_FATAL_MEMORY, "stralloc_catb");
             }
             endmntent (mounts);
         }
@@ -410,7 +418,7 @@ again:
             stralloc_catb (&sa, "/dev", 5);
             r = aa_scan_dir (&sa, 2, it_loops_dms, &sas);
             if (r < 0)
-                aa_strerr_diefu1sys (2, "scan /dev");
+                aa_strerr_diefu1sys (RC_FATAL_IO, "scan /dev");
             sa.len = 0;
         }
 
@@ -452,5 +460,23 @@ again:
         show_left ("Remaining block device", &sa_dms);
     }
 
-    return (sa_swaps.len + sa_mounts.len + sa_loops.len + sa_dms.len == 0) ? 0 : 2;
+    {
+        int rc = RC_OK;
+
+        if (sa_swaps.len > 0)
+            rc |= RC_ST_SWAP;
+        if (sa_mounts.len > 0)
+            rc |= RC_ST_MOUNT;
+        if (sa_loops.len > 0)
+            rc |= RC_ST_LOOP;
+        if (sa_dms.len > 0)
+            rc |= RC_ST_DM;
+
+        stralloc_free (&sa_swaps);
+        stralloc_free (&sa_mounts);
+        stralloc_free (&sa_loops);
+        stralloc_free (&sa_dms);
+
+        return rc;
+    }
 }
diff --git a/src/utils/aa-test.c b/src/utils/aa-test.c
index adda3f2..9481ab0 100644
--- a/src/utils/aa-test.c
+++ b/src/utils/aa-test.c
@@ -28,6 +28,12 @@
 #include <anopa/common.h>
 #include <anopa/output.h>
 
+enum
+{
+    RC_ST_EXIST     = 1 << 1,
+    RC_ST_FAIL      = 2 << 1
+};
+
 static int
 is_group_member (gid_t gid)
 {
@@ -126,7 +132,7 @@ main (int argc, char * const argv[])
                 break;
 
             case 'h':
-                dieusage (0);
+                dieusage (RC_OK);
 
             case 'O':
                 aa_set_log_file_or_die (optarg);
@@ -134,7 +140,7 @@ main (int argc, char * const argv[])
 
             case 'R':
                 if (optarg && !uint0_scan (optarg, &repeat))
-                    aa_strerr_diefu2sys (1, "set repeat counter to ", optarg);
+                    aa_strerr_diefu2sys (RC_FATAL_USAGE, "set repeat counter to ", optarg);
                 else if (!optarg)
                     repeat = 1;
                 else
@@ -145,59 +151,59 @@ main (int argc, char * const argv[])
                 aa_die_version ();
 
             default:
-                dieusage (1);
+                dieusage (RC_FATAL_USAGE);
         }
     }
     argc -= optind;
     argv += optind;
 
     if (argc != 1 || test == 0)
-        dieusage (1);
+        dieusage (RC_FATAL_USAGE);
 
 again:
     if (lstat (argv[0], &st) < 0)
     {
         if (errno != ENOENT)
-            aa_strerr_diefu2sys (2, "stat ", argv[0]);
+            aa_strerr_diefu2sys (RC_FATAL_IO, "stat ", argv[0]);
         else if (repeat >= 1)
         {
             if (repeat > 2)
                 --repeat;
             else if (repeat == 2)
-                return 3;
+                return RC_ST_EXIST;
             sleep (1);
             goto again;
         }
         else
-            return 3;
+            return RC_ST_EXIST;
     }
 
     switch (test)
     {
         case 'b':
-            return (S_ISBLK (st.st_mode)) ? 0 : 4;
+            return (S_ISBLK (st.st_mode)) ? RC_OK : RC_ST_FAIL;
 
         case 'd':
-            return (S_ISDIR (st.st_mode)) ? 0 : 4;
+            return (S_ISDIR (st.st_mode)) ? RC_OK : RC_ST_FAIL;
 
         case 'e':
             return 0;
 
         case 'f':
-            return (S_ISREG (st.st_mode)) ? 0 : 4;
+            return (S_ISREG (st.st_mode)) ? RC_OK : RC_ST_FAIL;
 
         case 'L':
-            return (S_ISLNK (st.st_mode)) ? 0 : 4;
+            return (S_ISLNK (st.st_mode)) ? RC_OK : RC_ST_FAIL;
 
         case 'p':
-            return (S_ISFIFO (st.st_mode)) ? 0 : 4;
+            return (S_ISFIFO (st.st_mode)) ? RC_OK : RC_ST_FAIL;
 
         case 'r':
             mode = R_OK;
             break;
 
         case 'S':
-            return (S_ISSOCK (st.st_mode)) ? 0 : 4;
+            return (S_ISSOCK (st.st_mode)) ? RC_OK : RC_ST_FAIL;
 
         case 'w':
             mode = W_OK;
@@ -213,12 +219,12 @@ again:
     {
         /* root can read/write any file */
         if (mode != X_OK)
-            return 0;
+            return RC_OK;
         /* and execute anything any execute permission set */
         else if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
-            return 0;
+            return RC_OK;
         else
-            return 4;
+            return RC_ST_FAIL;
     }
 
     if (st.st_uid == euid)
@@ -226,5 +232,5 @@ again:
     else if (is_group_member (st.st_gid))
         mode <<= 3;
 
-    return (st.st_mode & mode) ? 0 : 4;
+    return (st.st_mode & mode) ? RC_OK : RC_ST_FAIL;
 }
diff --git a/src/utils/aa-tty.c b/src/utils/aa-tty.c
index 91e4ae1..58f57e8 100644
--- a/src/utils/aa-tty.c
+++ b/src/utils/aa-tty.c
@@ -72,7 +72,7 @@ main (int argc, char * const argv[])
                 break;
 
             case 'h':
-                dieusage (0);
+                dieusage (RC_OK);
 
             case 'O':
                 aa_set_log_file_or_die (optarg);
@@ -82,21 +82,21 @@ main (int argc, char * const argv[])
                 aa_die_version ();
 
             default:
-                dieusage (1);
+                dieusage (RC_FATAL_USAGE);
         }
     }
     argc -= optind;
     argv += optind;
 
     if (argc != 0)
-        dieusage (1);
+        dieusage (RC_FATAL_USAGE);
 
     byte_copy (file, sizeof (PREFIX) - 1, PREFIX);
     byte_copy (file + sizeof (PREFIX) - 1, 7, "console");
     byte_copy (file + sizeof (PREFIX) + 6, sizeof (NAME), NAME);
     r = openreadnclose (file, name, max);
     if (r <= 0)
-        aa_strerr_diefu2sys (2, "read ", file);
+        aa_strerr_diefu2sys (RC_FATAL_IO, "read ", file);
     /* last entry is the active one */
     skip = byte_rchr (name, r, ' ') + 1;
     if (skip > (size_t) r)
@@ -116,10 +116,10 @@ main (int argc, char * const argv[])
             {
                 aa_bs (AA_OUT, "/dev/");
                 aa_bb_flush (AA_OUT, s, l);
-                return 0;
+                return RC_OK;
             }
             else
-                aa_strerr_diefu2sys (2, "read ", file);
+                aa_strerr_diefu2sys (RC_FATAL_IO, "read ", file);
         }
         skip = 0;
     }
diff --git a/src/utils/aa-umount.c b/src/utils/aa-umount.c
index 521a987..91413b2 100644
--- a/src/utils/aa-umount.c
+++ b/src/utils/aa-umount.c
@@ -75,7 +75,7 @@ main (int argc, char * const argv[])
                 break;
 
             case 'h':
-                dieusage (0);
+                dieusage (RC_OK);
 
             case 'l':
                 flags = MNT_DETACH;
@@ -89,17 +89,17 @@ main (int argc, char * const argv[])
                 aa_die_version ();
 
             default:
-                dieusage (1);
+                dieusage (RC_FATAL_USAGE);
         }
     }
     argc -= optind;
     argv += optind;
 
     if (argc != 1)
-        dieusage (1);
+        dieusage (RC_FATAL_USAGE);
 
     if (umount2 (argv[0], flags) < 0)
-        aa_strerr_diefu2sys (3, "unmount ", argv[0]);
+        aa_strerr_diefu2sys (RC_FATAL_IO, "unmount ", argv[0]);
 
-    return 0;
+    return RC_OK;
 }