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