diff options
Diffstat (limited to 'src/timeout.c')
-rw-r--r-- | src/timeout.c | 163 |
1 files changed, 134 insertions, 29 deletions
diff --git a/src/timeout.c b/src/timeout.c index 1fe0354..ca4a7cd 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -2,9 +2,9 @@ * timout.c Advanced timeout handling for file system calls * to avoid deadlocks on remote file shares. * - * Version: 0.1 07-Sep-2011 Fink + * Version: 0.2 11-Dec-2012 Fink * - * Copyright 2011 Werner Fink, 2011 SUSE LINUX Products GmbH, Germany. + * Copyright 2011,2012 Werner Fink, 2011,2012 SUSE LINUX Products GmbH, Germany. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,26 +18,29 @@ # define _GNU_SOURCE #endif -#ifndef USE_SOCKETPAIR -# define USE_SOCKETPAIR 1 -#endif - #ifdef _FEATURES_H # error Include local config.h before any system header file #endif -#include "config.h" /* For _FILE_OFFSET_BITS */ +#include "config.h" + +#ifndef WITH_TIMEOUT_STAT +# define WITH_TIMEOUT_STAT 0 +#endif + +#ifndef USE_SOCKETPAIR +# define USE_SOCKETPAIR 1 +#endif #include <errno.h> -#include <pthread.h> #include <setjmp.h> #include <signal.h> #include <stdlib.h> #include <string.h> +#include <sys/mman.h> #include <sys/time.h> #include <sys/types.h> #include <sys/select.h> #include <sys/stat.h> - #include <unistd.h> #if USE_SOCKETPAIR # include <sys/socket.h> @@ -86,6 +89,15 @@ # define strcpy(d,s) __builtin_strcpy((d),(s)) /* Without boundary check please */ #endif +#if WITH_TIMEOUT_STAT +static sigjmp_buf jenv; +static void sigjump(int sig attribute((unused))) +{ + siglongjmp(jenv, 1); +} +#endif + +#if WITH_TIMEOUT_STAT == 2 /* * The structure used for communication between the processes */ @@ -106,7 +118,8 @@ typedef struct _handle { static volatile pid_t active; static int pipes[4] = {-1, -1, -1, -1}; -static char buf[PATH_MAX + sizeof(handle_t) + 1]; +static handle_t *restrict handle; +static const size_t buflen = PATH_MAX+sizeof(handle_t)+1; static void sigchild(int sig attribute((unused))) { @@ -122,6 +135,7 @@ static void attribute((constructor)) start(void) { sigset_t sigset, oldset; struct sigaction act; + char sync[1]; ssize_t in; if (pipes[1] >= 0) close(pipes[1]); @@ -138,6 +152,12 @@ static void attribute((constructor)) start(void) act.sa_handler = sigchild; sigaction(SIGCHLD, &act, 0); + if (!handle) + handle = mmap(NULL, buflen, PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_SHARED, -1, 0); + if (handle == MAP_FAILED) + goto error; + if ((active = fork()) < 0) goto error; @@ -164,16 +184,22 @@ static void attribute((constructor)) start(void) pipes[1] = pipes[2] = -1; pipes[0] = pipes[3] = -1; - { - handle_t *restrict handle = (void*)&buf[0]; - - while ((in = read(STDIN_FILENO, handle, sizeof(buf))) > sizeof(handle_t)) { - if (handle->function(handle->path, &handle->argument) < 0) - handle->errcode = errno; - write(STDOUT_FILENO, &handle->errcode, sizeof(handle->errcode)+sizeof(handle->argument)); - memset(handle, 0, sizeof(handle_t)); + while ((in = read(STDIN_FILENO, &sync, sizeof(sync))) != 0) { + ssize_t out; + if (in < 0) { + if (errno == EINTR) + continue; + break; } + if (!handle) + break; + if (handle->function(handle->path, &handle->argument) < 0) + handle->errcode = errno; + do + out = write(STDOUT_FILENO, &sync, sizeof(sync)); + while (out < 0 && errno == EINTR); } + sigprocmask(SIG_SETMASK, &oldset, NULL); exit(0); error: @@ -181,6 +207,9 @@ error: if (pipes[1] >= 0) close(pipes[1]); if (pipes[2] >= 0) close(pipes[2]); if (pipes[3] >= 0) close(pipes[3]); + if (handle && handle != MAP_FAILED) + munmap(handle, buflen); + handle = NULL; } static void /* attribute((destructor)) */ stop(void) @@ -189,24 +218,24 @@ static void /* attribute((destructor)) */ stop(void) kill(active, SIGKILL); } -static sigjmp_buf jenv; -static void sigjump(int sig attribute((unused))) -{ - siglongjmp(jenv, 1); -} - /* * External routine + * + * Execute stat(2) system call with timeout to avoid deadlock + * on network based file systems. + * */ -int timeout(stat_t function, const char *path, struct stat *restrict argument, time_t seconds) +int +timeout(stat_t function, const char *path, struct stat *restrict argument, time_t seconds) { - handle_t *restrict handle = (void*)&buf[0]; struct sigaction alrm_act, pipe_act, new_act; sigset_t sigset, oldset; + char sync[1] = "x"; if (active <= 0) /* Oops, last one failed therefore clear status and restart */ start(); - + if (!handle) /* No shared memory area */ + return function(path, argument); memset(handle, 0, sizeof(handle_t)); handle->len = strlen(path) + 1; if (handle->len >= PATH_MAX) { @@ -235,8 +264,8 @@ int timeout(stat_t function, const char *path, struct stat *restrict argument, t sigaction(SIGPIPE, &new_act, &pipe_act); alarm(seconds); - write(pipes[1], handle, sizeof(handle_t)+handle->len); - read(pipes[2], &handle->errcode, sizeof(handle->errcode)+sizeof(handle->argument)); + write(pipes[1], &sync, sizeof(sync)); + read(pipes[2], &sync, sizeof(sync)); alarm(0); sigaction(SIGPIPE, &pipe_act, NULL); @@ -261,6 +290,82 @@ timed: error: return -1; } +#elif WITH_TIMEOUT_STAT == 1 +/* + * External routine + * + * Execute stat(2) system call with timeout to avoid deadlock + * on network based file systems. + * + */ +int +timeout(stat_t function, const char *path, struct stat *restrict argument, time_t seconds) +{ + struct sigaction alrm_act, pipe_act, new_act; + sigset_t sigset, oldset; + int ret = 0, pipes[4]; + pid_t pid = 0; + ssize_t len; + + if (pipe(&pipes[0]) < 0) + goto error; + switch ((pid = fork())) { + case -1: + close(pipes[0]); + close(pipes[1]); + goto error; + case 0: + new_act.sa_handler = SIG_DFL; + sigaction(SIGALRM, &new_act, NULL); + close(pipes[0]); + if ((ret = function(path, argument)) == 0) + do + len = write(pipes[1], argument, sizeof(struct stat)); + while (len < 0 && errno == EINTR); + close(pipes[1]); + exit(ret); + default: + close(pipes[1]); + + sigemptyset(&sigset); + sigaddset(&sigset, SIGALRM); + sigaddset(&sigset, SIGPIPE); + sigprocmask(SIG_UNBLOCK, &sigset, &oldset); + + memset(&new_act, 0, sizeof(new_act)); + sigemptyset(&new_act.sa_mask); + + if (sigsetjmp(jenv, 1)) + goto timed; + + new_act.sa_handler = sigjump; + sigaction(SIGALRM, &new_act, &alrm_act); + sigaction(SIGPIPE, &new_act, &pipe_act); + alarm(seconds); + if (read(pipes[0], argument, sizeof(struct stat)) == 0) { + errno = EFAULT; + ret = -1; + } + (void)alarm(0); + sigaction(SIGPIPE, &pipe_act, NULL); + sigaction(SIGALRM, &alrm_act, NULL); + + close(pipes[0]); + waitpid(pid, NULL, 0); + break; + } + return ret; +timed: + (void)alarm(0); + sigaction(SIGPIPE, &pipe_act, NULL); + sigaction(SIGALRM, &alrm_act, NULL); + if (waitpid(0, NULL, WNOHANG) == 0) + kill(pid, SIGKILL); + errno = ETIMEDOUT; +error: + return -1; +} +#endif /* * End of timeout.c |