Пример #1
0
/**
* @brief Main entry point
*
* @param argc argument counter 
* @param argv argument array 
* @details uses opts global var
*
* @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise
*/
int main(int argc, char **argv)
{

	char cmd[MAX_LINE_LENGTH];
	
	/* chack opts */
	if(parse_args(argc, argv) == -1) {
		usage();
		return EXIT_FAILURE;
	}

	/* needs to be done here */
	if(opts.opt_e) {
		(void) fprintf(stdout, "<html><head></head><body>\n");
	}

	/* read commands */
	while(fgets(cmd, MAX_LINE_LENGTH, stdin) != NULL) {

		/* and spanw the workers */
		if(spawn_worker(cmd) == -1) {
			return EXIT_FAILURE;
		}

	}

	if(opts.opt_e) {
		(void) fprintf(stdout, "</body></head>\n");
	}

	return EXIT_SUCCESS;
}
Пример #2
0
int setup_workers(struct ev_loop* loop, struct listener_s* listener) {
    char* ip = listener->argv[1];
    uint16_t port = (uint16_t)atoi(listener->argv[2]);

    int listen_fd;
#ifndef SO_REUSEPORT
    listen_fd = start_listen(ip, port);
#endif

    pid_t pid;
    int i;
    struct worker_s* workers = listener->workers;
    for (i = 0; i < listener->worker_count; i++) {
#ifdef SO_REUSEPORT
        listen_fd = start_listen(ip, port);
#endif
        workers[i].listen_fd = listen_fd;
        workers[i].worker_id = i;
        workers[i].listener = listener;
        pid = spawn_worker(&workers[i]);
        if (pid < 0) {
            return -1;
        }
        workers[i].pid = pid;
        ev_child_init(&workers[i].cwatcher, exit_cb, pid, 0);
        ev_child_start(loop, &workers[i].cwatcher);
    }

    return 0;
}
Пример #3
0
int init_workers(int desired_workers)
{
	worker_process **wps;
	int i;

	if (desired_workers <= 0) {
		desired_workers = 4;
	}

	if (workers_alive() == desired_workers)
		return 0;

	/* can't shrink the number of workers (yet) */
	if (desired_workers < num_workers)
		return -1;

	wps = calloc(desired_workers, sizeof(worker_process *));
	if (!wps)
		return -1;

	if (workers) {
		if (num_workers < desired_workers) {
			for (i = 0; i < num_workers; i++) {
				wps[i] = workers[i];
			}
		}

		free(workers);
	}

	workers = wps;
	for (i = 0; i < desired_workers; i++) {
		int ret;
		worker_process *wp;

		if (wps[i])
			continue;

		wp = spawn_worker(worker_init_func, (void *)get_global_macros());
		if (!wp) {
			logit(NSLOG_RUNTIME_WARNING, TRUE, "Failed to spawn worker: %s\n", strerror(errno));
			free_worker_memory(0);
			return ERROR;
		}
		set_socket_options(wp->sd, 256 * 1024);

		wps[i] = wp;
		ret = iobroker_register(nagios_iobs, wp->sd, wp, handle_worker_result);
		if (ret < 0) {
			printf("Failed to register worker socket with iobroker %p\n", nagios_iobs);
			exit(1);
		}
	}
	num_workers = desired_workers;

	logit(NSLOG_INFO_MESSAGE, TRUE, "Workers spawned: %d\n", num_workers);
	return 0;
}
Пример #4
0
	balanced_thread_pool(float idle_time_threshold_for_new_worker = .1f, float kernel_time_thershold_for_despawn_extra_worker = .1f, float idle_time_threshold_for_despawn_surplus_worker = .15f)
		: idle_time_threshold_for_new_worker(idle_time_threshold_for_new_worker),
		  kernel_time_thershold_for_despawn_extra_worker(kernel_time_thershold_for_despawn_extra_worker),
		  idle_time_threshold_for_despawn_surplus_worker(idle_time_threshold_for_despawn_surplus_worker) {
		const int threads = min_worker_threads();
		const int max_threads = std::thread::hardware_concurrency();
		for (int i = 0; i < threads; ++i)
			spawn_worker(max_threads - threads + i);
	}
Пример #5
0
void mt_task_queue::enqueue(gtask const & t) {
    lean_always_assert(get_state(t).load() < task_state::Running);
    lean_always_assert(get_imp(t));
    get_state(t) = task_state::Queued;
    m_queue[get_prio(t)].push_back(t);
    if (m_required_workers > 0) {
        spawn_worker();
    } else {
        m_queue_added.notify_one();
    }
    notify_queue_changed();
}
Пример #6
0
int main(int argc, char **argv)
{
	simple_worker *wp;
	int i;
#ifdef HAVE_SIGACTION
	struct sigaction sig_action;

	sig_action.sa_sigaction = NULL;
	sigfillset(&sig_action.sa_mask);
	sig_action.sa_flags=SA_NOCLDSTOP;
	sig_action.sa_handler = child_exited;
	sigaction(SIGCHLD, &sig_action, NULL);

	sig_action.sa_flags = SA_NODEFER|SA_RESTART;
	sig_action.sa_handler = sighandler;
	sigfillset(&sig_action.sa_mask);
	sigaction(SIGINT, &sig_action, NULL);
	sigaction(SIGPIPE, &sig_action, NULL);
#else /* HAVE_SIGACTION */

	signal(SIGINT, sighandler);
	signal(SIGPIPE, sighandler);
	signal(SIGCHLD, child_exited);
#endif /* HAVE_SIGACTION */

	iobs = iobroker_create();
	if (!iobs)
		die("Failed to create io broker set");

	for (i = 0; i < NWPS; i++) {
		wp = spawn_worker(print_some_crap, "lalala");
		if (!wp) {
			die("Failed to spawn worker(s)\n");
		}
		wps[i] = wp;
		printf("Registering worker sd %d with io broker\n", wp->sd);
		iobroker_register(iobs, wp->sd, wp, print_input);
	}

	iobroker_register(iobs, fileno(stdin), NULL, send_command);

	/* get to work */
	while (!sigreceived && iobroker_get_num_fds(iobs)) {
		iobroker_poll(iobs, -1);
	}

	for (i = 0; i < NWPS; i++) {
		kill(wps[i]->pid, SIGKILL);
	}

	return 0;
}
Пример #7
0
	/*
	 *	@brief	Load balances the pool.
	 *			Creates/destroys workers, as needed.
	 */
	void load_balance() {
		assert(is_main_thread());

		const auto now = std::chrono::high_resolution_clock::now();
		std::chrono::duration<float> time_since_last_pool_balance = now - last_pool_balance;
		if (time_since_last_pool_balance.count() < .05f)
			return;
		last_pool_balance = now;

		float idle_frac = .0f;
		float kernel_frac = .0f;
		float user_frac = .0f;
		if (!sys_times.get_times_since_last_call(idle_frac, kernel_frac, user_frac))
			return;

		// Check for dead workers (e.g. exception thrown)
		for (auto it = workers.begin(); it != workers.end();) {
			if (it->is_terminated())
				it = workers.erase(it);
			else
				++it;
		}

		// Load balance
		const unsigned min_threads = min_worker_threads();
		const auto req = get_pending_requests_count();
		const auto threads_sleeping = get_sleeping_workers_count();
		const auto total_workers_count = get_workers_count();
		if (threads_sleeping == 0 &&
			idle_frac > idle_time_threshold_for_new_worker &&
			total_workers_count < max_worker_threads()) {
			spawn_worker();
		}
		else if (workers.size() > min_threads &&
			(kernel_frac > kernel_time_thershold_for_despawn_extra_worker ||
				(req == 0 && idle_frac > idle_time_threshold_for_despawn_surplus_worker) ||
				threads_sleeping > 1)) {
			despawn_worker();
		}
	}
Пример #8
0
void exit_cb(struct ev_loop* loop, struct ev_child* cwatcher, int status) {
    struct worker_s* worker = container_of(cwatcher, struct worker_s, cwatcher);
    ev_child_stop(loop, cwatcher);

    err("worker[pid:%d] exit with status:%d, stop_moniter:%d", worker->pid, cwatcher->rstatus, worker->listener->stop_moniter);

    struct timeval tv;
    gettimeofday(&tv, 0);

    if (worker->listener->stop_moniter || 2 > ((int)tv.tv_sec - worker->starttime)) {
        return;
    }

    worker->pid = spawn_worker(worker);
    if (-1 == worker->pid) {
        err("spawn worker failed, worker_id:%d", worker->worker_id);
        exit(EXIT_FAILURE);
    }

    err("worker %d restart, new pid: %d", worker->worker_id, worker->pid);

    ev_child_set(cwatcher, worker->pid, 0);
    ev_child_start(loop, cwatcher);
}
Пример #9
0
int
main(int argc, char *argv[])
{
	ssize_t n;
	Packet pckt_req;

	struct sigaction sigchld_action = {
		.sa_handler = SIG_DFL,
		.sa_flags = SA_NOCLDWAIT
	};
	// Avoid having to wait for child processes to exit
	sigaction(SIGCHLD, &sigchld_action, NULL);

	printf("Starting server... ");
	if (init_server() == -1) {
		printf("\x1B[31m[ERROR]\x1B[0m\nError initializing server.\n");
		return 1;
	}
	printf("\x1B[32m[OK]\x1B[0m\n");

	// Assign custom sleep time or default if error or no time specified
	if (argc == 2 && sscanf(argv[1], "%d", &sleep_time) == 0)
		sleep_time = DEFAULT_SLEEP_TIME;

	printf("Sleep time set to %d second(s).\n", sleep_time);

	while (1) {
		// Receive requests and spawn workers
		n = pk_receive(SRV_ID, &pckt_req, sizeof(pckt_req));
		printf("%d byte/s received...\n", (int) n);

		spawn_worker(&pckt_req);
	}

	return 0;
}
Пример #10
0
void mt_task_queue::wait_for_finish(gtask const & t) {
    if (!t || get_state(t).load() > task_state::Running) return;
    unique_lock<mutex> lock(m_mutex);
    submit_core(t, get_default_prio());
    if (get_state(t).load() <= task_state::Running) {
        int additionally_required_workers = 0;
        if (g_current_task) {
            additionally_required_workers++;
            if (m_sleeping_workers == 0) {
                spawn_worker();
            } else {
                m_wake_up_worker.notify_one();
            }
        }
        scoped_add<int> inc_required(m_required_workers, additionally_required_workers);
        get_sched_info(t).wait(lock, [&] {
            return get_state(t).load() > task_state::Running;
        });
    }
    switch (get_state(t).load()) {
        case task_state::Failed: case task_state::Success: return;
        default: throw exception("invalid task state");
    }
}
Пример #11
0
static void query(void)
{
	struct query_list *q = free_queries;
	enum helper_exit_status r;

	/* find an unused queue entry */
	if (q == NULL) {
		q = malloc(sizeof(*q));
		if (q == NULL) {
			syslog(LOG_ERR, "malloc(3) failed");
			exit(HES_MALLOC);
		}
	} else {
		free_queries = q->next;
	}

	r = read_pipe(PLUTO_QFD, (unsigned char *)&q->aq,
		      sizeof(q->aq), sizeof(q->aq));

	if (r == HES_OK) {
		/* EOF: we're done, except for unanswered queries */
		struct worker_info *w;

		eof_from_pluto = TRUE;
		q->next = free_queries;
		free_queries = q;

		/* Send bye-bye to unbusy processes.
		 * Note that if there are queued queries, there won't be
		 * any non-busy workers.
		 */
		for (w = wi; w != wi_roof; w++)
			if (!w->busy)
				send_eof(w);
	} else if (r != HES_CONTINUE) {
		exit(r);
	} else if (q->aq.qmagic != ADNS_Q_MAGIC) {
		syslog(LOG_ERR, "error in query from Pluto: bad magic");
		exit(HES_BAD_MAGIC);
	} else {
		struct worker_info *w;

		/* got a query */

		/* add it to FIFO */
		q->next = NULL;
		if (oldest_query == NULL)
			oldest_query = q;
		else
			newest_query->next = q;
		newest_query = q;

		/* See if any worker available */
		for (w = wi;; w++) {
			if (w == wi_roof) {
				/* no free worker */
				if (w == wi + MAX_WORKERS)
					break; /* no more to be created */
				/* make a new one */
				if (!spawn_worker())
					break; /* cannot create one at this time */
			}
			if (!w->busy) {
				/* assign first to free worker */
				forward_query(w);
				break;
			}
		}
	}
	return;
}
Пример #12
0
int main(int argc, char** argv)
{
	/*
	 * These counters will be used as offsets for messages when using pipes
	 * i.e. write(stack + stack_pos, strlen(stack + stack_pos))
	 */
	int stack_pos = 0;
	int input_pos = 1;

	char input[MAX_BUF_SIZE] = "";
	char stack[MAX_BUF_SIZE] = "";
	char result[MAX_BUF_SIZE] = "";

	fgets(input, MAX_BUF_SIZE - 1, stdin);
	fgets(stack, MAX_BUF_SIZE - 1, stdin);
	fgets(result, MAX_BUF_SIZE - 1, stdin);

	input[strcspn(input, "\n")] = 0;
	stack[strcspn(stack, "\n")] = 0;
	result[strcspn(result, "\n")] = 0;

	if ((strlen(input) == 0)) // End of input, clearing stack
	{
		while (stack_pos < strlen(stack))
		{
				write_to_result(result, ' ');
				write_to_result(result, stack[stack_pos++]);
		}

		if (debug)
			fprintf(stderr, "pid: %d end of input! result: %s \n",
				getpid(), result
			);

		printf("%s", result);
		return 0;
	}

	if (debug)
		fprintf(stderr, "pid: %d input[0]: %c stack: %s result: %s \n",
			getpid(), input[0], stack, result
		);

	switch (input[0])
	{
		case '-': /* Odp.: Przez liczbę całkowitą należy rozumież liczbę całkowitą bez znaku */
		case '^':
		case '*':
		case '/':
		case '+':
			while (stack_pos < strlen(stack))
			{
				/* treating every op as left-associative */
				if (op_priority(input[0]) <= op_priority(stack[stack_pos]))
				{
					write_to_result(result, ' ');
					write_to_result(result, stack[stack_pos++]);
				}
				else break;
			}

			write_to_result(result, ' ');

		case '(':
			write_to_stack(stack, input[0], &stack_pos);
			break;
		case ')':
			while(stack_pos <= strlen(stack))
			{
				if (stack[stack_pos] != '(')
				{
					write_to_result(result, ' ');
					write_to_result(result, stack[stack_pos++]);
				}
				else
				{
					 stack_pos++;
					 break;
				}
			}
		case ' ':
		case '\n':
			break;
		default:
			add_space_to_result(result);
			/*
			 * We only checked first character of expression (input[0])
			 * Now let's check if there are other characters in expression
			 */
			for (int i = 0; i < strlen(input); i++)
			{
				/* When there's no space around parenthesis
				 * easier testing
				 */
				if (!isspace(input[i]) && (input[i] != ')'))
				{
					write_to_result(result, input[i]);
				}
				else
				{
					input_pos = i + ((input[i] != ')') ? (1) : (0));
					break;
				}
			}
			break;
	}

	/* Moving offset to first nonwhite character in input */
	while ((input_pos < strlen(input)) && (isspace(input[input_pos]))) input_pos++;

	spawn_worker(input + input_pos, stack + stack_pos, result);

	return 0;
}
Пример #13
0
int
main(int argc, char **argv)
{
	struct timespec mtime = { 0 };
	sigset_t mask, sigmask_orig;
	int c, fd;
	int ep_timeout   = 0;
	int ignore_timer = 0;
	int new_events   = 0;

	char *eventdir, *prog, **prog_args;

	struct option long_options[] = {
		{ "help", no_argument, 0, 'h' },
		{ "version", no_argument, 0, 'V' },
		{ "foreground", no_argument, 0, 'f' },
		{ "loglevel", required_argument, 0, 'L' },
		{ "logfile", required_argument, 0, 'l' },
		{ "pidfile", required_argument, 0, 'p' },
		{ "timeout", required_argument, 0, 't' },
		{ 0, 0, 0, 0 }
	};

	while ((c = getopt_long(argc, argv, "hVfL:l:p:", long_options, NULL)) != -1) {
		switch (c) {
			case 't':
				timeout = atoi(optarg);
				if (!timeout)
					timeout = DEFAULT_TIMEOUT;
				break;
			case 'p':
				pidfile = optarg;
				break;
			case 'l':
				logfile = optarg;
				break;
			case 'L':
				log_priority = logging_level(optarg);
				break;
			case 'f':
				daemonize = 0;
				break;
			case 'V':
				printf("%s %s\n", PROGRAM_NAME, VERSION);
				return EXIT_SUCCESS;
			default:
			case 'h':
				printf("Usage: %s [options] DIRECTORY PROGRAM [ARGS...]\n"
				       "\nThe utility monitors the DIRECTORY and when\n"
				       "new files appear run the PROGRAM.\n\n"
				       "Options:\n"
				       " -p, --pidfile=FILE   pid file location;\n"
				       " -l, --logfile=FILE   log file;\n"
				       " -L, --loglevel=LVL   logging level;\n"
				       " -t, --timeout=SEC    number of seconds that need to wait"
				       "                      for files before the PROGRAM launch;\n"
				       " -f, --foreground     stay in the foreground;\n"
				       " -V, --version        print program version and exit;\n"
				       " -h, --help           show this text and exit.\n"
				       "\n",
				       PROGRAM_NAME);
				return EXIT_SUCCESS;
		}
	}

	if (optind >= argc)
		error(EXIT_FAILURE, 0, "You must specify the directory");

	eventdir = argv[optind++];

	if (optind >= argc)
		error(EXIT_FAILURE, 0, "You must specify the program");

	prog = canonicalize_file_name(argv[optind]);
	if (!prog)
		error(EXIT_FAILURE, errno, "Bad program");

	argv[optind] = strrchr(prog, '/');
	if (!argv[optind])
		argv[optind] = prog;

	prog_args = argv + optind;

	if (!log_priority)
		log_priority = logging_level("info");

	if (pidfile && check_pid(pidfile))
		error(EXIT_FAILURE, 0, "%s: already running", PROGRAM_NAME);

	if (chdir("/") < 0)
		error(EXIT_FAILURE, errno, "%s: chdir(/)", PROGRAM_NAME);

	close(STDIN_FILENO);

	if ((fd = open("/dev/null", O_RDONLY)) < 0)
		error(EXIT_FAILURE, errno, "%s: open(/dev/null)", PROGRAM_NAME);

	if (fd != STDIN_FILENO) {
		dup2(fd, STDIN_FILENO);
		close(fd);
	}

	if (daemonize && daemon(0, 1) < 0)
		error(EXIT_FAILURE, errno, "%s: daemon", PROGRAM_NAME);

	logging_init();

	info("starting version %s", VERSION);

	if (pidfile && write_pid(pidfile) == 0)
		return EXIT_FAILURE;

	sigfillset(&mask);
	sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);

	sigdelset(&mask, SIGABRT);
	sigdelset(&mask, SIGSEGV);

	if ((fd_ep = epoll_create1(EPOLL_CLOEXEC)) < 0)
		fatal("epoll_create1: %m");

	if ((fd_signal = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC)) < 0)
		fatal("signalfd: %m");

	epollin_add(fd_ep, fd_signal);

	if ((fd_eventdir = inotify_init1(IN_NONBLOCK | IN_CLOEXEC)) < 0)
		fatal("inotify_init1: %m");

	if (inotify_add_watch(fd_eventdir, eventdir, IN_ONLYDIR | IN_DONT_FOLLOW | IN_MOVED_TO | IN_CLOSE_WRITE) < 0)
		fatal("inotify_add_watch: %m");

	epollin_add(fd_ep, fd_eventdir);

	ignore_timer = is_dir_not_empty(eventdir);

	if (clock_gettime(CLOCK_MONOTONIC, &now) < 0)
		fatal("clock_gettime: %m");
	last.tv_sec = now.tv_sec;

	while (!do_exit) {
		struct epoll_event ev[42];
		int i, fdcount;
		ssize_t size;

		if ((fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), ep_timeout)) < 0)
			continue;

		if (!ep_timeout)
			ep_timeout = timeout * 1000;

		for (i = 0; i < fdcount; i++) {

			if (!(ev[i].events & EPOLLIN)) {
				continue;

			} else if (ev[i].data.fd == fd_signal) {
				struct signalfd_siginfo fdsi;

				size = TEMP_FAILURE_RETRY(read(fd_signal,
				                               &fdsi, sizeof(struct signalfd_siginfo)));

				if (size != sizeof(struct signalfd_siginfo)) {
					err("unable to read signal info");
					continue;
				}

				handle_signal(fdsi.ssi_signo);

			} else if (ev[i].data.fd == fd_eventdir) {
				read_inotify_events(fd_eventdir);
				new_events += 1;
			}
		}

		if (new_events) {
			struct stat sb;

			new_events = 0;

			if (lstat(eventdir, &sb) < 0)
				fatal("lstat: %s: %m", eventdir);

			if (mtime.tv_sec != sb.st_mtim.tv_sec || mtime.tv_nsec != sb.st_mtim.tv_nsec) {
				if (clock_gettime(CLOCK_MONOTONIC, &now) < 0)
					fatal("clock_gettime: %m");
				last.tv_sec = now.tv_sec;
			}

			mtime.tv_sec  = sb.st_mtim.tv_sec;
			mtime.tv_nsec = sb.st_mtim.tv_nsec;
		}

		if (worker_pid)
			continue;

		if (!ignore_timer) {
			if (clock_gettime(CLOCK_MONOTONIC, &now) < 0)
				fatal("clock_gettime: %m");

			if (now.tv_sec < last.tv_sec || (now.tv_sec - last.tv_sec) < timeout)
				continue;
		}
		ignore_timer = 0;

		if ((worker_pid = spawn_worker(prog, prog_args)) < 0)
			fatal("spawn_worker: %m");

		dbg("Run worker %d", worker_pid);
	}

	epollin_remove(fd_ep, fd_signal);
	epollin_remove(fd_ep, fd_eventdir);

	free(prog);

	if (pidfile)
		remove_pid(pidfile);

	logging_close();

	return EXIT_SUCCESS;
}
Пример #14
0
int main(int argc, char *argv[])
{
	int rc = 1;
	int ret;

	signal(SIGINT, signal_handler);

	int c;
	char ep[28];
	const char *host = "0.0.0.0";
	char *ohost = NULL;
	int port = 48005;

	void *ctx = NULL;
	void *fe = NULL;
	void *be = NULL;

	while ((c = getopt (argc, argv, "h:p:")) != -1)
		switch (c) {
			case 'h':
				ohost = strdup(optarg);
				break;
			case 'p':
				port = atoi(optarg);
		}

	pid_t pid = spawn_worker();
	printf("Spawned worker w/ pid %d\n", pid);

	if (ohost != NULL)
		host = ohost;

	snprintf(ep, 28, "tcp://%s:%d", host, port);
	if (ohost != NULL)
		free(ohost);

	ctx = zmq_ctx_new();
	if (ctx == NULL)
		goto finished;

	fe = zmq_socket(ctx, ZMQ_ROUTER);
	if (fe == NULL)
		goto finished;

	be = zmq_socket(ctx, ZMQ_DEALER);
	if (be == NULL)
		goto finished;

	ret = zmq_bind(fe, ep);
	if (ret < 0) {
		fprintf(stderr, "Unable to bind socket\n");
		goto finished;
	}

	zmq_bind(be, WORKER_IPC);

	zmq_pollitem_t items [] = {
		{ fe, 0, ZMQ_POLLIN, 0 },
		{ be,  0, ZMQ_POLLIN, 0 }
	};

	printf("Waiting for messages...\n");

	void *cred;
	void *buf;
	int len;

	while (1) {
		zmq_msg_t message;
		zmq_msg_t out;
		ret = zmq_poll(items, 2, -1);

		if (ret < 0)
			if (errno == EINTR) {
				break;
			}

		if (items[0].revents & ZMQ_POLLIN) {
			while (1) {
				zmq_msg_init(&message);
				zmq_msg_recv(&message, fe, 0);
				int more = zmq_msg_more(&message);

				if (!more) {
					cred = zmq_msg_data(&message);

					munge_err_t err = munge_decode(cred, NULL, &buf, &len, NULL, NULL);
					if (err != EMUNGE_SUCCESS)
						fprintf(stderr, "Munge failed to decode\n");

					zmq_msg_init_data(&out, buf, len, free_buf, NULL);
					zmq_msg_send(&out, be, 0);
					zmq_msg_close(&out);
				} else {
					zmq_msg_send(&message, be, more? ZMQ_SNDMORE: 0);
				}

				zmq_msg_close(&message);
				if (!more)
					break;
			}
		}
		if (items[1].revents & ZMQ_POLLIN) {
			while (1) {
				zmq_msg_init(&message);
				zmq_msg_recv(&message, be, 0);
				int more = zmq_msg_more(&message);
				zmq_msg_send(&message, fe, more? ZMQ_SNDMORE: 0);
				zmq_msg_close(&message);
				if (!more)
					break;
			}
		}
	}

	rc = 0;

	printf("Shutting down\n");

finished:
	printf("Stopping workers...\n");

	pid_t w;
	int status;
	do {
		w = waitpid(pid, &status, 0);
		if (w == -1) {
			fprintf(stderr, "Unable to wait\n");
			break;
		}
	} while (!WIFEXITED(status) && !WIFSIGNALED(status));

	printf("Finishing cleanup\n");
	ret = zmq_close(fe);
	if (ret < 0)
		fprintf(stderr, "Failed to close frontend socket\n");

	ret = zmq_close(be);
	if (ret < 0)
		fprintf(stderr, "Failed to close backend socket\n");

	ret = zmq_ctx_destroy(ctx);
	if(ret < 0)
		fprintf(stderr, "Failed to stop ZMQ\n");

	return rc;
}