Example #1
0
static void destroy_job(child_process *cp)
{
	/*
	 * we must remove the job's timeout ticker,
	 * or we'll end up accessing an already free()'d
	 * pointer, or the pointer to a different child.
	 */
	squeue_remove(sq, cp->ei->sq_event);
	running_jobs--;
	fanout_remove(ptab, cp->ei->pid);

	if (cp->outstd.buf) {
		free(cp->outstd.buf);
		cp->outstd.buf = NULL;
	}
	if (cp->outerr.buf) {
		free(cp->outerr.buf);
		cp->outerr.buf = NULL;
	}

	if(NULL != cp->env) kvvec_destroy(cp->env, KVVEC_FREE_ALL);
	kvvec_destroy(cp->request, KVVEC_FREE_ALL);
	free(cp->cmd);

	free(cp->ei);
	free(cp);
}
Example #2
0
/*
 * Handles adding the command and macros to the kvvec,
 * as well as shipping the command off to a designated
 * worker
 */
static int wproc_run_job(struct wproc_job *job, nagios_macros *mac)
{
	static struct kvvec kvv = KVVEC_INITIALIZER;
	struct kvvec_buf *kvvb;
	struct kvvec *env_kvvp = NULL;
	struct kvvec_buf *env_kvvb = NULL;
	struct wproc_worker *wp;
	int ret, result = OK;

	if (!job || !job->wp)
		return ERROR;

	wp = job->wp;

	if (!kvvec_init(&kvv, 4))	/* job_id, type, command and timeout */
		return ERROR;

	kvvec_addkv(&kvv, "job_id", (char *)mkstr("%d", job->id));
	kvvec_addkv(&kvv, "type", (char *)mkstr("%d", job->type));
	kvvec_addkv(&kvv, "command", job->command);
	kvvec_addkv(&kvv, "timeout", (char *)mkstr("%u", job->timeout));

	/* Add the macro environment variables */
	if(mac) {
		env_kvvp = macros_to_kvv(mac);
		if(NULL != env_kvvp) {
			env_kvvb = kvvec2buf(env_kvvp, '=', '\n', 0);
			if(NULL == env_kvvb) {
				kvvec_destroy(env_kvvp, KVVEC_FREE_KEYS);
			}
			else {
				kvvec_addkv_wlen(&kvv, "env", strlen("env"), env_kvvb->buf,
						env_kvvb->buflen);
			}
		}
	}
	kvvb = build_kvvec_buf(&kvv);
	ret = write(wp->sd, kvvb->buf, kvvb->bufsize);
	if (ret != (int)kvvb->bufsize) {
		logit(NSLOG_RUNTIME_ERROR, TRUE, "wproc: '%s' seems to be choked. ret = %d; bufsize = %lu: errno = %d (%s)\n",
			  wp->name, ret, kvvb->bufsize, errno, strerror(errno));
		destroy_job(job);
		result = ERROR;
	} else {
		wp->jobs_running++;
		wp->jobs_started++;
		loadctl.jobs_running++;
	}
	if(NULL != env_kvvp) kvvec_destroy(env_kvvp, KVVEC_FREE_KEYS);
	if(NULL != env_kvvb) {
		free(env_kvvb->buf);
		free(env_kvvb);
	}
	free(kvvb->buf);
	free(kvvb);

	return result;
}
Example #3
0
static int send_command(int sd, int events, void *discard)
{
	char buf[8192];
	int ret;
	simple_worker *wp;
	struct kvvec *kvv;

	ret = read(sd, buf, sizeof(buf));
	if (ret == 0) {
		iobroker_close(iobs, sd);
		return 0;
	}
	if (ret < 0) {
		printf("main: Failed to read() from fd %d: %s",
			   sd, strerror(errno));
	}

	/* this happens when we're reading from stdin */
	buf[--ret] = 0;

	kvv = kvvec_create(5);
	wp = wps[wp_index++ % NWPS];
	kvvec_addkv(kvv, "job_id", (char *)mkstr("%d", wp->job_index++));
	kvvec_addkv_wlen(kvv, "command", sizeof("command") - 1, buf, ret);
	kvvec_addkv(kvv, "timeout", (char *)mkstr("%d", 10));
	printf("Sending kvvec with %d pairs to worker %d\n", kvv->kv_pairs, wp->pid);
	worker_send_kvvec(wp->sd, kvv);
	kvvec_destroy(kvv, 0);
	return 0;
}
Example #4
0
END_TEST

START_TEST( kvvec_test_lookup_sorted ) {
	struct kvvec *kvv;
	struct key_value *kv;
	kvv = kvvec_create(1);
	ck_assert(kvv != NULL);

	kvvec_addkv_str(kvv, "golf", "7");
	kvvec_addkv_str(kvv, "alfa", "1");
	kvvec_addkv_str(kvv, "echo", "5");
	kvvec_addkv_str(kvv, "foxtrot", "6");
	kvvec_addkv_str(kvv, "bravo", "2");
	kvvec_addkv_str(kvv, "hotel", "8");
	kvvec_addkv_str(kvv, "charlie", "3");
	kvvec_addkv_str(kvv, "delta", "4");

	kvvec_sort(kvv);

	ck_assert_int_eq(kvv->kv_pairs, 8);

	kv = kvvec_fetch(kvv, "foxtrot", strlen("foxtrot"));
	ck_assert(kv != NULL);
	ck_assert_str_eq(kv->key, "foxtrot");
	ck_assert_str_eq(kv->value, "6");

	kv = kvvec_fetch(kvv, "hotel", strlen("hotel"));
	ck_assert(kv != NULL);
	ck_assert_str_eq(kv->key, "hotel");
	ck_assert_str_eq(kv->value, "8");

	kv = kvvec_fetch(kvv, "delta", strlen("delta"));
	ck_assert(kv != NULL);
	ck_assert_str_eq(kv->key, "delta");
	ck_assert_str_eq(kv->value, "4");

	kv = kvvec_fetch(kvv, "golf", strlen("golf"));
	ck_assert(kv != NULL);
	ck_assert_str_eq(kv->key, "golf");
	ck_assert_str_eq(kv->value, "7");

	kv = kvvec_fetch(kvv, "fox", strlen("fox"));
	ck_assert(kv == NULL);

	kv = kvvec_fetch(kvv, "foxtrottrot", strlen("foxtrottrot"));
	ck_assert(kv == NULL);

	kvvec_destroy(kvv, 0);
}
Example #5
0
static void job_error(child_process *cp, struct kvvec *kvv, const char *fmt, ...)
{
	char msg[4096];
	int len;
	va_list ap;
	int ret;

	va_start(ap, fmt);
	len = vsnprintf(msg, sizeof(msg) - 1, fmt, ap);
	va_end(ap);
	if (cp) {
		kvvec_addkv(kvv, "job_id", (char *)mkstr("%d", cp->id));
	}
	kvvec_addkv_wlen(kvv, "error_msg", 9, msg, len);
	ret = send_kvvec(master_sd, kvv);
	if (ret < 0 && errno == EPIPE)
		exit_worker();
	kvvec_destroy(kvv, 0);
}
Example #6
0
END_TEST


/**
 * This test verifies that the is_sorted flag triggers a binary search, which
 * reduces the overall time. Forcing a unsorted list to be reduced means some
 * values shouldn't be found.
 *
 * This shouldn't happen, since is_sorted should only be set if the list is
 * sorted on key.
 */
START_TEST( kvvec_test_lookup_sorted_uses_binary ) {
	struct kvvec *kvv;
	struct key_value *kv;
	kvv = kvvec_create(1);
	ck_assert(kvv != NULL);

	kvvec_addkv_str(kvv, "bravo", "2");
	kvvec_addkv_str(kvv, "charlie", "3");
	kvvec_addkv_str(kvv, "delta", "4");
	kvvec_addkv_str(kvv, "echo", "5");
	kvvec_addkv_str(kvv, "foxtrot", "6");
	kvvec_addkv_str(kvv, "golf", "7");
	kvvec_addkv_str(kvv, "hotel", "8");
	kvvec_addkv_str(kvv, "alfa", "1");

	/* Using non-sorted lookup alfa should be found */
	kv = kvvec_fetch(kvv, "alfa", strlen("alfa"));
	ck_assert(kv != NULL);
	ck_assert_str_eq(kv->key, "alfa");
	ck_assert_str_eq(kv->value, "1");

	/* Forcing sorted flag, binary search shoud be used */
	kvv->kvv_sorted = 1;

	/* alfa shouldn't be found, since binary search reduces to first half */
	kv = kvvec_fetch(kvv, "alfa", strlen("alfa"));
	ck_assert(kv == NULL);

	kvvec_destroy(kvv, 0);
}
Example #7
0
/* a service for registering workers */
static int register_worker(int sd, char *buf, unsigned int len)
{
	int i, is_global = 1;
	struct kvvec *info;
	struct wproc_worker *worker;

	logit(NSLOG_INFO_MESSAGE, TRUE, "wproc: Registry request: %s\n", buf);
	if (!(worker = calloc(1, sizeof(*worker)))) {
		logit(NSLOG_RUNTIME_ERROR, TRUE, "wproc: Failed to allocate worker: %s\n", strerror(errno));
		return 500;
	}

	info = buf2kvvec(buf, len, '=', ';', 0);
	if (info == NULL) {
		free(worker);
		logit(NSLOG_RUNTIME_ERROR, TRUE, "wproc: Failed to parse registration request\n");
		return 500;
	}

	worker->sd = sd;
	worker->ioc = iocache_create(1 * 1024 * 1024);

	iobroker_unregister(nagios_iobs, sd);
	iobroker_register(nagios_iobs, sd, worker, handle_worker_result);

	for(i = 0; i < info->kv_pairs; i++) {
		struct key_value *kv = &info->kv[i];
		if (!strcmp(kv->key, "name")) {
			worker->name = strdup(kv->value);
		}
		else if (!strcmp(kv->key, "pid")) {
			worker->pid = atoi(kv->value);
		}
		else if (!strcmp(kv->key, "max_jobs")) {
			worker->max_jobs = atoi(kv->value);
		}
		else if (!strcmp(kv->key, "plugin")) {
			struct wproc_list *command_handlers;
			is_global = 0;
			if (!(command_handlers = dkhash_get(specialized_workers, kv->value, NULL))) {
				command_handlers = calloc(1, sizeof(struct wproc_list));
				command_handlers->wps = calloc(1, sizeof(struct wproc_worker**));
				command_handlers->len = 1;
				command_handlers->wps[0] = worker;
				dkhash_insert(specialized_workers, strdup(kv->value), NULL, command_handlers);
			}
			else {
				command_handlers->len++;
				command_handlers->wps = realloc(command_handlers->wps, command_handlers->len * sizeof(struct wproc_worker**));
				command_handlers->wps[command_handlers->len - 1] = worker;
			}
			worker->wp_list = command_handlers;
		}
	}

	if (!worker->max_jobs) {
		/*
		 * each worker uses two filedescriptors per job, one to
		 * connect to the master and about 13 to handle libraries
		 * and memory allocation, so this guesstimate shouldn't
		 * be too far off (for local workers, at least).
		 */
		worker->max_jobs = (iobroker_max_usable_fds() / 2) - 50;
	}
	worker->jobs = fanout_create(worker->max_jobs);

	if (is_global) {
		workers.len++;
		workers.wps = realloc(workers.wps, workers.len * sizeof(struct wproc_worker *));
		workers.wps[workers.len - 1] = worker;
		worker->wp_list = &workers;
	}
	wproc_num_workers_online++;
	kvvec_destroy(info, 0);
	nsock_printf_nul(sd, "OK");

	/* signal query handler to release its iocache for this one */
	return QH_TAKEOVER;
}
Example #8
0
static int finish_job(child_process *cp, int reason)
{
	static struct kvvec resp = KVVEC_INITIALIZER;
	struct rusage *ru = &cp->rusage;
	int i, ret;

	/* how many key/value pairs do we need? */
	if (kvvec_init(&resp, 12 + cp->request->kv_pairs) == NULL) {
		/* what the hell do we do now? */
		exit_worker();
	}

	gettimeofday(&cp->stop, NULL);

	if (running_jobs != squeue_size(sq)) {
		wlog("running_jobs(%d) != squeue_size(sq) (%d)\n",
			 running_jobs, squeue_size(sq));
		wlog("started: %d; running: %d; finished: %d\n",
			 started, running_jobs, started - running_jobs);
	}

	/*
	 * we must remove the job's timeout ticker,
	 * or we'll end up accessing an already free()'d
	 * pointer, or the pointer to a different child.
	 */
	squeue_remove(sq, cp->sq_event);

	/* get rid of still open filedescriptors */
	if (cp->outstd.fd != -1)
		iobroker_close(iobs, cp->outstd.fd);
	if (cp->outerr.fd != -1)
		iobroker_close(iobs, cp->outerr.fd);

	cp->runtime = tv_delta_f(&cp->start, &cp->stop);

	/*
	 * Now build the return message.
	 * First comes the request, minus environment variables
	 */
	for (i = 0; i < cp->request->kv_pairs; i++) {
		struct key_value *kv = &cp->request->kv[i];
		/* skip environment macros */
		if (kv->key_len == 3 && !strcmp(kv->key, "env")) {
			continue;
		}
		kvvec_addkv_wlen(&resp, kv->key, kv->key_len, kv->value, kv->value_len);
	}
	kvvec_addkv(&resp, "wait_status", (char *)mkstr("%d", cp->ret));
	kvvec_addkv_wlen(&resp, "outstd", 6, cp->outstd.buf, cp->outstd.len);
	kvvec_addkv_wlen(&resp, "outerr", 6, cp->outerr.buf, cp->outerr.len);
	kvvec_add_tv(&resp, "start", cp->start);
	kvvec_add_tv(&resp, "stop", cp->stop);
	kvvec_addkv(&resp, "runtime", (char *)mkstr("%f", cp->runtime));
	if (!reason) {
		/* child exited nicely */
		kvvec_addkv(&resp, "exited_ok", "1");
		kvvec_add_tv(&resp, "ru_utime", ru->ru_utime);
		kvvec_add_tv(&resp, "ru_stime", ru->ru_stime);
		kvvec_add_long(&resp, "ru_minflt", ru->ru_minflt);
		kvvec_add_long(&resp, "ru_majflt", ru->ru_majflt);
		kvvec_add_long(&resp, "ru_nswap", ru->ru_nswap);
		kvvec_add_long(&resp, "ru_inblock", ru->ru_inblock);
		kvvec_add_long(&resp, "ru_oublock", ru->ru_oublock);
		kvvec_add_long(&resp, "ru_nsignals", ru->ru_nsignals);
	} else {
		/* some error happened */
		kvvec_addkv(&resp, "exited_ok", "0");
		kvvec_addkv(&resp, "error_code", (char *)mkstr("%d", reason));
	}
	ret = send_kvvec(master_sd, &resp);
	if (ret < 0 && errno == EPIPE)
		exit_worker();

	running_jobs--;
	if (cp->outstd.buf) {
		free(cp->outstd.buf);
		cp->outstd.buf = NULL;
	}
	if (cp->outerr.buf) {
		free(cp->outerr.buf);
		cp->outerr.buf = NULL;
	}

	kvvec_destroy(cp->request, KVVEC_FREE_ALL);
	free(cp->cmd);
	free(cp);

	return 0;
}
Example #9
0
int main(int argc, char **argv)
{
	int i, j;
	struct kvvec *kvv, *kvv2, *kvv3;
	struct kvvec_buf *kvvb, *kvvb2;
	struct kvvec k = KVVEC_INITIALIZER;

	t_set_colors(0);

	t_start("key/value vector tests");
	kvv = kvvec_create(1);
	kvv2 = kvvec_create(1);
	kvv3 = kvvec_create(1);
	add_vars(kvv, test_data, 1239819);
	add_vars(kvv, (const char **)argv + 1, argc - 1);

	kvvec_sort(kvv);
	kvvec_foreach(kvv, NULL, walker);

	/* kvvec2buf -> buf2kvvec -> kvvec2buf -> buf2kvvec conversion */
	kvvb = kvvec2buf(kvv, KVSEP, PAIRSEP, OVERALLOC);
	kvv3 = buf2kvvec(kvvb->buf, kvvb->buflen, KVSEP, PAIRSEP, KVVEC_COPY);
	kvvb2 = kvvec2buf(kvv3, KVSEP, PAIRSEP, OVERALLOC);

	buf2kvvec_prealloc(kvv2, kvvb->buf, kvvb->buflen, KVSEP, PAIRSEP, KVVEC_ASSIGN);
	kvvec_foreach(kvv2, kvv, walker);

	kvvb = kvvec2buf(kvv, KVSEP, PAIRSEP, OVERALLOC);

	test(kvv->kv_pairs == kvv2->kv_pairs, "pairs should be identical");

	for (i = 0; i < kvv->kv_pairs; i++) {
		struct key_value *kv1, *kv2;
		kv1 = &kvv->kv[i];
		if (i >= kvv2->kv_pairs) {
			t_fail("missing var %d in kvv2", i);
			printf("[%s=%s] (%d+%d)\n", kv1->key, kv1->value, kv1->key_len, kv1->value_len);
			continue;
		}
		kv2 = &kvv2->kv[i];
		if (!test(!kv_compare(kv1, kv2), "kv pair %d must match", i)) {
			printf("%d failed: [%s=%s] (%d+%d) != [%s=%s (%d+%d)]\n",
				   i,
				   kv1->key, kv1->value, kv1->key_len, kv1->value_len,
				   kv2->key, kv2->value, kv2->key_len, kv2->value_len);
		}
	}

	test(kvvb2->buflen == kvvb->buflen, "buflens must match");
	test(kvvb2->bufsize == kvvb->bufsize, "bufsizes must match");

	if (kvvb2->buflen == kvvb->buflen && kvvb2->bufsize == kvvb->bufsize &&
		!memcmp(kvvb2->buf, kvvb->buf, kvvb->bufsize))
	{
		t_pass("kvvec -> buf -> kvvec conversion works flawlessly");
	} else {
		t_fail("kvvec -> buf -> kvvec conversion failed :'(");
	}

	free(kvvb->buf);
	free(kvvb);
	free(kvvb2->buf);
	free(kvvb2);
	kvvec_destroy(kvv, 1);
	kvvec_destroy(kvv3, KVVEC_FREE_ALL);

	for (j = 0; pair_term_missing[j]; j++) {
		buf2kvvec_prealloc(&k, strdup(pair_term_missing[j]), strlen(pair_term_missing[j]), '=', ';', KVVEC_COPY);
		for (i = 0; i < k.kv_pairs; i++) {
			struct key_value *kv = &k.kv[i];
			test(kv->key_len == kv->value_len, "%d.%d; key_len=%d; value_len=%d (%s = %s)",
				 j, i, kv->key_len, kv->value_len, kv->key, kv->value);
			test(kv->value_len == strlen(kv->value),
				 "%d.%d; kv->value_len(%d) == strlen(%s)(%d)",
				 j, i, kv->value_len, kv->value, (int)strlen(kv->value));
		}
	}

	t_end();
	return 0;
}
Example #10
0
END_TEST

START_TEST( kvvec_test_free_null ) {
	kvvec_destroy(NULL, KVVEC_FREE_ALL);
}