Example #1
0
static int __attempt_send(struct homer_sender *hs, GString *gs) {
	int ret;

	ret = write(hs->socket.fd, gs->str, gs->len);
	if (ret == gs->len) {
		// full write
		g_string_free(gs, TRUE);
		return 0;
	}
	if (ret < 0) {
		if (errno != EWOULDBLOCK && errno != EAGAIN) {
			ilog(LOG_ERR, "Write error to Homer at %s: %s",
					endpoint_print_buf(&hs->endpoint), strerror(errno));
			__reset(hs);
			return 1;
		}
		ilog(LOG_DEBUG, "Home write blocked");
		// XXX use poller for blocked writes?
		return 2;
	}
	// partial write
	ilog(LOG_DEBUG, "Home write blocked (partial write)");
	g_string_erase(gs, 0, ret);
	return 3;
}
Example #2
0
/* agent must be locked */
static struct ice_candidate_pair *__pair_candidate(struct stream_fd *sfd, struct ice_agent *ag,
		struct ice_candidate *cand)
{
	struct ice_candidate_pair *pair;

	if (sfd->socket.family != cand->endpoint.address.family)
		return NULL;

	pair = g_slice_alloc0(sizeof(*pair));

	pair->agent = ag;
	pair->remote_candidate = cand;
	pair->local_intf = sfd->local_intf;
	pair->sfd = sfd;
	if (cand->component_id != 1)
		PAIR_SET(pair, FROZEN);
	__do_ice_pair_priority(pair);
	__new_stun_transaction(pair);

	g_queue_push_tail(&ag->candidate_pairs, pair);
	g_hash_table_insert(ag->pair_hash, pair, pair);
	g_tree_insert(ag->all_pairs, pair, pair);

	ilog(LOG_DEBUG, "Created candidate pair "PAIR_FORMAT" between %s and %s, type %s", PAIR_FMT(pair),
			sockaddr_print_buf(&sfd->socket.local.address),
			endpoint_print_buf(&cand->endpoint),
			ice_candidate_type_str(cand->type));

	return pair;
}
Example #3
0
static int __check_conn(struct homer_sender *hs, int ret) {
	if (ret == 0) {
		ilog(LOG_INFO, "Connection to Homer at %s has been established",
				endpoint_print_buf(&hs->endpoint));
		hs->state = __established;
		return hs->state(hs);
	}
	if (ret == 1) {
		ilog(LOG_DEBUG, "connection to Homer is in progress");
		hs->state = __in_progress;
		return 0;
	}

	ilog(LOG_ERR, "Failed to connect to Homer at %s: %s",
			endpoint_print_buf(&hs->endpoint), strerror(errno));

	__reset(hs);
	return -1;
}
Example #4
0
static int __no_socket(struct homer_sender *hs) {
	int ret;

	if (hs->retry > time(NULL))
		return 0;

	ilog(LOG_INFO, "Connecting to Homer at %s", endpoint_print_buf(&hs->endpoint));

	ret = connect_socket_nb(&hs->socket, hs->protocol, &hs->endpoint);
	return __check_conn(hs, ret);
}
Example #5
0
/* call(W) or call(R)+agent must be locked - no in_lock or out_lock must be held */
static int __check_valid(struct ice_agent *ag) {
	struct call_media *media = ag->media;
	struct packet_stream *ps;
	GList *l, *k, *m;
	GQueue all_compos;
	struct ice_candidate_pair *pair;
//	const struct local_intf *ifa;
	struct stream_fd *sfd;

	if (!ag) {
		ilog(LOG_ERR, "ice ag is NULL");
		return 0;
	}

	__get_complete_valid_pairs(&all_compos, ag);

	if (!all_compos.length) {
		ilog(LOG_DEBUG, "ICE not completed yet");
		return 0;
	}

	pair = all_compos.head->data;
	ilog(LOG_DEBUG, "ICE completed, using pair "PAIR_FORMAT, PAIR_FMT(pair));
	AGENT_SET(ag, COMPLETED);

	for (l = media->streams.head, k = all_compos.head; l && k; l = l->next, k = k->next) {
		ps = l->data;
		pair = k->data;

		mutex_lock(&ps->out_lock);
		if (memcmp(&ps->endpoint, &pair->remote_candidate->endpoint, sizeof(ps->endpoint))) {
			ilog(LOG_INFO, "ICE negotiated: peer for component %u is %s", ps->component,
					endpoint_print_buf(&pair->remote_candidate->endpoint));
			ps->endpoint = pair->remote_candidate->endpoint;
		}
		mutex_unlock(&ps->out_lock);

		for (m = ps->sfds.head; m; m = m->next) {
			sfd = m->data;
			if (sfd->local_intf != pair->local_intf)
				continue;
			ps->selected_sfd = sfd;
			if (ps->component == 1)
				ilog(LOG_INFO, "ICE negotiated: local interface %s",
						sockaddr_print_buf(&pair->local_intf->spec->local_address.addr));
		}
	}

	call_media_unkernelize(media);

	g_queue_clear(&all_compos);
	return 1;
}
Example #6
0
/* agent must NOT be locked, but call must be locked in R */
static void __do_ice_check(struct ice_candidate_pair *pair) {
	struct stream_fd *sfd = pair->sfd;
	struct ice_agent *ag = pair->agent;
	u_int32_t prio, transact[3];

	if (PAIR_ISSET(pair, SUCCEEDED) && !PAIR_ISSET(pair, TO_USE))
		return;

	if (!ag->pwd[0].s)
		return;

	prio = ice_priority(ICT_PRFLX, pair->local_intf->unique_id,
			pair->remote_candidate->component_id);

	mutex_lock(&ag->lock);

	pair->retransmit = g_now;
	if (!PAIR_SET(pair, IN_PROGRESS)) {
		PAIR_CLEAR2(pair, FROZEN, FAILED);
		pair->retransmit_ms = STUN_RETRANSMIT_INTERVAL;
		pair->retransmits = 0;
	}
	else if (pair->retransmits > STUN_MAX_RETRANSMITS) {
		__fail_pair(pair);
		mutex_unlock(&ag->lock);
		return;
	}
	else {
		pair->retransmit_ms *= 2;
		pair->retransmits++;
	}
	timeval_add_usec(&pair->retransmit, pair->retransmit_ms * 1000);
	__agent_schedule_abs(pair->agent, &pair->retransmit);
	memcpy(transact, pair->stun_transaction, sizeof(transact));

	pair->was_controlling = AGENT_ISSET(ag, CONTROLLING);
	pair->was_nominated = PAIR_ISSET(pair, TO_USE);

	mutex_unlock(&ag->lock);

	ilog(LOG_DEBUG, "Sending %sICE/STUN request for candidate pair "PAIR_FORMAT" from %s to %s",
			PAIR_ISSET(pair, TO_USE) ? "nominating " : "",
			PAIR_FMT(pair), sockaddr_print_buf(&pair->local_intf->spec->local_address.addr),
			endpoint_print_buf(&pair->remote_candidate->endpoint));

	stun_binding_request(&pair->remote_candidate->endpoint, transact, &ag->pwd[0], ag->ufrag,
			AGENT_ISSET(ag, CONTROLLING), tie_breaker,
			prio, &sfd->socket,
			PAIR_ISSET(pair, TO_USE));

}
Example #7
0
static int __established(struct homer_sender *hs) {
	char buf[16];
	int ret;
	GString *gs;

	// test connection with a dummy read
	ret = read(hs->socket.fd, buf, sizeof(buf));
	if (ret < 0) {
		if (errno != EWOULDBLOCK && errno != EAGAIN) {
			ilog(LOG_ERR, "Connection error from Homer at %s: %s",
					endpoint_print_buf(&hs->endpoint), strerror(errno));
			__reset(hs);
			return -1;
		}
	}
	// XXX handle return data from Homer?

	if (hs->partial) {
		ilog(LOG_DEBUG, "dequeue partial packet to Homer");
		ret = __attempt_send(hs, hs->partial);
		if (ret == 3 || ret == 2) // partial write or not sent at all
			return 0;
		if (ret == 1) // write error, takes care of deleting hs->partial
			return -1;
		// ret == 0 -> sent OK, drop through to unqueue
		g_string_free(hs->partial, TRUE);
		hs->partial = NULL;
	}

	// unqueue as much as we can
	while ((gs = g_queue_pop_head(&hs->send_queue))) {
		ilog(LOG_DEBUG, "dequeue send queue to Homer");
		ret = __attempt_send(hs, gs);
		if (ret == 0) // everything sent OK
			continue;
		if (ret == 3) { // partial write
			hs->partial = gs;
			return 0;
		}
		g_queue_push_head(&hs->send_queue, gs);
		if (ret == 1) // write error
			return -1;
		// ret == 2 -> blocked
		return 0;
	}

	// everything unqueued
	return 0;
}
Example #8
0
int connect_to_graphite_server(const endpoint_t *ep) {
	graphite_ep = ep;
	int rc;

	ilog(LOG_INFO, "Connecting to graphite server %s", endpoint_print_buf(ep));

	rc = connect_socket_nb(&graphite_sock, SOCK_STREAM, ep);
	if (rc == -1) {
		ilog(LOG_ERROR,"Couldn't make socket for connecting to graphite.");
		return -1;
	}
	if (rc == 0)
		ilog(LOG_INFO, "Graphite server connected.");
	else {
		/* EINPROGRESS */
		ilog(LOG_INFO, "Connection to graphite is in progress.");
		connectinprogress = 1;
	}

	return 0;
}
Example #9
0
/* call is locked in R */
int ice_response(struct stream_fd *sfd, const endpoint_t *src,
		struct stun_attrs *attrs, u_int32_t transaction[3])
{
	struct ice_candidate_pair *pair, *opair;
	struct ice_agent *ag;
	struct packet_stream *ps = sfd->stream;
	struct call_media *media = ps->media;
	const char *err;
	unsigned int component;
	struct ice_candidate *cand;
	const struct local_intf *ifa;
	int ret, was_ctl;

	__DBG("received ICE response from %s on %s", endpoint_print_buf(src),
			endpoint_print_buf(&sfd->socket.local));

	ag = media->ice_agent;
	if (!ag)
		return -1;

	atomic64_set(&ag->last_activity, poller_now);

	mutex_lock(&ag->lock);

	pair = g_hash_table_lookup(ag->transaction_hash, transaction);
	err = "ICE/STUN response with unknown transaction received";
	if (!pair)
		goto err_unlock;
	was_ctl = pair->was_controlling;

	mutex_unlock(&ag->lock);

	ifa = pair->local_intf;

	ilog(LOG_DEBUG, "Received ICE/STUN response code %u for candidate pair "PAIR_FORMAT" from %s to %s",
			attrs->error_code, PAIR_FMT(pair),
			endpoint_print_buf(&pair->remote_candidate->endpoint),
			sockaddr_print_buf(&ifa->spec->local_address.addr));

	/* verify endpoints */
	err = "ICE/STUN response received, but source address didn't match remote candidate address";
	if (!endpoint_eq(src, &pair->remote_candidate->endpoint))
		goto err;

	err = "ICE/STUN response received, but destination address didn't match local interface address";
	if (pair->sfd != sfd)
		goto err;

	PAIR_CLEAR(pair, IN_PROGRESS);
	ret = 0;

	/* handle all errors */
	if (attrs->error_code) {
		err = "ICE/STUN error received";
		if (attrs->error_code != 487)
			goto err;
		__role_change(ag, !was_ctl);
		__trigger_check(pair);
		goto out;
	}

	/* we don't discover peer reflexive here (RFC 5245 7.1.3.2.1) as we don't expect to be behind NAT */
	/* we also skip parts of 7.1.3.2.2 as we don't do server reflexive */

	mutex_lock(&ag->lock);

	/* check if we're in the final (controlling) phase */
	if (pair->was_nominated && PAIR_CLEAR(pair, TO_USE)) {
		ilog(LOG_DEBUG, "Setting nominated ICE candidate pair "PAIR_FORMAT" as valid", PAIR_FMT(pair));
		PAIR_SET(pair, VALID);
		g_tree_insert(ag->valid_pairs, pair, pair);
		ret = __check_valid(ag);
		goto out_unlock;
	}

	if (PAIR_SET(pair, SUCCEEDED))
		goto out_unlock;

	ilog(LOG_DEBUG, "Setting ICE candidate pair "PAIR_FORMAT" as succeeded", PAIR_FMT(pair));
	g_tree_insert(ag->succeeded_pairs, pair, pair);

	if (!ag->start_nominating.tv_sec) {
		if (__check_succeeded_complete(ag)) {
			ag->start_nominating = g_now;
			timeval_add_usec(&ag->start_nominating, 100000);
			__agent_schedule_abs(ag, &ag->start_nominating);
		}
	}

	/* now unfreeze all other pairs from the same foundation */
	for (component = 1; component <= MAX_COMPONENTS; component++) {
		if (component == ps->component)
			continue;
		cand = __foundation_lookup(ag, &pair->remote_candidate->foundation, component);
		if (!cand)
			continue;
		opair = __pair_lookup(ag, cand, ifa);
		if (!opair)
			continue;

		if (PAIR_ISSET(opair, FAILED))
			continue;
		if (!PAIR_CLEAR(opair, FROZEN))
			continue;

		ilog(LOG_DEBUG, "Unfreezing related ICE pair "PAIR_FORMAT, PAIR_FMT(opair));
	}

	/* if this was previously nominated by the peer, it's now valid */
	if (PAIR_ISSET(pair, NOMINATED)) {
		PAIR_SET(pair, VALID);
		g_tree_insert(ag->valid_pairs, pair, pair);

		if (!AGENT_ISSET(ag, CONTROLLING))
			ret = __check_valid(ag);
	}

out_unlock:
	mutex_unlock(&ag->lock);
out:
	return ret;

err_unlock:
	mutex_unlock(&ag->lock);
err:
	if (err)
		ilog(LOG_NOTICE, "%s (from %s on interface %s)",
				err, endpoint_print_buf(src), endpoint_print_buf(&sfd->socket.local));

	if (pair && attrs->error_code)
		__fail_pair(pair);

	return 0;
}
Example #10
0
/* return values:
 * 1 = ICE completed, interfaces selected
 * 0 = packet processed
 * -1 = generic error, process packet as normal
 * -2 = role conflict
 */
int ice_request(struct stream_fd *sfd, const endpoint_t *src,
		struct stun_attrs *attrs)
{
	struct packet_stream *ps = sfd->stream;
	struct call_media *media = ps->media;
	struct ice_agent *ag;
	const char *err;
	struct ice_candidate *cand;
	struct ice_candidate_pair *pair;
	int ret;

	__DBG("received ICE request from %s on %s", endpoint_print_buf(src),
			endpoint_print_buf(&sfd->socket.local));

	ag = media->ice_agent;
	if (!ag)
		return -1;

	atomic64_set(&ag->last_activity, poller_now);

	/* determine candidate pair */
	mutex_lock(&ag->lock);

	cand = __cand_lookup(ag, src, ps->component);

	if (!cand)
		pair = __learned_candidate(ag, sfd, src, attrs->priority);
	else
		pair = __pair_lookup(ag, cand, sfd->local_intf);

	err = "Failed to determine ICE candidate from STUN request";
	if (!pair)
		goto err_unlock;

	mutex_unlock(&ag->lock);

	/* determine role conflict */
	if (attrs->controlling && AGENT_ISSET(ag, CONTROLLING)) {
		if (tie_breaker >= attrs->tiebreaker)
			return -2;
		else
			__role_change(ag, 0);
	}
	else if (attrs->controlled && !AGENT_ISSET(ag, CONTROLLING)) {
		if (tie_breaker >= attrs->tiebreaker)
			__role_change(ag, 1);
		else
			return -2;
	}


	if (PAIR_ISSET(pair, SUCCEEDED))
		;
	else
		__trigger_check(pair);

	ret = 0;

	if (attrs->use && !PAIR_SET(pair, NOMINATED)) {
		ilog(LOG_DEBUG, "ICE pair "PAIR_FORMAT" has been nominated by peer", PAIR_FMT(pair));

		mutex_lock(&ag->lock);

		g_tree_insert(ag->nominated_pairs, pair, pair);

		if (PAIR_ISSET(pair, SUCCEEDED)) {
			PAIR_SET(pair, VALID);
			g_tree_insert(ag->valid_pairs, pair, pair);
		}

		if (!AGENT_ISSET(ag, CONTROLLING))
			ret = __check_valid(ag);

		mutex_unlock(&ag->lock);
	}

	return ret;

err_unlock:
	mutex_unlock(&ag->lock);
	ilog(LOG_NOTICE, "%s (from %s on interface %s)", err, endpoint_print_buf(src),
			endpoint_print_buf(&sfd->socket.local));
	return 0;
}
Example #11
0
static void create_everything(void) {
	struct control_tcp *ct;
	struct control_udp *cu;
	struct cli *cl;
	struct timeval tmp_tv;
	struct timeval redis_start, redis_stop;
	double redis_diff = 0;

	if (rtpe_config.kernel_table < 0)
		goto no_kernel;
	if (kernel_setup_table(rtpe_config.kernel_table)) {
		if (rtpe_config.no_fallback) {
			ilog(LOG_CRIT, "Userspace fallback disallowed - exiting");
			exit(-1);
		}
		goto no_kernel;
	}

no_kernel:
	rtpe_poller = poller_new();
	if (!rtpe_poller)
		die("poller creation failed");

	dtls_timer(rtpe_poller);

	if (call_init())
		abort();

        rwlock_init(&rtpe_config.config_lock);
	if (rtpe_config.max_sessions < -1) {
		rtpe_config.max_sessions = -1;
	}

	if (rtpe_config.redis_num_threads < 1) {
#ifdef _SC_NPROCESSORS_ONLN
		rtpe_config.redis_num_threads = sysconf( _SC_NPROCESSORS_ONLN );
#endif
		if (rtpe_config.redis_num_threads < 1) {
			rtpe_config.redis_num_threads = REDIS_RESTORE_NUM_THREADS;
		}
	}

	ct = NULL;
	if (rtpe_config.tcp_listen_ep.port) {
		ct = control_tcp_new(rtpe_poller, &rtpe_config.tcp_listen_ep);
		if (!ct)
			die("Failed to open TCP control connection port");
	}

	cu = NULL;
	if (rtpe_config.udp_listen_ep.port) {
		interfaces_exclude_port(rtpe_config.udp_listen_ep.port);
		cu = control_udp_new(rtpe_poller, &rtpe_config.udp_listen_ep);
		if (!cu)
			die("Failed to open UDP control connection port");
	}

	rtpe_control_ng = NULL;
	if (rtpe_config.ng_listen_ep.port) {
		interfaces_exclude_port(rtpe_config.ng_listen_ep.port);
		rtpe_control_ng = control_ng_new(rtpe_poller, &rtpe_config.ng_listen_ep, rtpe_config.control_tos);
		if (!rtpe_control_ng)
			die("Failed to open UDP control connection port");
	}

	cl = NULL;
	if (rtpe_config.cli_listen_ep.port) {
		interfaces_exclude_port(rtpe_config.cli_listen_ep.port);
	    cl = cli_new(rtpe_poller, &rtpe_config.cli_listen_ep);
	    if (!cl)
	        die("Failed to open UDP CLI connection port");
	}

	if (!is_addr_unspecified(&rtpe_config.redis_write_ep.address)) {
		rtpe_redis_write = redis_new(&rtpe_config.redis_write_ep,
				rtpe_config.redis_write_db, rtpe_config.redis_write_auth,
				ANY_REDIS_ROLE, rtpe_config.no_redis_required);
		if (!rtpe_redis_write)
			die("Cannot start up without running Redis %s write database! See also NO_REDIS_REQUIRED parameter.",
				endpoint_print_buf(&rtpe_config.redis_write_ep));
	}

		if (!is_addr_unspecified(&rtpe_config.redis_ep.address)) {
			rtpe_redis = redis_new(&rtpe_config.redis_ep, rtpe_config.redis_db, rtpe_config.redis_auth, rtpe_redis_write ? ANY_REDIS_ROLE : MASTER_REDIS_ROLE, rtpe_config.no_redis_required);
			rtpe_redis_notify = redis_new(&rtpe_config.redis_ep, rtpe_config.redis_db, rtpe_config.redis_auth, rtpe_redis_write ? ANY_REDIS_ROLE : MASTER_REDIS_ROLE, rtpe_config.no_redis_required);
			if (!rtpe_redis || !rtpe_redis_notify)
			die("Cannot start up without running Redis %s database! See also NO_REDIS_REQUIRED parameter.",
				endpoint_print_buf(&rtpe_config.redis_ep));

		if (!rtpe_redis_write)
			rtpe_redis_write = rtpe_redis;
	}

	daemonize();
	wpidfile();

	homer_sender_init(&rtpe_config.homer_ep, rtpe_config.homer_protocol, rtpe_config.homer_id);

	rtcp_init(); // must come after Homer init

	if (rtpe_redis) {
		// start redis restore timer
		gettimeofday(&redis_start, NULL);

		// restore
		if (redis_restore(rtpe_redis))
			die("Refusing to continue without working Redis database");

		// stop redis restore timer
		gettimeofday(&redis_stop, NULL);

		// print redis restore duration
		redis_diff += timeval_diff(&redis_stop, &redis_start) / 1000.0;
		ilog(LOG_INFO, "Redis restore time = %.0lf ms", redis_diff);
	}

	gettimeofday(&rtpe_latest_graphite_interval_start, NULL);

	timeval_from_us(&tmp_tv, (long long) rtpe_config.graphite_interval*1000000);
	set_graphite_interval_tv(&tmp_tv);
}
Example #12
0
static void create_everything(struct main_context *ctx) {
	struct callmaster_config mc;
	struct control_tcp *ct;
	struct control_udp *cu;
	struct control_ng *cn;
	struct cli *cl;
	int kfd = -1;
	struct timeval tmp_tv;
	struct timeval redis_start, redis_stop;
	double redis_diff = 0;

	if (table < 0)
		goto no_kernel;
	if (kernel_create_table(table)) {
		fprintf(stderr, "FAILED TO CREATE KERNEL TABLE %i, KERNEL FORWARDING DISABLED\n", table);
		ilog(LOG_CRIT, "FAILED TO CREATE KERNEL TABLE %i, KERNEL FORWARDING DISABLED\n", table);
		if (no_fallback)
			exit(-1);
		goto no_kernel;
	}
	kfd = kernel_open_table(table);
	if (kfd == -1) {
		fprintf(stderr, "FAILED TO OPEN KERNEL TABLE %i, KERNEL FORWARDING DISABLED\n", table);
		ilog(LOG_CRIT, "FAILED TO OPEN KERNEL TABLE %i, KERNEL FORWARDING DISABLED\n", table);
		if (no_fallback)
			exit(-1);
		goto no_kernel;
	}

no_kernel:
	ctx->p = poller_new();
	if (!ctx->p)
		die("poller creation failed");

	ctx->m = callmaster_new(ctx->p);
	if (!ctx->m)
		die("callmaster creation failed");

	dtls_timer(ctx->p);

	ZERO(mc);
        rwlock_init(&mc.config_lock);
	mc.kernelfd = kfd;
	mc.kernelid = table;
	if (max_sessions < -1) {
		max_sessions = -1;
	}
	mc.max_sessions = max_sessions;
	mc.timeout = timeout;
	mc.silent_timeout = silent_timeout;
	mc.final_timeout = final_timeout;
	mc.delete_delay = delete_delay;
	mc.default_tos = tos;
	mc.b2b_url = b2b_url;
	mc.fmt = xmlrpc_fmt;
	mc.graphite_ep = graphite_ep;
	mc.graphite_interval = graphite_interval;
	mc.redis_subscribed_keyspaces = g_queue_copy(&keyspaces);

	if (redis_num_threads < 1) {
#ifdef _SC_NPROCESSORS_ONLN
		redis_num_threads = sysconf( _SC_NPROCESSORS_ONLN );
#endif
		if (redis_num_threads < 1) {
			redis_num_threads = REDIS_RESTORE_NUM_THREADS;
		}
	}
	mc.redis_num_threads = redis_num_threads;

	ct = NULL;
	if (tcp_listen_ep.port) {
		ct = control_tcp_new(ctx->p, &tcp_listen_ep, ctx->m);
		if (!ct)
			die("Failed to open TCP control connection port");
	}

	cu = NULL;
	if (udp_listen_ep.port) {
		interfaces_exclude_port(udp_listen_ep.port);
		cu = control_udp_new(ctx->p, &udp_listen_ep, ctx->m);
		if (!cu)
			die("Failed to open UDP control connection port");
	}

	cn = NULL;
	if (ng_listen_ep.port) {
		interfaces_exclude_port(ng_listen_ep.port);
		cn = control_ng_new(ctx->p, &ng_listen_ep, ctx->m);
		if (!cn)
			die("Failed to open UDP control connection port");
	}

	cl = NULL;
	if (cli_listen_ep.port) {
		interfaces_exclude_port(cli_listen_ep.port);
	    cl = cli_new(ctx->p, &cli_listen_ep, ctx->m);
	    if (!cl)
	        die("Failed to open UDP CLI connection port");
	}

	if (!is_addr_unspecified(&redis_write_ep.address)) {
		mc.redis_write = redis_new(&redis_write_ep, redis_write_db, redis_write_auth, ANY_REDIS_ROLE, no_redis_required);
		if (!mc.redis_write)
			die("Cannot start up without running Redis %s write database! See also NO_REDIS_REQUIRED paramter.",
				endpoint_print_buf(&redis_write_ep));
	}

	if (!is_addr_unspecified(&redis_ep.address)) {
		mc.redis = redis_new(&redis_ep, redis_db, redis_auth, mc.redis_write ? ANY_REDIS_ROLE : MASTER_REDIS_ROLE, no_redis_required);
		mc.redis_notify = redis_new(&redis_ep, redis_db, redis_auth, mc.redis_write ? ANY_REDIS_ROLE : MASTER_REDIS_ROLE, no_redis_required);
		if (!mc.redis || !mc.redis_notify)
			die("Cannot start up without running Redis %s database! See also NO_REDIS_REQUIRED paramter.",
				endpoint_print_buf(&redis_ep));

		if (!mc.redis_write)
			mc.redis_write = mc.redis;
	}

	mc.redis_expires_secs = redis_expires;

	ctx->m->conf = mc;

	if (!foreground)
		daemonize();
	wpidfile();

	ctx->m->homer = homer_sender_new(&homer_ep, homer_protocol, homer_id);

	if (mc.redis) {
		// start redis restore timer
		gettimeofday(&redis_start, NULL);

		// restore
		if (redis_restore(ctx->m, mc.redis))
			die("Refusing to continue without working Redis database");

		// stop redis restore timer
		gettimeofday(&redis_stop, NULL);

		// print redis restore duration
		redis_diff += timeval_diff(&redis_stop, &redis_start) / 1000.0;
		ilog(LOG_INFO, "Redis restore time = %.0lf ms", redis_diff);
	}

	gettimeofday(&ctx->m->latest_graphite_interval_start, NULL);

	timeval_from_us(&tmp_tv, graphite_interval*1000000);
	set_graphite_interval_tv(&tmp_tv);
}