summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaweł Redman <pawel.redman@gmail.com>2017-04-07 13:09:34 +0200
committerPaweł Redman <pawel.redman@gmail.com>2017-04-07 13:09:34 +0200
commit848eb855fe98d51b98b096bf4453ab7333192042 (patch)
tree19c17c64c1bbf82321a3ec4c0eb2319da3c9c926
parent03d33627e9341cc161c459d4a557c547cade28af (diff)
(WIP) Handle errors and timeouts in whois.enneract/wip
-rw-r--r--src/lists.c2
-rw-r--r--src/worker.c160
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);
}