Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
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…242 │ systemd's private error codes (might be extended to 254 in future development)
22 : : * 243…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_EXCEPTION] = { "EXCEPTION", EXIT_STATUS_SYSTEMD },
74 : :
75 : : [EXIT_INVALIDARGUMENT] = { "INVALIDARGUMENT", EXIT_STATUS_LSB },
76 : : [EXIT_NOTIMPLEMENTED] = { "NOTIMPLEMENTED", EXIT_STATUS_LSB },
77 : : [EXIT_NOPERMISSION] = { "NOPERMISSION", EXIT_STATUS_LSB },
78 : : [EXIT_NOTINSTALLED] = { "NOTINSTALLED", EXIT_STATUS_LSB },
79 : : [EXIT_NOTCONFIGURED] = { "NOTCONFIGURED", EXIT_STATUS_LSB },
80 : : [EXIT_NOTRUNNING] = { "NOTRUNNING", EXIT_STATUS_LSB },
81 : :
82 : : [EX_USAGE] = { "USAGE", EXIT_STATUS_BSD },
83 : : [EX_DATAERR] = { "DATAERR", EXIT_STATUS_BSD },
84 : : [EX_NOINPUT] = { "NOINPUT", EXIT_STATUS_BSD },
85 : : [EX_NOUSER] = { "NOUSER", EXIT_STATUS_BSD },
86 : : [EX_NOHOST] = { "NOHOST", EXIT_STATUS_BSD },
87 : : [EX_UNAVAILABLE] = { "UNAVAILABLE", EXIT_STATUS_BSD },
88 : : [EX_SOFTWARE] = { "SOFTWARE", EXIT_STATUS_BSD },
89 : : [EX_OSERR] = { "OSERR", EXIT_STATUS_BSD },
90 : : [EX_OSFILE] = { "OSFILE", EXIT_STATUS_BSD },
91 : : [EX_CANTCREAT] = { "CANTCREAT", EXIT_STATUS_BSD },
92 : : [EX_IOERR] = { "IOERR", EXIT_STATUS_BSD },
93 : : [EX_TEMPFAIL] = { "TEMPFAIL", EXIT_STATUS_BSD },
94 : : [EX_PROTOCOL] = { "PROTOCOL", EXIT_STATUS_BSD },
95 : : [EX_NOPERM] = { "NOPERM", EXIT_STATUS_BSD },
96 : : [EX_CONFIG] = { "CONFIG", EXIT_STATUS_BSD },
97 : : };
98 : :
99 : 1048 : const char* exit_status_to_string(int code, ExitStatusClass class) {
100 [ + + + + ]: 1048 : if (code < 0 || (size_t) code >= ELEMENTSOF(exit_status_mappings))
101 : 8 : return NULL;
102 [ + + ]: 1040 : return class & exit_status_mappings[code].class ? exit_status_mappings[code].name : NULL;
103 : : }
104 : :
105 : 1032 : const char* exit_status_class(int code) {
106 [ + + + + ]: 1032 : if (code < 0 || (size_t) code >= ELEMENTSOF(exit_status_mappings))
107 : 8 : return NULL;
108 : :
109 [ + + + + : 1024 : switch (exit_status_mappings[code].class) {
+ ]
110 : 8 : case EXIT_STATUS_LIBC:
111 : 8 : return "libc";
112 : 168 : case EXIT_STATUS_SYSTEMD:
113 : 168 : return "systemd";
114 : 24 : case EXIT_STATUS_LSB:
115 : 24 : return "LSB";
116 : 60 : case EXIT_STATUS_BSD:
117 : 60 : return "BSD";
118 : 764 : default: return NULL;
119 : : }
120 : : }
121 : :
122 : 284 : int exit_status_from_string(const char *s) {
123 : : uint8_t val;
124 : : int r;
125 : :
126 [ + + ]: 45960 : for (size_t i = 0; i < ELEMENTSOF(exit_status_mappings); i++)
127 [ + + ]: 45944 : if (streq_ptr(s, exit_status_mappings[i].name))
128 : 268 : return i;
129 : :
130 : 16 : r = safe_atou8(s, &val);
131 [ + + ]: 16 : if (r < 0)
132 : 12 : return r;
133 : :
134 : 4 : return val;
135 : : }
136 : :
137 : 0 : bool is_clean_exit(int code, int status, ExitClean clean, const ExitStatusSet *success_status) {
138 [ # # ]: 0 : if (code == CLD_EXITED)
139 [ # # # # ]: 0 : return status == 0 ||
140 [ # # ]: 0 : (success_status &&
141 : 0 : bitmap_isset(&success_status->status, status));
142 : :
143 : : /* If a daemon does not implement handlers for some of the signals, we do not consider this an
144 : : unclean shutdown */
145 [ # # ]: 0 : if (code == CLD_KILLED)
146 : : return
147 [ # # # # : 0 : (clean == EXIT_CLEAN_DAEMON && IN_SET(status, SIGHUP, SIGINT, SIGTERM, SIGPIPE)) ||
# # # # ]
148 [ # # ]: 0 : (success_status &&
149 : 0 : bitmap_isset(&success_status->signal, status));
150 : :
151 : 0 : return false;
152 : : }
153 : :
154 : 552 : void exit_status_set_free(ExitStatusSet *x) {
155 [ - + ]: 552 : assert(x);
156 : :
157 : 552 : bitmap_clear(&x->status);
158 : 552 : bitmap_clear(&x->signal);
159 : 552 : }
160 : :
161 : 68 : bool exit_status_set_is_empty(const ExitStatusSet *x) {
162 [ - + ]: 68 : if (!x)
163 : 0 : return true;
164 : :
165 [ + - + - ]: 68 : return bitmap_isclear(&x->status) && bitmap_isclear(&x->signal);
166 : : }
167 : :
168 : 0 : bool exit_status_set_test(const ExitStatusSet *x, int code, int status) {
169 [ # # # # ]: 0 : if (code == CLD_EXITED && bitmap_isset(&x->status, status))
170 : 0 : return true;
171 : :
172 [ # # # # : 0 : if (IN_SET(code, CLD_KILLED, CLD_DUMPED) && bitmap_isset(&x->signal, status))
# # ]
173 : 0 : return true;
174 : :
175 : 0 : return false;
176 : : }
|