1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <signal.h>
4 #include <stdlib.h>
5 #include <sysexits.h>
6 
7 #include "exit-status.h"
8 #include "macro.h"
9 #include "parse-util.h"
10 #include "set.h"
11 #include "string-util.h"
12 
13 const ExitStatusMapping exit_status_mappings[256] = {
14         /* Exit status ranges:
15          *
16          *   0…1   │ ISO C, EXIT_SUCCESS + EXIT_FAILURE
17          *   2…7   │ LSB exit codes for init scripts
18          *   8…63  │ (Currently unmapped)
19          *  64…78  │ BSD defined exit codes
20          *  79…199 │ (Currently unmapped)
21          * 200…244 │ systemd's private error codes (might be extended to 254 in future development)
22          * 245…254 │ (Currently unmapped, but see above)
23          *
24          *   255   │ EXIT_EXCEPTION (We use this to propagate exit-by-signal events. It's frequently used by others apps (like bash)
25          *         │ to indicate exit reason that cannot really be expressed in a single exit status value — such as a propagated
26          *         │ signal or such, and we follow that logic here.)
27          */
28 
29         [EXIT_SUCCESS] =                 { "SUCCESS",                 EXIT_STATUS_LIBC },
30         [EXIT_FAILURE] =                 { "FAILURE",                 EXIT_STATUS_LIBC },
31 
32         [EXIT_CHDIR] =                   { "CHDIR",                   EXIT_STATUS_SYSTEMD },
33         [EXIT_NICE] =                    { "NICE",                    EXIT_STATUS_SYSTEMD },
34         [EXIT_FDS] =                     { "FDS",                     EXIT_STATUS_SYSTEMD },
35         [EXIT_EXEC] =                    { "EXEC",                    EXIT_STATUS_SYSTEMD },
36         [EXIT_MEMORY] =                  { "MEMORY",                  EXIT_STATUS_SYSTEMD },
37         [EXIT_LIMITS] =                  { "LIMITS",                  EXIT_STATUS_SYSTEMD },
38         [EXIT_OOM_ADJUST] =              { "OOM_ADJUST",              EXIT_STATUS_SYSTEMD },
39         [EXIT_SIGNAL_MASK] =             { "SIGNAL_MASK",             EXIT_STATUS_SYSTEMD },
40         [EXIT_STDIN] =                   { "STDIN",                   EXIT_STATUS_SYSTEMD },
41         [EXIT_STDOUT] =                  { "STDOUT",                  EXIT_STATUS_SYSTEMD },
42         [EXIT_CHROOT] =                  { "CHROOT",                  EXIT_STATUS_SYSTEMD },
43         [EXIT_IOPRIO] =                  { "IOPRIO",                  EXIT_STATUS_SYSTEMD },
44         [EXIT_TIMERSLACK] =              { "TIMERSLACK",              EXIT_STATUS_SYSTEMD },
45         [EXIT_SECUREBITS] =              { "SECUREBITS",              EXIT_STATUS_SYSTEMD },
46         [EXIT_SETSCHEDULER] =            { "SETSCHEDULER",            EXIT_STATUS_SYSTEMD },
47         [EXIT_CPUAFFINITY] =             { "CPUAFFINITY",             EXIT_STATUS_SYSTEMD },
48         [EXIT_GROUP] =                   { "GROUP",                   EXIT_STATUS_SYSTEMD },
49         [EXIT_USER] =                    { "USER",                    EXIT_STATUS_SYSTEMD },
50         [EXIT_CAPABILITIES] =            { "CAPABILITIES",            EXIT_STATUS_SYSTEMD },
51         [EXIT_CGROUP] =                  { "CGROUP",                  EXIT_STATUS_SYSTEMD },
52         [EXIT_SETSID] =                  { "SETSID",                  EXIT_STATUS_SYSTEMD },
53         [EXIT_CONFIRM] =                 { "CONFIRM",                 EXIT_STATUS_SYSTEMD },
54         [EXIT_STDERR] =                  { "STDERR",                  EXIT_STATUS_SYSTEMD },
55         [EXIT_PAM] =                     { "PAM",                     EXIT_STATUS_SYSTEMD },
56         [EXIT_NETWORK] =                 { "NETWORK",                 EXIT_STATUS_SYSTEMD },
57         [EXIT_NAMESPACE] =               { "NAMESPACE",               EXIT_STATUS_SYSTEMD },
58         [EXIT_NO_NEW_PRIVILEGES] =       { "NO_NEW_PRIVILEGES",       EXIT_STATUS_SYSTEMD },
59         [EXIT_SECCOMP] =                 { "SECCOMP",                 EXIT_STATUS_SYSTEMD },
60         [EXIT_SELINUX_CONTEXT] =         { "SELINUX_CONTEXT",         EXIT_STATUS_SYSTEMD },
61         [EXIT_PERSONALITY] =             { "PERSONALITY",             EXIT_STATUS_SYSTEMD },
62         [EXIT_APPARMOR_PROFILE] =        { "APPARMOR",                EXIT_STATUS_SYSTEMD },
63         [EXIT_ADDRESS_FAMILIES] =        { "ADDRESS_FAMILIES",        EXIT_STATUS_SYSTEMD },
64         [EXIT_RUNTIME_DIRECTORY] =       { "RUNTIME_DIRECTORY",       EXIT_STATUS_SYSTEMD },
65         [EXIT_CHOWN] =                   { "CHOWN",                   EXIT_STATUS_SYSTEMD },
66         [EXIT_SMACK_PROCESS_LABEL] =     { "SMACK_PROCESS_LABEL",     EXIT_STATUS_SYSTEMD },
67         [EXIT_KEYRING] =                 { "KEYRING",                 EXIT_STATUS_SYSTEMD },
68         [EXIT_STATE_DIRECTORY] =         { "STATE_DIRECTORY",         EXIT_STATUS_SYSTEMD },
69         [EXIT_CACHE_DIRECTORY] =         { "CACHE_DIRECTORY",         EXIT_STATUS_SYSTEMD },
70         [EXIT_LOGS_DIRECTORY] =          { "LOGS_DIRECTORY",          EXIT_STATUS_SYSTEMD },
71         [EXIT_CONFIGURATION_DIRECTORY] = { "CONFIGURATION_DIRECTORY", EXIT_STATUS_SYSTEMD },
72         [EXIT_NUMA_POLICY] =             { "NUMA_POLICY",             EXIT_STATUS_SYSTEMD },
73         [EXIT_CREDENTIALS] =             { "CREDENTIALS",             EXIT_STATUS_SYSTEMD },
74         [EXIT_BPF] =                     { "BPF",                     EXIT_STATUS_SYSTEMD },
75 
76         [EXIT_EXCEPTION] =               { "EXCEPTION",               EXIT_STATUS_SYSTEMD },
77 
78         [EXIT_INVALIDARGUMENT] =         { "INVALIDARGUMENT",         EXIT_STATUS_LSB },
79         [EXIT_NOTIMPLEMENTED] =          { "NOTIMPLEMENTED",          EXIT_STATUS_LSB },
80         [EXIT_NOPERMISSION] =            { "NOPERMISSION",            EXIT_STATUS_LSB },
81         [EXIT_NOTINSTALLED] =            { "NOTINSTALLED",            EXIT_STATUS_LSB },
82         [EXIT_NOTCONFIGURED] =           { "NOTCONFIGURED",           EXIT_STATUS_LSB },
83         [EXIT_NOTRUNNING] =              { "NOTRUNNING",              EXIT_STATUS_LSB },
84 
85         [EX_USAGE] =                     { "USAGE",                   EXIT_STATUS_BSD },
86         [EX_DATAERR] =                   { "DATAERR",                 EXIT_STATUS_BSD },
87         [EX_NOINPUT] =                   { "NOINPUT",                 EXIT_STATUS_BSD },
88         [EX_NOUSER] =                    { "NOUSER",                  EXIT_STATUS_BSD },
89         [EX_NOHOST] =                    { "NOHOST",                  EXIT_STATUS_BSD },
90         [EX_UNAVAILABLE] =               { "UNAVAILABLE",             EXIT_STATUS_BSD },
91         [EX_SOFTWARE] =                  { "SOFTWARE",                EXIT_STATUS_BSD },
92         [EX_OSERR] =                     { "OSERR",                   EXIT_STATUS_BSD },
93         [EX_OSFILE] =                    { "OSFILE",                  EXIT_STATUS_BSD },
94         [EX_CANTCREAT] =                 { "CANTCREAT",               EXIT_STATUS_BSD },
95         [EX_IOERR] =                     { "IOERR",                   EXIT_STATUS_BSD },
96         [EX_TEMPFAIL] =                  { "TEMPFAIL",                EXIT_STATUS_BSD },
97         [EX_PROTOCOL] =                  { "PROTOCOL",                EXIT_STATUS_BSD },
98         [EX_NOPERM] =                    { "NOPERM",                  EXIT_STATUS_BSD },
99         [EX_CONFIG] =                    { "CONFIG",                  EXIT_STATUS_BSD },
100 };
101 
exit_status_to_string(int code,ExitStatusClass class)102 const char* exit_status_to_string(int code, ExitStatusClass class) {
103         if (code < 0 || (size_t) code >= ELEMENTSOF(exit_status_mappings))
104                 return NULL;
105         return class & exit_status_mappings[code].class ? exit_status_mappings[code].name : NULL;
106 }
107 
exit_status_class(int code)108 const char* exit_status_class(int code) {
109         if (code < 0 || (size_t) code >= ELEMENTSOF(exit_status_mappings))
110                 return NULL;
111 
112         switch (exit_status_mappings[code].class) {
113         case EXIT_STATUS_LIBC:
114                 return "libc";
115         case EXIT_STATUS_SYSTEMD:
116                 return "systemd";
117         case EXIT_STATUS_LSB:
118                 return "LSB";
119         case EXIT_STATUS_BSD:
120                 return "BSD";
121         default: return NULL;
122         }
123 }
124 
exit_status_from_string(const char * s)125 int exit_status_from_string(const char *s) {
126         uint8_t val;
127         int r;
128 
129         for (size_t i = 0; i < ELEMENTSOF(exit_status_mappings); i++)
130                 if (streq_ptr(s, exit_status_mappings[i].name))
131                         return i;
132 
133         r = safe_atou8(s, &val);
134         if (r < 0)
135                 return r;
136 
137         return val;
138 }
139 
is_clean_exit(int code,int status,ExitClean clean,const ExitStatusSet * success_status)140 bool is_clean_exit(int code, int status, ExitClean clean, const ExitStatusSet *success_status) {
141         if (code == CLD_EXITED)
142                 return status == 0 ||
143                        (success_status &&
144                         bitmap_isset(&success_status->status, status));
145 
146         /* If a daemon does not implement handlers for some of the signals, we do not consider this an
147            unclean shutdown */
148         if (code == CLD_KILLED)
149                 return
150                         (clean == EXIT_CLEAN_DAEMON && IN_SET(status, SIGHUP, SIGINT, SIGTERM, SIGPIPE)) ||
151                         (success_status &&
152                          bitmap_isset(&success_status->signal, status));
153 
154         return false;
155 }
156 
exit_status_set_free(ExitStatusSet * x)157 void exit_status_set_free(ExitStatusSet *x) {
158         assert(x);
159 
160         bitmap_clear(&x->status);
161         bitmap_clear(&x->signal);
162 }
163 
exit_status_set_is_empty(const ExitStatusSet * x)164 bool exit_status_set_is_empty(const ExitStatusSet *x) {
165         if (!x)
166                 return true;
167 
168         return bitmap_isclear(&x->status) && bitmap_isclear(&x->signal);
169 }
170 
exit_status_set_test(const ExitStatusSet * x,int code,int status)171 bool exit_status_set_test(const ExitStatusSet *x, int code, int status) {
172         if (code == CLD_EXITED && bitmap_isset(&x->status, status))
173                 return true;
174 
175         if (IN_SET(code, CLD_KILLED, CLD_DUMPED) && bitmap_isset(&x->signal, status))
176                 return true;
177 
178         return false;
179 }
180