summaryrefslogtreecommitdiff
path: root/src/basic/process-util.c
diff options
context:
space:
mode:
authorKyle Walker <kwalker@redhat.com>2017-12-13 12:49:26 -0500
committerSven Eden <yamakuzure@gmx.net>2018-05-30 07:49:36 +0200
commitd4a60be201f9f21577ab6a43f55ff38fbee1c46f (patch)
tree299cf71698017cea66655d06796bec92fedd2e3e /src/basic/process-util.c
parent2205140c2dca028beb4870267b98bd6c76d07385 (diff)
core: Implement timeout based umount/remount limit
Remount, and subsequent umount, attempts can hang for inaccessible network based mount points. This can leave a system in a hard hang state that requires a hard reset in order to recover. This change moves the remount, and umount attempts into separate child processes. The remount and umount operations will block for up to 90 seconds (DEFAULT_TIMEOUT_USEC). Should those waits fail, the parent will issue a SIGKILL to the child and continue with the shutdown efforts. In addition, instead of only reporting some additional errors on the final attempt, failures are reported as they occur.
Diffstat (limited to 'src/basic/process-util.c')
-rw-r--r--src/basic/process-util.c72
1 files changed, 61 insertions, 11 deletions
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index 611fd2339..2494ba6af 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -26,7 +26,6 @@
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
-#include <stdio_ext.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
@@ -131,8 +130,6 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
return -errno;
}
- (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
-
if (max_length == 1) {
/* If there's only room for one byte, return the empty string */
@@ -411,8 +408,6 @@ int is_kernel_thread(pid_t pid) {
return -errno;
}
- (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
-
count = fread(&c, 1, 1, f);
eof = feof(f);
fclose(f);
@@ -497,8 +492,6 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
return -errno;
}
- (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
-
FOREACH_LINE(line, f, return -errno) {
char *l;
@@ -577,8 +570,6 @@ int get_process_environ(pid_t pid, char **env) {
return -errno;
}
- (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
-
while ((c = fgetc(f)) != EOF) {
if (!GREEDY_REALLOC(outcome, allocated, sz + 5))
return -ENOMEM;
@@ -714,6 +705,67 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_cod
return -EPROTO;
}
+/*
+ * Return values:
+ * < 0 : wait_for_terminate_with_timeout() failed to get the state of the
+ * process, the process timed out, the process was terminated by a
+ * signal, or failed for an unknown reason.
+ * >=0 : The process terminated normally with no failures.
+ *
+ * Success is indicated by a return value of zero, a timeout is indicated
+ * by ETIMEDOUT, and all other child failure states are indicated by error
+ * is indicated by a non-zero value.
+ */
+int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) {
+ sigset_t mask;
+ int r;
+ usec_t until;
+
+ assert_se(sigemptyset(&mask) == 0);
+ assert_se(sigaddset(&mask, SIGCHLD) == 0);
+
+ /* Drop into a sigtimewait-based timeout. Waiting for the
+ * pid to exit. */
+ until = now(CLOCK_MONOTONIC) + timeout;
+ for (;;) {
+ usec_t n;
+ siginfo_t status = {};
+ struct timespec ts;
+
+ n = now(CLOCK_MONOTONIC);
+ if (n >= until)
+ break;
+
+ r = sigtimedwait(&mask, NULL, timespec_store(&ts, until - n)) < 0 ? -errno : 0;
+ /* Assuming we woke due to the child exiting. */
+ if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) {
+ if (status.si_pid == pid) {
+ /* This is the correct child.*/
+ if (status.si_code == CLD_EXITED)
+ return (status.si_status == 0) ? 0 : -EPROTO;
+ else
+ return -EPROTO;
+ }
+ }
+ /* Not the child, check for errors and proceed appropriately */
+ if (r < 0) {
+ switch (r) {
+ case -EAGAIN:
+ /* Timed out, child is likely hung. */
+ return -ETIMEDOUT;
+ case -EINTR:
+ /* Received a different signal and should retry */
+ continue;
+ default:
+ /* Return any unexpected errors */
+ return r;
+ }
+ }
+ }
+
+ return -EPROTO;
+}
+
#if 0 /// UNNEEDED by elogind
void sigkill_wait(pid_t pid) {
assert(pid > 1);
@@ -766,8 +818,6 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) {
return -errno;
}
- (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
-
l = strlen(field);
r = 0;