/*! * \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); } }
/*! * \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; }
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; }
/*! * \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); } }
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; }
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; }
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; }
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; }
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); } }
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; }
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; }
/*! \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; }
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); } }
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; }
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); } }
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; }
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; }
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; }
/*! * \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; }
/*! \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; }
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); }
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); }
/*! \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; }
/*! * \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); }
/*! * \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; }
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"); }
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); }
/*! \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); }
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; }
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; }