diff options
| author | Paweł Redman <pawel.redman@gmail.com> | 2017-04-07 13:09:34 +0200 | 
|---|---|---|
| committer | Paweł Redman <pawel.redman@gmail.com> | 2017-04-07 13:09:34 +0200 | 
| commit | 848eb855fe98d51b98b096bf4453ab7333192042 (patch) | |
| tree | 19c17c64c1bbf82321a3ec4c0eb2319da3c9c926 /src | |
| parent | 03d33627e9341cc161c459d4a557c547cade28af (diff) | |
(WIP) Handle errors and timeouts in whois.enneract/wip
Diffstat (limited to 'src')
| -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);  }  | 
