diff options
-rw-r--r-- | src/lists.c | 2 | ||||
-rw-r--r-- | src/worker.c | 160 |
2 files changed, 123 insertions, 39 deletions
diff --git a/src/lists.c b/src/lists.c index 0a308c0..ea35cea 100644 --- a/src/lists.c +++ b/src/lists.c @@ -171,7 +171,7 @@ int lists_test(const char *revdns, const char *whois) !strstr(revdns, entry->pattern)) continue; - if (entry->type == ENTRY_WHOIS && + if (whois && entry->type == ENTRY_WHOIS && !strstr(whois, entry->pattern)) continue; diff --git a/src/worker.c b/src/worker.c index 82d4ce6..7553fb8 100644 --- a/src/worker.c +++ b/src/worker.c @@ -18,6 +18,8 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "shared.h" #include <netdb.h> // reverse DNS +#include <sys/types.h> +#include <sys/wait.h> static job_t *job_queue; static pthread_cond_t job_queue_cond = PTHREAD_COND_INITIALIZER; @@ -62,47 +64,119 @@ void job_quit(void) pthread_cond_broadcast(&job_queue_cond); } -// FIXME: refactor with vstrs -static int read_everything(FILE *fp, char **buffer) +// Execute a process and read its stdout. +// path: the path to the process. +// argv: the argument list. The first arg is usually expected to be the +// process's path and the last to be NULL. +// timeout: wait for at most this long, kill the process if it takes to long. +// out: the output. +// NOTE: the caller is supposed to free the returned string +// NOTE: it's undefined if this function fails +static int get_process_output(const char *path, char * const *argv, + const struct timeval *timeout, char **out) { - size_t read = 0, alloc = 128, partial; + int rv, pipefds[2], status; + pid_t child; + struct timeval time_left; + vstr_t vstr = VSTR_INITIALIZER; + + if (pipe(pipefds) == -1) { + perror("pipe"); + return 1; + } - alloc = 128; - *buffer = malloc(alloc); - if (!*buffer) - goto oom; + child = fork(); + if (child == -1) { + close(pipefds[0]); + close(pipefds[1]); + perror("fork"); + return 1; + } - while (1) { - size_t left = alloc - read; + if (child == 0) { + close(pipefds[0]); + dup2(pipefds[1], STDOUT_FILENO); + close(pipefds[1]); + if (execv(path, argv) == -1) + return 1; + } - if (left == 0) { - alloc *= 2; - *buffer = realloc(*buffer, alloc); - if (!buffer) - goto oom; + close(pipefds[1]); - continue; - } + memcpy(&time_left, timeout, sizeof(struct timeval)); - partial = fread((*buffer) + read, 1, left, fp); - if (partial == 0) { - if (ferror(fp)) - return 1; + for (;;) { + fd_set fds; + ssize_t i, r; + char buffer[1024]; + + FD_ZERO(&fds); + FD_SET(pipefds[0], &fds); + + rv = select(pipefds[0] + 1, &fds, NULL, NULL, &time_left); + if (rv == 0) { + eprintf("select timed out\n"); + goto error; + } else if (rv == -1) { + perror("select"); + goto error; + } + r = read(pipefds[0], buffer, sizeof(buffer)); + if (r == 0) break; + else if (r == -1) { + perror("read"); + goto error; } - read += partial; + // FIXME: don't do this char-by-char + for (i = 0; i < r; i++) + if (vstr_putc(&vstr, buffer[i])) { + eprintf("get_process_output: out of memory\n"); + goto error; + } } + if (waitpid(child, &status, 0) == -1) { + perror("waitpid"); + // FIXME: figure out it's safe to ignore waitpid errors. + // I'm not sure if this can leak resources. + } else { + // FIXME: check the exit code for errors + eprintf("subprocess exited with code %i\n", status); + } + + close(pipefds[0]); + + if (vstr.size == 0) { + *out = strdup(""); + if (!*out) { + eprintf("get_process_output: out of memory\n"); + return 1; + } + } else { + + if (vstr.alloc > vstr.size + 1) { + vstr.data = realloc(vstr.data, vstr.size + 1); + if (!vstr.data) { + eprintf("get_process_output: realloc failed (wtf?)\n"); + return 1; + } + } + + vstr.data[vstr.size] = '\0'; + *out = vstr.data; + } - *buffer = realloc(*buffer, read + 1); - (*buffer)[read] = '\0'; return 0; -oom: - fprintf(stderr, "read_everything: out of memory\n"); - abort(); +error: + vstr_destroy(&vstr); + if (kill(child, SIGTERM) == -1) + perror("kill"); + close(pipefds[0]); + return 1; } // Check if the database entry is complete (all fields are DB_VALID). @@ -126,10 +200,17 @@ static void update_entry(db_entry *entry, db_entry_field *field, char *data) { pthread_mutex_lock(&entry->mutex); - field->state = DB_VALID; free(field->data); - field->data = data; - field->exp_time = get_time() + 24 * TIME_HOUR; + + if (data) { + field->state = DB_VALID; + field->data = data; + field->exp_time = get_time() + 24 * TIME_HOUR; + } else { + field->state = DB_VALID; + field->data = NULL; + field->exp_time = get_time() + 30 * TIME_MINUTE; + } // invalidate the result cache entry->cached_result_valid = false; @@ -213,18 +294,21 @@ static void worker_job_revdns(job_t *job) // This job makes a WHOIS query and stores it in the database. static void worker_job_whois(job_t *job) { - char command[80]; - FILE *fp; - char *out; + char *argv[3], iparg[20], *out; + struct timeval timeout = {15, 0}; // FIXME: don't hardcode this - snprintf(command, sizeof(command), "whois \"%hhu.%hhu.%hhu.%hhu\"", - (job->ipv4 >> 24) & 0xFF, (job->ipv4 >> 16) & 0xFF, + snprintf(iparg, sizeof(iparg), "%hhu.%hhu.%hhu.%hhu", + (job->ipv4 >> 24) & 0xFF, (job->ipv4 >> 16) & 0xFF, (job->ipv4 >> 8) & 0xFF, job->ipv4 & 0xFF); - DEBUG("%s\n", command); - fp = popen(command, "r"); - read_everything(fp, &out); // FIXME: handle errors - fclose(fp); + argv[0] = "/bin/whois"; + argv[1] = iparg; + argv[2] = NULL; + + if (get_process_output("/bin/whois", argv, &timeout, &out)) { + eprintf("whois failed for %s\n", iparg); + out = NULL; + } update_entry(job->entry, &job->entry->whois, out); } |