core/exec-invoke: record correct exit status when failed to locate executable

Follow-up for 4d8b0f0f7a

After the mentioned commit, when the ExecCommand executable is missing,
and failure will be ignored by manager, we exit with EXIT_SUCCESS at executor
side too. The behavior however contradicts systemd.service(5), which states:

> If the executable path is prefixed with "-", an exit code of the command
> normally considered a failure (i.e. non-zero exit status or abnormal exit
> due to signal is _recorded_, but has no further effect and is considered
> equivalent to success.

and thus makes debugging unexpected failures harder. Therefore, let's still
exit with EXIT_EXEC, but just skip LOG_ERR level log.
This commit is contained in:
Mike Yuan
2024-02-19 23:12:59 +08:00
parent 12af0efba5
commit 80b18d217a
3 changed files with 35 additions and 20 deletions
+10 -19
View File
@@ -4792,26 +4792,17 @@ int exec_invoke(
_cleanup_close_ int executable_fd = -EBADF;
r = find_executable_full(command->path, /* root= */ NULL, context->exec_search_path, false, &executable, &executable_fd);
if (r < 0) {
if (r != -ENOMEM && (command->flags & EXEC_COMMAND_IGNORE_FAILURE)) {
log_exec_struct_errno(context, params, LOG_INFO, r,
"MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR,
LOG_EXEC_INVOCATION_ID(params),
LOG_EXEC_MESSAGE(params,
"Executable %s missing, skipping: %m",
command->path),
"EXECUTABLE=%s", command->path);
*exit_status = EXIT_SUCCESS;
return 0;
}
*exit_status = EXIT_EXEC;
return log_exec_struct_errno(context, params, LOG_INFO, r,
"MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR,
LOG_EXEC_INVOCATION_ID(params),
LOG_EXEC_MESSAGE(params,
"Failed to locate executable %s: %m",
command->path),
"EXECUTABLE=%s", command->path);
log_exec_struct_errno(context, params, LOG_INFO, r,
"MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR,
LOG_EXEC_INVOCATION_ID(params),
LOG_EXEC_MESSAGE(params,
"Unable to locate executable '%s': %m",
command->path),
"EXECUTABLE=%s", command->path);
/* If the error will be ignored by manager, tune down the log level here. Missing executable
* is very much expected in this case. */
return r != -ENOMEM && FLAGS_SET(command->flags, EXEC_COMMAND_IGNORE_FAILURE) ? 1 : r;
}
r = add_shifted_fd(keep_fds, ELEMENTSOF(keep_fds), &n_keep_fds, &executable_fd);
+3 -1
View File
@@ -250,7 +250,9 @@ static int run(int argc, char *argv[]) {
status, command.path),
"EXECUTABLE=%s", command.path);
} else
assert(exit_status == EXIT_SUCCESS); /* When 'skip' is chosen in the confirm spawn prompt */
/* r == 0: 'skip' is chosen in the confirm spawn prompt
* r > 0: expected/ignored failure, do not log at error level */
assert((r == 0) == (exit_status == EXIT_SUCCESS));
return exit_status;
}
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
cat >/run/systemd/system/nonexistent-execstart-exit-status.service <<EOF
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=-/foo/bar/not-exist
EOF
systemctl start nonexistent-execstart-exit-status.service
systemctl is-active nonexistent-execstart-exit-status.service
assert_eq "$(systemctl show nonexistent-execstart-exit-status.service -P Result)" "success"
(( $(systemctl show nonexistent-execstart-exit-status.service -P ExecMainStatus) > 0 ))
systemctl stop nonexistent-execstart-exit-status.service
rm /run/systemd/system/nonexistent-execstart-exit-status.service