Exemplo n.º 1
0
/*!
 * \brief Add threads to the threadpool
 *
 * This function is called from the threadpool's control taskprocessor thread.
 * \param pool The pool that is expanding
 * \delta The number of threads to add to the pool
 */
static void grow(struct ast_threadpool *pool, int delta)
{
	int i;

	int current_size = ao2_container_count(pool->active_threads) +
		ao2_container_count(pool->idle_threads);

	if (pool->options.max_size && current_size + delta > pool->options.max_size) {
		delta = pool->options.max_size - current_size;
	}

	ast_debug(3, "Increasing threadpool %s's size by %d\n",
			ast_taskprocessor_name(pool->tps), delta);

	for (i = 0; i < delta; ++i) {
		struct worker_thread *worker = worker_thread_alloc(pool);
		if (!worker) {
			return;
		}
		if (ao2_link(pool->idle_threads, worker)) {
			if (worker_thread_start(worker)) {
				ast_log(LOG_ERROR, "Unable to start worker thread %d. Destroying.\n", worker->id);
				ao2_unlink(pool->active_threads, worker);
			}
		} else {
			ast_log(LOG_WARNING, "Failed to activate worker thread %d. Destroying.\n", worker->id);
		}
		ao2_ref(worker, -1);
	}
}
Exemplo n.º 2
0
/*!
 * \brief Change the size of the threadpool
 *
 * This can either result in shrinking or growing the threadpool depending
 * on the new desired size and the current size.
 *
 * This function is run from the threadpool control taskprocessor thread
 *
 * \param data A set_size_data used for determining how to act
 * \return 0
 */
static int queued_set_size(void *data)
{
	RAII_VAR(struct set_size_data *, ssd, data, ao2_cleanup);
	struct ast_threadpool *pool = ssd->pool;
	unsigned int num_threads = ssd->size;

	/* We don't count zombie threads as being "live" when potentially resizing */
	unsigned int current_size = ao2_container_count(pool->active_threads) +
			ao2_container_count(pool->idle_threads);

	if (current_size == num_threads) {
		ast_debug(3, "Not changing threadpool size since new size %u is the same as current %u\n",
			  num_threads, current_size);
		return 0;
	}

	if (current_size < num_threads) {
		ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE,
				activate_thread, pool);

		/* As the above may have altered the number of current threads update it */
		current_size = ao2_container_count(pool->active_threads) +
				ao2_container_count(pool->idle_threads);
		grow(pool, num_threads - current_size);
		ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE,
				activate_thread, pool);
	} else {
		shrink(pool, current_size - num_threads);
	}

	threadpool_send_state_changed(pool);
	return 0;
}
Exemplo n.º 3
0
static int format_ami_aor_handler(void *obj, void *arg, int flags)
{
	struct ast_sip_aor *aor = obj;
	struct ast_sip_ami *ami = arg;
	const struct ast_sip_endpoint *endpoint = ami->arg;
	RAII_VAR(struct ast_str *, buf,
		 ast_sip_create_ami_event("AorDetail", ami), ast_free);

	int total_contacts;
	int num_permanent;
	RAII_VAR(struct ao2_container *, contacts,
		 ast_sip_location_retrieve_aor_contacts(aor), ao2_cleanup);

	if (!buf) {
		return -1;
	}

	sip_aor_to_ami(aor, &buf);
	total_contacts = ao2_container_count(contacts);
	num_permanent = aor->permanent_contacts ?
		ao2_container_count(aor->permanent_contacts) : 0;

	ast_str_append(&buf, 0, "TotalContacts: %d\r\n", total_contacts);
	ast_str_append(&buf, 0, "ContactsRegistered: %d\r\n",
		       total_contacts - num_permanent);
	ast_str_append(&buf, 0, "EndpointName: %s\r\n",
		       ast_sorcery_object_get_id(endpoint));

	astman_append(ami->s, "%s\r\n", ast_str_buffer(buf));
	ami->count++;

	return 0;
}
Exemplo n.º 4
0
/*!
 * \brief Notify the threadpool listener that the state has changed.
 *
 * This notifies the threadpool listener via its state_changed callback.
 * \param pool The threadpool whose state has changed
 */
static void threadpool_send_state_changed(struct ast_threadpool *pool)
{
	int active_size = ao2_container_count(pool->active_threads);
	int idle_size = ao2_container_count(pool->idle_threads);

	if (pool->listener && pool->listener->callbacks->state_changed) {
		pool->listener->callbacks->state_changed(pool, pool->listener, active_size, idle_size);
	}
}
Exemplo n.º 5
0
static int pthread_timer_open(void)
{
	struct pthread_timer *timer;
	int fd;

	if (!(timer = ao2_alloc(sizeof(*timer), pthread_timer_destructor))) {
		errno = ENOMEM;
		return -1;
	}

	timer->pipe[PIPE_READ] = timer->pipe[PIPE_WRITE] = -1;
	timer->state = TIMER_STATE_IDLE;

	if (pipe(timer->pipe)) {
		ao2_ref(timer, -1);
		return -1;
	}

	ao2_lock(pthread_timers);
	if (!ao2_container_count(pthread_timers)) {
		ast_mutex_lock(&timing_thread.lock);
		ast_cond_signal(&timing_thread.cond);
		ast_mutex_unlock(&timing_thread.lock);
	}
	ao2_link(pthread_timers, timer);
	ao2_unlock(pthread_timers);

	fd = timer->pipe[PIPE_READ];

	ao2_ref(timer, -1);

	return fd;
}
Exemplo n.º 6
0
int ast_format_cap_is_empty(const struct ast_format_cap *cap)
{
	if (!cap) {
		return 1;
	}
	return ao2_container_count(cap->formats) == 0 ? 1 : 0;
}
Exemplo n.º 7
0
static int mwi_subscription_established(struct ast_sip_subscription *sip_sub)
{
	const char *resource = ast_sip_subscription_get_resource_name(sip_sub);
	struct mwi_subscription *sub;
	struct ast_sip_endpoint *endpoint = ast_sip_subscription_get_endpoint(sip_sub);

	/* no aor in uri? subscribe to all on endpoint */
	if (ast_strlen_zero(resource)) {
		sub = mwi_subscribe_all(endpoint, sip_sub);
	} else {
		sub = mwi_subscribe_single(endpoint, sip_sub, resource);
	}
	if (!sub) {
		ao2_cleanup(endpoint);
		return -1;
	}

	if (!ao2_container_count(sub->stasis_subs)) {
		/*
		 * We setup no MWI subscriptions so remove the MWI datastore
		 * to break the ref loop.
		 */
		ast_sip_subscription_remove_datastore(sip_sub, MWI_DATASTORE);
	}

	ao2_cleanup(sub);
	ao2_cleanup(endpoint);
	return 0;
}
Exemplo n.º 8
0
static void *pthread_timer_open(void)
{
	struct pthread_timer *timer;

	if (!(timer = ao2_alloc(sizeof(*timer), pthread_timer_destructor))) {
		errno = ENOMEM;
		return NULL;
	}

	timer->pipe[PIPE_READ] = timer->pipe[PIPE_WRITE] = -1;
	timer->state = TIMER_STATE_IDLE;

	if (ast_pipe_nonblock(timer->pipe)) {
		ao2_ref(timer, -1);
		return NULL;
	}

	ao2_lock(pthread_timers);
	if (!ao2_container_count(pthread_timers)) {
		ast_mutex_lock(&timing_thread.lock);
		ast_cond_signal(&timing_thread.cond);
		ast_mutex_unlock(&timing_thread.lock);
	}
	ao2_link_flags(pthread_timers, timer, OBJ_NOLOCK);
	ao2_unlock(pthread_timers);

	return timer;
}
Exemplo n.º 9
0
void stasis_app_set_global_debug(int debug)
{
	global_debug = debug;
	if (!global_debug) {
		struct ao2_container *app_names = stasis_app_get_all();
		struct ao2_iterator it_app_names;
		char *app_name;
		struct stasis_app *app;

		if (!app_names || !ao2_container_count(app_names)) {
			ao2_cleanup(app_names);
			return;
		}

		it_app_names = ao2_iterator_init(app_names, 0);
		while ((app_name = ao2_iterator_next(&it_app_names))) {
			if ((app = stasis_app_get_by_name(app_name))) {
				stasis_app_set_debug(app, 0);
			}

			ao2_cleanup(app_name);
			ao2_cleanup(app);
		}
		ao2_iterator_cleanup(&it_app_names);
		ao2_cleanup(app_names);
	}
}
Exemplo n.º 10
0
static void *pthread_timer_open(void)
{
	struct pthread_timer *timer;
	int i;

	if (!(timer = ao2_alloc(sizeof(*timer), pthread_timer_destructor))) {
		errno = ENOMEM;
		return NULL;
	}

	timer->pipe[PIPE_READ] = timer->pipe[PIPE_WRITE] = -1;
	timer->state = TIMER_STATE_IDLE;

	if (pipe(timer->pipe)) {
		ao2_ref(timer, -1);
		return NULL;
	}

	for (i = 0; i < ARRAY_LEN(timer->pipe); ++i) {
		int flags = fcntl(timer->pipe[i], F_GETFL);
		flags |= O_NONBLOCK;
		fcntl(timer->pipe[i], F_SETFL, flags);
	}
	
	ao2_lock(pthread_timers);
	if (!ao2_container_count(pthread_timers)) {
		ast_mutex_lock(&timing_thread.lock);
		ast_cond_signal(&timing_thread.cond);
		ast_mutex_unlock(&timing_thread.lock);
	}
	ao2_link(pthread_timers, timer);
	ao2_unlock(pthread_timers);

	return timer;
}
Exemplo n.º 11
0
static void *do_timing(void *arg)
{
	struct timeval next_wakeup = ast_tvnow();

	while (!timing_thread.stop) {
		struct timespec ts = { 0, };

		ao2_callback(pthread_timers, OBJ_NODATA, run_timer, NULL);

		next_wakeup = ast_tvadd(next_wakeup, ast_tv(0, 5000));

		ts.tv_sec = next_wakeup.tv_sec;
		ts.tv_nsec = next_wakeup.tv_usec * 1000;

		ast_mutex_lock(&timing_thread.lock);
		if (!timing_thread.stop) {
			if (ao2_container_count(pthread_timers)) {
				ast_cond_timedwait(&timing_thread.cond, &timing_thread.lock, &ts);
			} else {
				ast_cond_wait(&timing_thread.cond, &timing_thread.lock);
			}
		}
		ast_mutex_unlock(&timing_thread.lock);
	}

	return NULL;
}
Exemplo n.º 12
0
/*! \brief Load (or reload) configuration. */
static int process_config(int reload)
{
	RAII_VAR(struct ast_ari_conf *, conf, NULL, ao2_cleanup);

	switch (aco_process_config(&cfg_info, reload)) {
	case ACO_PROCESS_ERROR:
		return -1;
	case ACO_PROCESS_OK:
	case ACO_PROCESS_UNCHANGED:
		break;
	}

	conf = ast_ari_config_get();
	if (!conf) {
		ast_assert(0); /* We just configured; it should be there */
		return -1;
	}

	if (conf->general->enabled) {
		if (ao2_container_count(conf->users) == 0) {
			ast_log(LOG_ERROR, "No configured users for ARI\n");
		} else {
			ao2_callback(conf->users, OBJ_NODATA, validate_user_cb, NULL);
		}
	}

	return 0;
}
Exemplo n.º 13
0
void ast_ari_websocket_events_event_websocket_established(struct ast_ari_websocket_session *ws_session,
	struct ast_variable *headers,
	struct ast_ari_events_event_websocket_args *args)
{
	RAII_VAR(struct event_session *, session, NULL, session_cleanup);
	struct ast_json *msg;
	int res;
	size_t i;
	int (* register_handler)(const char *, stasis_app_cb handler, void *data);

	ast_debug(3, "/events WebSocket connection\n");

	session = session_create(ws_session);
	if (!session) {
		ast_ari_websocket_session_write(ws_session, ast_ari_oom_json());
		return;
	}

	if (args->subscribe_all) {
		register_handler = &stasis_app_register_all;
	} else {
		register_handler = &stasis_app_register;
	}

	res = 0;
	for (i = 0; i < args->app_count; ++i) {
		if (ast_strlen_zero(args->app[i])) {
			continue;
		}
		res |= session_register_app(session, args->app[i], register_handler);
	}

	if (ao2_container_count(session->websocket_apps) == 0) {
		RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);

		msg = ast_json_pack("{s: s, s: [s]}",
			"type", "MissingParams",
			"params", "app");
		if (!msg) {
			msg = ast_json_ref(ast_ari_oom_json());
		}

		ast_ari_websocket_session_write(session->ws_session, msg);
		return;
	}

	if (res != 0) {
		ast_ari_websocket_session_write(ws_session, ast_ari_oom_json());
		return;
	}

	/* We don't process any input, but we'll consume it waiting for EOF */
	while ((msg = ast_ari_websocket_session_read(ws_session))) {
		ast_json_unref(msg);
	}
}
Exemplo n.º 14
0
int app_is_finished(struct stasis_app *app)
{
	int ret;

	ao2_lock(app);
	ret = app->handler == NULL && ao2_container_count(app->forwards) == 0;
	ao2_unlock(app);

	return ret;
}
Exemplo n.º 15
0
void stasis_app_to_cli(const struct stasis_app *app, struct ast_cli_args *a)
{
	struct ao2_iterator *channels;
	struct ao2_iterator *endpoints;
	struct ao2_iterator *bridges;
	struct app_forwards *forward;
	enum forward_type forward_type;

	ast_cli(a->fd, "Name: %s\n"
		"  Debug: %s\n"
		"  Subscription Model: %s\n",
		app->name,
		app->debug ? "Yes" : "No",
		app->subscription_model == STASIS_APP_SUBSCRIBE_ALL ?
			"Global Resource Subscription" :
			"Application/Explicit Resource Subscription");
	ast_cli(a->fd, "  Subscriptions: %d\n", ao2_container_count(app->forwards));

	ast_cli(a->fd, "    Channels:\n");
	forward_type = FORWARD_CHANNEL;
	channels = ao2_callback(app->forwards, OBJ_MULTIPLE,
		forwards_filter_by_type, &forward_type);
	if (channels) {
		while ((forward = ao2_iterator_next(channels))) {
			ast_cli(a->fd, "      %s (%d)\n", forward->id, forward->interested);
			ao2_ref(forward, -1);
		}
		ao2_iterator_destroy(channels);
	}

	ast_cli(a->fd, "    Bridges:\n");
	forward_type = FORWARD_BRIDGE;
	bridges = ao2_callback(app->forwards, OBJ_MULTIPLE,
		forwards_filter_by_type, &forward_type);
	if (bridges) {
		while ((forward = ao2_iterator_next(bridges))) {
			ast_cli(a->fd, "      %s (%d)\n", forward->id, forward->interested);
			ao2_ref(forward, -1);
		}
		ao2_iterator_destroy(bridges);
	}

	ast_cli(a->fd, "    Endpoints:\n");
	forward_type = FORWARD_ENDPOINT;
	endpoints = ao2_callback(app->forwards, OBJ_MULTIPLE,
		forwards_filter_by_type, &forward_type);
	if (endpoints) {
		while ((forward = ao2_iterator_next(endpoints))) {
			ast_cli(a->fd, "      %s (%d)\n", forward->id, forward->interested);
			ao2_ref(forward, -1);
		}
		ao2_iterator_destroy(endpoints);
	}
}
Exemplo n.º 16
0
static int ami_show_auths(struct mansession *s, const struct message *m)
{
	struct ast_sip_ami ami = { .s = s, .m = m, .action_id = astman_get_header(m, "ActionID"), };
	struct ao2_container *auths;

	auths = cli_get_auths();
	if (!auths) {
		astman_send_error(s, m, "Could not get Auths\n");
		return 0;
	}

	if (!ao2_container_count(auths)) {
		astman_send_error(s, m, "No Auths found\n");
		ao2_ref(auths, -1);
		return 0;
	}

	astman_send_listack(s, m, "A listing of Auths follows, presented as AuthList events",
			"start");

	ao2_callback(auths, OBJ_NODATA, format_ami_authlist_handler, &ami);

	astman_send_list_complete_start(s, m, "AuthListComplete", ami.count);
	astman_send_list_complete_end(s);

	ao2_ref(auths, -1);

	return 0;
}

static struct ao2_container *cli_get_container(const char *regex)
{
	RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup);
	struct ao2_container *s_container;

	container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "auth", regex);
	if (!container) {
		return NULL;
	}

	s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0,
		ast_sorcery_object_id_sort, ast_sorcery_object_id_compare);
	if (!s_container) {
		return NULL;
	}

	if (ao2_container_dup(s_container, container, 0)) {
		ao2_ref(s_container, -1);
		return NULL;
	}

	return s_container;
}
Exemplo n.º 17
0
struct ast_sip_contact *ast_sip_location_retrieve_first_aor_contact(const struct ast_sip_aor *aor)
{
	struct ao2_container *contacts;
	struct ast_sip_contact *contact = NULL;

	contacts = ast_sip_location_retrieve_aor_contacts(aor);
	if (contacts && ao2_container_count(contacts)) {
		/* Get the first AOR contact in the container. */
		contact = ao2_callback(contacts, 0, NULL, NULL);
	}
	ao2_cleanup(contacts);
	return contact;
}
Exemplo n.º 18
0
struct ast_sip_contact *ast_sip_location_retrieve_first_aor_contact(const struct ast_sip_aor *aor)
{
	RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
	struct ast_sip_contact *contact;

	contacts = ast_sip_location_retrieve_aor_contacts(aor);
	if (!contacts || (ao2_container_count(contacts) == 0)) {
		return NULL;
	}

	contact = ao2_callback(contacts, 0, contact_find_first, NULL);
	return contact;
}
Exemplo n.º 19
0
/*!
 * \brief Queued task called when tasks are pushed into the threadpool
 *
 * This function first calls into the threadpool's listener to let it know
 * that a task has been pushed. It then wakes up all idle threads and moves
 * them into the active thread container.
 * \param data A task_pushed_data
 * \return 0
 */
static int queued_task_pushed(void *data)
{
	struct task_pushed_data *tpd = data;
	struct ast_threadpool *pool = tpd->pool;
	int was_empty = tpd->was_empty;
	unsigned int existing_active;

	if (pool->listener && pool->listener->callbacks->task_pushed) {
		pool->listener->callbacks->task_pushed(pool, pool->listener, was_empty);
	}

	existing_active = ao2_container_count(pool->active_threads);

	/* The first pass transitions any existing idle threads to be active, and
	 * will also remove any worker threads that have recently entered the dead
	 * state.
	 */
	ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA,
			activate_thread, pool);

	/* If no idle threads could be transitioned to active grow the pool as permitted. */
	if (ao2_container_count(pool->active_threads) == existing_active) {
		if (!pool->options.auto_increment) {
			ao2_ref(tpd, -1);
			return 0;
		}
		grow(pool, pool->options.auto_increment);
		/* An optional second pass transitions any newly added threads. */
		ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA,
				activate_thread, pool);
	}

	threadpool_send_state_changed(pool);
	ao2_ref(tpd, -1);
	return 0;
}
Exemplo n.º 20
0
/*! \brief Function called to unload the module */
static int unload_module(void)
{
	ao2_callback(cli_aliases, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, alias_unregister_cb, NULL);

	if (ao2_container_count(cli_aliases)) {
		ast_log(LOG_ERROR, "Could not unregister all CLI aliases\n");
		return -1;
	}

	ao2_ref(cli_aliases, -1);

	ast_cli_unregister_multiple(cli_alias, ARRAY_LEN(cli_alias));

	return 0;
}
Exemplo n.º 21
0
static void mwi_subscription_mailboxes_str(struct ao2_container *stasis_subs,
					   struct ast_str **str)
{
	int num = ao2_container_count(stasis_subs);

	struct mwi_stasis_subscription *node;
	struct ao2_iterator i = ao2_iterator_init(stasis_subs, 0);

	while ((node = ao2_iterator_next(&i))) {
		if (--num) {
			ast_str_append(str, 0, "%s,", node->mailbox);
		} else {
			ast_str_append(str, 0, "%s", node->mailbox);
		}
		ao2_ref(node, -1);
	}
	ao2_iterator_destroy(&i);
}
Exemplo n.º 22
0
void control_wait(struct stasis_app_control *control)
{
    if (!control) {
        return;
    }

    ast_assert(control->command_queue != NULL);

    ao2_lock(control->command_queue);
    while (ao2_container_count(control->command_queue) == 0) {
        int res = ast_cond_wait(&control->wait_cond,
                                ao2_object_get_lockaddr(control->command_queue));
        if (res < 0) {
            ast_log(LOG_ERROR, "Error waiting on command queue\n");
            break;
        }
    }
    ao2_unlock(control->command_queue);
}
Exemplo n.º 23
0
/*! \brief CLI command "local show channels" */
static char *locals_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
	struct local_pvt *p;
	struct ao2_iterator it;

	switch (cmd) {
	case CLI_INIT:
		e->command = "local show channels";
		e->usage =
			"Usage: local show channels\n"
			"       Provides summary information on active local proxy channels.\n";
		return NULL;
	case CLI_GENERATE:
		return NULL;
	}

	if (a->argc != 3) {
		return CLI_SHOWUSAGE;
	}

	if (ao2_container_count(locals) == 0) {
		ast_cli(a->fd, "No local channels in use\n");
		return RESULT_SUCCESS;
	}

	it = ao2_iterator_init(locals, 0);
	while ((p = ao2_iterator_next(&it))) {
		ao2_lock(p);
		ast_cli(a->fd, "%s -- %s\n",
			p->base.owner ? ast_channel_name(p->base.owner) : "<unowned>",
			p->base.name);
		ao2_unlock(p);
		ao2_ref(p, -1);
	}
	ao2_iterator_destroy(&it);

	return CLI_SUCCESS;
}
Exemplo n.º 24
0
/*!
 * \brief Remove threads from the threadpool
 *
 * The preference is to kill idle threads. However, if there are
 * more threads to remove than there are idle threads, then active
 * threads will be zombified instead.
 *
 * This function is called from the threadpool control taskprocessor thread.
 *
 * \param pool The threadpool to remove threads from
 * \param delta The number of threads to remove
 */
static void shrink(struct ast_threadpool *pool, int delta)
{
	/*
	 * Preference is to kill idle threads, but
	 * we'll move on to deactivating active threads
	 * if we have to
	 */
	int idle_threads = ao2_container_count(pool->idle_threads);
	int idle_threads_to_kill = MIN(delta, idle_threads);
	int active_threads_to_zombify = delta - idle_threads_to_kill;

	ast_debug(3, "Destroying %d idle threads in threadpool %s\n", idle_threads_to_kill,
			ast_taskprocessor_name(pool->tps));

	ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE,
			kill_threads, &idle_threads_to_kill);

	ast_debug(3, "Destroying %d active threads in threadpool %s\n", active_threads_to_zombify,
			ast_taskprocessor_name(pool->tps));

	ao2_callback_data(pool->active_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE,
			zombify_threads, pool, &active_threads_to_zombify);
}
Exemplo n.º 25
0
/*!
 * \brief Queued task called when tasks are pushed into the threadpool
 *
 * This function first calls into the threadpool's listener to let it know
 * that a task has been pushed. It then wakes up all idle threads and moves
 * them into the active thread container.
 * \param data A task_pushed_data
 * \return 0
 */
static int queued_task_pushed(void *data)
{
	struct task_pushed_data *tpd = data;
	struct ast_threadpool *pool = tpd->pool;
	int was_empty = tpd->was_empty;

	if (pool->listener && pool->listener->callbacks->task_pushed) {
		pool->listener->callbacks->task_pushed(pool, pool->listener, was_empty);
	}
	if (ao2_container_count(pool->idle_threads) == 0) {
		if (!pool->options.auto_increment) {
			return 0;
		}
		grow(pool, pool->options.auto_increment);
	}

	ao2_callback(pool->idle_threads, OBJ_UNLINK | OBJ_NOLOCK | OBJ_NODATA,
			activate_thread, pool);

	threadpool_send_state_changed(pool);
	ao2_ref(tpd, -1);
	return 0;
}
Exemplo n.º 26
0
static void cli_display_parking_lot(int fd, const char *name)
{
	RAII_VAR(struct parking_lot *, lot, NULL, ao2_cleanup);
	lot = parking_lot_find_by_name(name);

	/* If the parking lot couldn't be found with the search, also abort. */
	if (!lot) {
		ast_cli(fd, "Could not find parking lot '%s'\n\n", name);
		return;
	}

	display_parking_lot(lot, fd);

	ast_cli(fd, "Parked Calls\n------------\n");

	if (!ao2_container_count(lot->parked_users)) {
		ast_cli(fd, "  (none)\n");
		ast_cli(fd, "\n\n");
		return;
	}

	ao2_callback(lot->parked_users, OBJ_MULTIPLE | OBJ_NODATA, display_parked_users_cb, &fd);
	ast_cli(fd, "\n");
}
Exemplo n.º 27
0
static void send_unsolicited_mwi_notify(struct mwi_subscription *sub,
		struct ast_sip_message_accumulator *counter)
{
	RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(),
				"endpoint", sub->id), ao2_cleanup);
	char *endpoint_aors;
	char *aor_name;
	struct ast_sip_body body;
	struct ast_str *body_text;
	struct ast_sip_body_data body_data = {
		.body_type = AST_SIP_MESSAGE_ACCUMULATOR,
		.body_data = counter,
	};

	if (!endpoint) {
		ast_log(LOG_WARNING, "Unable to send unsolicited MWI to %s because endpoint does not exist\n",
				sub->id);
		return;
	}
	if (ast_strlen_zero(endpoint->aors)) {
		ast_log(LOG_WARNING, "Unable to send unsolicited MWI to %s because the endpoint has no"
				" configured AORs\n", sub->id);
		return;
	}

	body.type = MWI_TYPE;
	body.subtype = MWI_SUBTYPE;

	body_text = ast_str_create(64);

	if (!body_text) {
		return;
	}

	if (ast_sip_pubsub_generate_body_content(body.type, body.subtype, &body_data, &body_text)) {
		ast_log(LOG_WARNING, "Unable to generate SIP MWI NOTIFY body.\n");
		ast_free(body_text);
		return;
	}

	body.body_text = ast_str_buffer(body_text);

	endpoint_aors = ast_strdupa(endpoint->aors);

	ast_debug(5, "Sending unsolicited MWI NOTIFY to endpoint %s, new messages: %d, old messages: %d\n",
			sub->id, counter->new_msgs, counter->old_msgs);

	while ((aor_name = strsep(&endpoint_aors, ","))) {
		RAII_VAR(struct ast_sip_aor *, aor, ast_sip_location_retrieve_aor(aor_name), ao2_cleanup);
		RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
		struct unsolicited_mwi_data mwi_data = {
			.sub = sub,
			.endpoint = endpoint,
			.body = &body,
		};

		if (!aor) {
			ast_log(LOG_WARNING, "Unable to locate AOR %s for unsolicited MWI\n", aor_name);
			continue;
		}

		contacts = ast_sip_location_retrieve_aor_contacts(aor);
		if (!contacts || (ao2_container_count(contacts) == 0)) {
			ast_log(LOG_NOTICE, "No contacts bound to AOR %s. Cannot send unsolicited MWI until a contact registers.\n", aor_name);
			continue;
		}

		ao2_callback(contacts, OBJ_NODATA, send_unsolicited_mwi_notify_to_contact, &mwi_data);
	}

	ast_free(body_text);
}
Exemplo n.º 28
0
/*! \brief Generate a Sound structure as documented in sounds.json for the specified filename */
static struct ast_json *create_sound_blob(const char *filename,
	struct ast_ari_sounds_list_args *args)
{
	RAII_VAR(struct ast_json *, sound, NULL, ast_json_unref);
	RAII_VAR(struct ao2_container *, languages, NULL, ao2_cleanup);
	const char *description;
	struct ast_json *format_lang_list;
	struct lang_format_info info;
	RAII_VAR(struct ast_media_index *, sounds_index, ast_sounds_get_index(), ao2_cleanup);

	if (!sounds_index) {
		return NULL;
	}

	description = ast_media_get_description(sounds_index, filename, "en");
	if (ast_strlen_zero(description)) {
		sound = ast_json_pack("{s: s, s: []}",
			"id", filename,
			"formats");
	} else {
		sound = ast_json_pack("{s: s, s: s, s: []}",
			"id", filename,
			"text", description,
			"formats");
	}
	if (!sound) {
		return NULL;
	}

	format_lang_list = ast_json_object_get(sound, "formats");
	if (!format_lang_list) {
		return NULL;
	}

	languages = ast_media_get_variants(sounds_index, filename);
	if (!languages || !ao2_container_count(languages)) {
		return NULL;
	}

	/* filter requested languages */
	if (args && !ast_strlen_zero(args->lang)) {
		char *lang_filter = ast_strdupa(args->lang);
		ao2_callback(languages, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, filter_langs_cb, lang_filter);
		if (!languages || !ao2_container_count(languages)) {
			return NULL;
		}
	}

	info.filename = filename;
	info.format_list = format_lang_list;
	info.format_filter = NULL;
	if (args) {
		info.format_filter = args->format;
	}
	ao2_callback(languages, OBJ_NODATA, add_format_information_cb, &info);

	/* no format/lang pairs for this sound so nothing to return */
	if (!ast_json_array_size(format_lang_list)) {
		return NULL;
	}

	return ast_json_ref(sound);
}
Exemplo n.º 29
0
static int register_aor_core(pjsip_rx_data *rdata,
	struct ast_sip_endpoint *endpoint,
	struct ast_sip_aor *aor,
	const char *aor_name,
	struct ao2_container *contacts)
{
	static const pj_str_t USER_AGENT = { "User-Agent", 10 };

	int added = 0, updated = 0, deleted = 0;
	pjsip_contact_hdr *contact_hdr = NULL;
	struct registrar_contact_details details = { 0, };
	pjsip_tx_data *tdata;
	RAII_VAR(struct ast_str *, path_str, NULL, ast_free);
	struct ast_sip_contact *response_contact;
	char *user_agent = NULL;
	pjsip_user_agent_hdr *user_agent_hdr;
	pjsip_expires_hdr *expires_hdr;
	pjsip_via_hdr *via_hdr;
	pjsip_via_hdr *via_hdr_last;
	char *via_addr = NULL;
	int via_port = 0;
	pjsip_cid_hdr *call_id_hdr;
	char *call_id = NULL;
	size_t alloc_size;

	/* So we don't count static contacts against max_contacts we prune them out from the container */
	ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_prune_static, NULL);

	if (registrar_validate_contacts(rdata, contacts, aor, &added, &updated, &deleted)) {
		/* The provided Contact headers do not conform to the specification */
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400, NULL, NULL, NULL);
		ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_contacts_provided");
		ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n",
				ast_sorcery_object_get_id(endpoint));
		return PJ_TRUE;
	}

	if (registrar_validate_path(rdata, aor, &path_str)) {
		/* Ensure that intervening proxies did not make invalid modifications to the request */
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 420, NULL, NULL, NULL);
		ast_log(LOG_WARNING, "Invalid modifications made to REGISTER request from '%s' by intervening proxy\n",
				ast_sorcery_object_get_id(endpoint));
		return PJ_TRUE;
	}

	if ((MAX(added - deleted, 0) + (!aor->remove_existing ? ao2_container_count(contacts) : 0)) > aor->max_contacts) {
		/* Enforce the maximum number of contacts */
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL);
		ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_exceeds_maximum_configured_contacts");
		ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %u\n",
				ast_sorcery_object_get_id(endpoint), aor_name, aor->max_contacts);
		return PJ_TRUE;
	}

	if (!(details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256))) {
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL);
		return PJ_TRUE;
	}

	user_agent_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &USER_AGENT, NULL);
	if (user_agent_hdr) {
		alloc_size = pj_strlen(&user_agent_hdr->hvalue) + 1;
		user_agent = ast_alloca(alloc_size);
		ast_copy_pj_str(user_agent, &user_agent_hdr->hvalue, alloc_size);
	}

	/* Find the first Via header */
	via_hdr = via_hdr_last = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_VIA, NULL);
	if (via_hdr) {
		/* Find the last Via header */
		while ( (via_hdr = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg,
				PJSIP_H_VIA, via_hdr->next)) != NULL) {
			via_hdr_last = via_hdr;
		}
		alloc_size = pj_strlen(&via_hdr_last->sent_by.host) + 1;
		via_addr = ast_alloca(alloc_size);
		ast_copy_pj_str(via_addr, &via_hdr_last->sent_by.host, alloc_size);
		via_port=via_hdr_last->sent_by.port;
	}

	call_id_hdr = (pjsip_cid_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CALL_ID, NULL);
	if (call_id_hdr) {
		alloc_size = pj_strlen(&call_id_hdr->id) + 1;
		call_id = ast_alloca(alloc_size);
		ast_copy_pj_str(call_id, &call_id_hdr->id, alloc_size);
	}

	/* Iterate each provided Contact header and add, update, or delete */
	while ((contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr ? contact_hdr->next : NULL))) {
		int expiration;
		char contact_uri[pjsip_max_url_size];
		RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);

		if (contact_hdr->star) {
			/* A star means to unregister everything, so do so for the possible contacts */
			ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, (void *)aor_name);
			break;
		}

		if (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri)) {
			/* This registrar only currently supports sip: and sips: URI schemes */
			continue;
		}

		expiration = registrar_get_expiration(aor, contact_hdr, rdata);
		details.uri = pjsip_uri_get_uri(contact_hdr->uri);
		pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri));

		if (!(contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details))) {
			/* If they are actually trying to delete a contact that does not exist... be forgiving */
			if (!expiration) {
				ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n",
					contact_uri, aor_name);
				continue;
			}

			if (ast_sip_location_add_contact_nolock(aor, contact_uri, ast_tvadd(ast_tvnow(),
				ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL,
					user_agent, via_addr, via_port, call_id, endpoint)) {
				ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n",
						contact_uri, aor_name);
				continue;
			}

			ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n",
				contact_uri, aor_name, expiration);
			ast_test_suite_event_notify("AOR_CONTACT_ADDED",
					"Contact: %s\r\n"
					"AOR: %s\r\n"
					"Expiration: %d\r\n"
					"UserAgent: %s",
					contact_uri,
					aor_name,
					expiration,
					user_agent);
		} else if (expiration) {
			struct ast_sip_contact *contact_update;

			contact_update = ast_sorcery_copy(ast_sip_get_sorcery(), contact);
			if (!contact_update) {
				ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n",
					contact->uri, expiration);
				continue;
			}

			contact_update->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1));
			contact_update->qualify_frequency = aor->qualify_frequency;
			contact_update->authenticate_qualify = aor->authenticate_qualify;
			if (path_str) {
				ast_string_field_set(contact_update, path, ast_str_buffer(path_str));
			}
			if (user_agent) {
				ast_string_field_set(contact_update, user_agent, user_agent);
			}
			if (!ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
				ast_string_field_set(contact_update, reg_server, ast_config_AST_SYSTEM_NAME);
			}

			if (ast_sip_location_update_contact(contact_update)) {
				ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n",
					contact->uri, expiration);
				ast_sip_location_delete_contact(contact);
				continue;
			}
			ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n",
				contact_uri, aor_name, expiration);
			ast_test_suite_event_notify("AOR_CONTACT_REFRESHED",
					"Contact: %s\r\n"
					"AOR: %s\r\n"
					"Expiration: %d\r\n"
					"UserAgent: %s",
					contact_uri,
					aor_name,
					expiration,
					contact_update->user_agent);
			ao2_cleanup(contact_update);
		} else {
			/* We want to report the user agent that was actually in the removed contact */
			ast_sip_location_delete_contact(contact);
			ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact_uri, aor_name);
			ast_test_suite_event_notify("AOR_CONTACT_REMOVED",
					"Contact: %s\r\n"
					"AOR: %s\r\n"
					"UserAgent: %s",
					contact_uri,
					aor_name,
					contact->user_agent);
		}
	}

	pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);

	/* If the AOR is configured to remove any existing contacts that have not been updated/added as a result of this REGISTER
	 * do so
	 */
	if (aor->remove_existing) {
		ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL);
	}

	/* Re-retrieve contacts.  Caller will clean up the original container. */
	contacts = ast_sip_location_retrieve_aor_contacts_nolock(aor);
	response_contact = ao2_callback(contacts, 0, NULL, NULL);

	/* Send a response containing all of the contacts (including static) that are present on this AOR */
	if (ast_sip_create_response(rdata, 200, response_contact, &tdata) != PJ_SUCCESS) {
		ao2_cleanup(response_contact);
		ao2_cleanup(contacts);
		return PJ_TRUE;
	}
	ao2_cleanup(response_contact);

	/* Add the date header to the response, some UAs use this to set their date and time */
	registrar_add_date_header(tdata);

	ao2_callback(contacts, 0, registrar_add_contact, tdata);
	ao2_cleanup(contacts);

	if ((expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) {
		expires_hdr = pjsip_expires_hdr_create(tdata->pool, registrar_get_expiration(aor, NULL, rdata));
		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr);
	}

	ast_sip_send_stateful_response(rdata, tdata, endpoint);

	return PJ_TRUE;
}
Exemplo n.º 30
0
static int rx_task(void *data)
{
	RAII_VAR(struct rx_task_data *, task_data, data, ao2_cleanup);
	RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);

	int added = 0, updated = 0, deleted = 0;
	pjsip_contact_hdr *contact_hdr = NULL;
	struct registrar_contact_details details = { 0, };
	pjsip_tx_data *tdata;
	pjsip_response_addr addr;
	const char *aor_name = ast_sorcery_object_get_id(task_data->aor);

	/* Retrieve the current contacts, we'll need to know whether to update or not */
	contacts = ast_sip_location_retrieve_aor_contacts(task_data->aor);

	/* So we don't count static contacts against max_contacts we prune them out from the container */
	ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_prune_static, NULL);

	if (registrar_validate_contacts(task_data->rdata, contacts, task_data->aor, &added, &updated, &deleted)) {
		/* The provided Contact headers do not conform to the specification */
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 400, NULL, NULL, NULL);
		ast_sip_report_failed_acl(task_data->endpoint, task_data->rdata, "registrar_invalid_contacts_provided");
		ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n",
				ast_sorcery_object_get_id(task_data->endpoint));
		return PJ_TRUE;
	}

	if ((MAX(added - deleted, 0) + (!task_data->aor->remove_existing ? ao2_container_count(contacts) : 0)) > task_data->aor->max_contacts) {
		/* Enforce the maximum number of contacts */
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 403, NULL, NULL, NULL);
		ast_sip_report_failed_acl(task_data->endpoint, task_data->rdata, "registrar_attempt_exceeds_maximum_configured_contacts");
		ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %d\n",
				ast_sorcery_object_get_id(task_data->endpoint), ast_sorcery_object_get_id(task_data->aor), task_data->aor->max_contacts);
		return PJ_TRUE;
	}

	if (!(details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256))) {
		pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 500, NULL, NULL, NULL);
		return PJ_TRUE;
	}

	/* Iterate each provided Contact header and add, update, or delete */
	while ((contact_hdr = pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr ? contact_hdr->next : NULL))) {
		int expiration;
		char contact_uri[PJSIP_MAX_URL_SIZE];
		RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);

		if (contact_hdr->star) {
			/* A star means to unregister everything, so do so for the possible contacts */
			ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, (void *)aor_name);
			break;
		}

		if (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri)) {
			/* This registrar only currently supports sip: and sips: URI schemes */
			continue;
		}

		expiration = registrar_get_expiration(task_data->aor, contact_hdr, task_data->rdata);
		details.uri = pjsip_uri_get_uri(contact_hdr->uri);
		pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri));

		if (!(contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details))) {
			/* If they are actually trying to delete a contact that does not exist... be forgiving */
			if (!expiration) {
				ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n",
					contact_uri, aor_name);
				continue;
			}

			ast_sip_location_add_contact(task_data->aor, contact_uri, ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)));
			ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n",
				contact_uri, aor_name, expiration);
			ast_test_suite_event_notify("AOR_CONTACT_ADDED",
					"Contact: %s\r\n"
					"AOR: %s\r\n"
					"Expiration: %d",
					contact_uri,
					aor_name,
					expiration);
		} else if (expiration) {
			RAII_VAR(struct ast_sip_contact *, updated, ast_sorcery_copy(ast_sip_get_sorcery(), contact), ao2_cleanup);
			updated->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1));
			updated->qualify_frequency = task_data->aor->qualify_frequency;
			updated->authenticate_qualify = task_data->aor->authenticate_qualify;

			ast_sip_location_update_contact(updated);
			ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n",
				contact_uri, aor_name, expiration);
			ast_test_suite_event_notify("AOR_CONTACT_REFRESHED",
					"Contact: %s\r\n"
					"AOR: %s\r\n"
					"Expiration: %d",
					contact_uri,
					aor_name,
					expiration);
		} else {
			ast_sip_location_delete_contact(contact);
			ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact_uri, aor_name);
			ast_test_suite_event_notify("AOR_CONTACT_REMOVED",
					"Contact: %s\r\n"
					"AOR: %s",
					contact_uri,
					aor_name);
		}
	}

	pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool);

	/* If the AOR is configured to remove any existing contacts that have not been updated/added as a result of this REGISTER
	 * do so
	 */
	if (task_data->aor->remove_existing) {
		ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL);
	}

	/* Update the contacts as things will probably have changed */
	ao2_cleanup(contacts);
	contacts = ast_sip_location_retrieve_aor_contacts(task_data->aor);

	/* Send a response containing all of the contacts (including static) that are present on this AOR */
	if (pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), task_data->rdata, 200, NULL, &tdata) != PJ_SUCCESS) {
		return PJ_TRUE;
	}

	/* Add the date header to the response, some UAs use this to set their date and time */
	registrar_add_date_header(tdata);

	ao2_callback(contacts, 0, registrar_add_contact, tdata);

	if (pjsip_get_response_addr(tdata->pool, task_data->rdata, &addr) == PJ_SUCCESS) {
		pjsip_endpt_send_response(ast_sip_get_pjsip_endpoint(), &addr, tdata, NULL, NULL);
	} else {
		pjsip_tx_data_dec_ref(tdata);
	}

	return PJ_TRUE;
}