diff options
-rw-r--r-- | rcynic/rcynic.c | 119 |
1 files changed, 87 insertions, 32 deletions
diff --git a/rcynic/rcynic.c b/rcynic/rcynic.c index 1a44a184..f99ac16b 100644 --- a/rcynic/rcynic.c +++ b/rcynic/rcynic.c @@ -37,6 +37,8 @@ #include <sys/stat.h> #include <dirent.h> #include <limits.h> +#include <fcntl.h> +#include <signal.h> #include <openssl/bio.h> #include <openssl/pem.h> @@ -54,6 +56,8 @@ #define URI_MAX (FILENAME_MAX + SIZEOF_RSYNC) +#define KILL_MAX 10 + /* * Structure to hold data parsed out of a certificate. */ @@ -384,24 +388,23 @@ static int rm_rf(const char *name) /* - * Run rsync. + * Run rsync. This is fairly nasty, because we need to: + * + * (a) Construct the argument list for rsync; + * + * (b) Run rsync in a child process; + * + * (c) Sit listening to rsync's output, logging whatever we get; * - * This probably isn't paranoid enough. Should use select() to do - * some kind of timeout when rsync is taking too long. Breaking the - * log stream into lines without fgets() is a pain, maybe setting - * nonblocking I/O before calling fdopen() would suffice to let us use - * select()? If we time out, we need to kill() the rsync process. + * (d) Impose an optional time limit on rsync's execution time * - * Ok, this is a PITA. I don't see any portable way to use fgets() - * with non-blocking I/O, so we have to revert to raw read()/write() - * calls (after setting fcntl(fd, O_NONBLOCK)), look for the newline - * ourselves, and perhaps even copy remainig text from the buffer down - * to the bottom (or do some kind of ringbuffer thing, hmmm...no that - * doesn't work well with null terminated strings, oh well). + * (e) Clean up from (b), (c), and (d); and * - * Type signature of execvp() is weird, hence the cast. Well, ok - * ANSI/ISO const is weird to begin with, but even once one gets past - * the bizzare syntax, execvp()'s type signature is still weird. + * (f) Keep track of which URIs we've already fetched, so we don't + * have to do it again. + * + * Taken all together, this is pretty icky. Breaking it into separate + * functions wouldn't help much. Don't read this on a full stomach. */ static int rsync_cmp(const char * const *a, const char * const *b) @@ -409,29 +412,29 @@ static int rsync_cmp(const char * const *a, const char * const *b) return strcmp(*a, *b); } +#define whine(msg) write(2, msg, sizeof(msg) - 1) + static int rsync(const rcynic_ctx_t *rc, const char * const *args, const char *uri) { static char *rsync_cmd[] = { - "rsync", "--update", "--times", "--copy-links", "--itemize-changes" + "rsync", "--update", "--times", "--copy-links", "--itemize-changes", NULL }; + const char *argv[100]; char *s, buffer[URI_MAX * 4], path[FILENAME_MAX]; - int i, ret, pipe_fds[2], argc = 0, pid_status = -1; -#if 0 + int i, n, ret, pipe_fds[2], argc = 0, pid_status = -1; + time_t now, deadline; struct timeval tv; + pid_t pid, wpid; fd_set rfds; - int n; -#endif - pid_t pid; - FILE *f; assert(rc && uri); memset(argv, 0, sizeof(argv)); - for (i = 0; i < sizeof(rsync_cmd)/sizeof(*rsync_cmd); i++) { + for (i = 0; rsync_cmd[i]; i++) { assert(argc < sizeof(argv)/sizeof(*argv)); argv[argc++] = rsync_cmd[i]; } @@ -488,8 +491,9 @@ static int rsync(const rcynic_ctx_t *rc, return 0; } - if ((f = fdopen(pipe_fds[0], "r")) == NULL) { - logmsg(rc, "Couldn't fdopen() rsync's output stream"); + if ((i = fcntl(pipe_fds[0], F_GETFL, 0)) == -1 || + fcntl(pipe_fds[0], F_SETFL, i | O_NONBLOCK) == -1) { + logmsg(rc, "Couldn't set rsync's output stream non-blocking"); close(pipe_fds[0]); close(pipe_fds[1]); return 0; @@ -498,11 +502,10 @@ static int rsync(const rcynic_ctx_t *rc, switch ((pid = vfork())) { case -1: logmsg(rc, "vfork() failed"); - fclose(f); + close(pipe_fds[0]); close(pipe_fds[1]); return 0; case 0: -#define whine(msg) write(2, msg, sizeof(msg) - 1) close(pipe_fds[0]); if (dup2(pipe_fds[1], 1) < 0) whine("dup2(1) failed\n"); @@ -514,18 +517,68 @@ static int rsync(const rcynic_ctx_t *rc, write(2, strerror(errno), strlen(strerror(errno))); whine("\n"); _exit(1); -#undef whine } close(pipe_fds[1]); - while (fgets(buffer, sizeof(buffer), f)) { - if ((s = strchr(buffer, '\n')) != NULL) - *s = '\0'; + deadline = time(0) + rc->rsync_timeout; + + i = 0; + while ((wpid = waitpid(pid, &pid_status, WNOHANG)) == 0 && + (!rc->rsync_timeout || (now = time(0)) < deadline)) { + FD_ZERO(&rfds); + FD_SET(pipe_fds[0], &rfds); + if (rc->rsync_timeout) { + tv.tv_sec = deadline - now; + tv.tv_usec = 0; + n = select(pipe_fds[0] + 1, &rfds, NULL, NULL, &tv); + } else { + n = select(pipe_fds[0] + 1, &rfds, NULL, NULL, NULL); + } + if (n == 0 || (n < 0 && errno == EINTR)) + continue; + if (n < 0) + break; + while ((n = read(pipe_fds[0], buffer + i, sizeof(buffer) - i - 1)) > 0) { + i += n; + assert(i < sizeof(buffer)); + buffer[i] = '\0'; + while ((s = strchr(buffer, '\n'))) { + *s++ = '\0'; + logmsg(rc, "%s", buffer); + i -= s - buffer; + assert(i >= 0); + if (i == 0) + break; + memmove(buffer, s, i); + } + if (n < 0 && errno == EAGAIN) + continue; + if (n <= 0) + break; + } + } + + close(pipe_fds[0]); + + assert(i >= 0 && i < sizeof(buffer)); + if (i) { + buffer[i] = '\0'; logmsg(rc, "%s", buffer); } - waitpid(pid, &pid_status, 0); + if (n < 0 && errno != EAGAIN) + logmsg(rc, "Problem reading rsync's output: %s", + strerror(errno)); + + if (rc->rsync_timeout && now >= deadline) + logmsg(rc, "Fetch took longer than %d seconds, terminating fetch", + rc->rsync_timeout); + + assert(pid > 0); + for (i = 0; i < KILL_MAX && wpid == 0; i++) + if ((wpid = waitpid(pid, &pid_status, 0)) == 0) + kill(pid, SIGTERM); if (WEXITSTATUS(pid_status)) { logmsg(rc, "rsync exited with status %d", pid_status); @@ -543,6 +596,8 @@ static int rsync(const rcynic_ctx_t *rc, return ret; } +#undef whine + static int rsync_crl(const rcynic_ctx_t *rc, const char *uri) { return rsync(rc, NULL, uri); |