/*! \brief Adds devicestate to local channels */ static int local_devicestate(const char *data) { char *exten = ast_strdupa(data); char *context = NULL, *opts = NULL; int res; struct local_pvt *lp; struct ao2_iterator it; if (!(context = strchr(exten, '@'))) { ast_log(LOG_WARNING, "Someone used Local/%s somewhere without a @context. This is bad.\n", exten); return AST_DEVICE_INVALID; } *context++ = '\0'; /* Strip options if they exist */ if ((opts = strchr(context, '/'))) *opts = '\0'; ast_debug(3, "Checking if extension %s@%s exists (devicestate)\n", exten, context); res = ast_exists_extension(NULL, context, exten, 1, NULL); if (!res) return AST_DEVICE_INVALID; res = AST_DEVICE_NOT_INUSE; it = ao2_iterator_init(locals, 0); while ((lp = ao2_iterator_next(&it)) && (res == AST_DEVICE_NOT_INUSE)) { if (!strcmp(exten, lp->exten) && !strcmp(context, lp->context) && lp->owner) { ao2_lock(lp); if (ast_test_flag(lp, LOCAL_LAUNCHED_PBX)) { res = AST_DEVICE_INUSE; } ao2_unlock(lp); } ao2_ref(lp, -1); } ao2_iterator_destroy(&it); return res; }
static int dialgroup_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) { struct ao2_iterator i; struct group *grhead = ao2_find(group_container, data, 0); struct group_entry *entry; size_t bufused = 0; int trunc_warning = 0; int res = 0; if (!grhead) { if (!ast_strlen_zero(cmd)) { ast_log(LOG_WARNING, "No such dialgroup '%s'\n", data); } return -1; } buf[0] = '\0'; i = ao2_iterator_init(grhead->entries, OBJ_POINTER); while ((entry = ao2_iterator_next(&i))) { int tmp = strlen(entry->name); /* Ensure that we copy only complete names, not partials */ if (len - bufused > tmp + 2) { if (bufused != 0) buf[bufused++] = '&'; ast_copy_string(buf + bufused, entry->name, len - bufused); bufused += tmp; } else if (trunc_warning++ == 0) { if (!ast_strlen_zero(cmd)) { ast_log(LOG_WARNING, "Dialgroup '%s' is too large. Truncating list.\n", data); } else { res = 1; ao2_ref(entry, -1); break; } } ao2_ref(entry, -1); } ao2_iterator_destroy(&i); return res; }
int control_dispatch_all(struct stasis_app_control *control, struct ast_channel *chan) { int count = 0; struct ao2_iterator i; void *obj; ast_assert(control->channel == chan); i = ao2_iterator_init(control->command_queue, AO2_ITERATOR_UNLINK); while ((obj = ao2_iterator_next(&i))) { RAII_VAR(struct stasis_app_command *, command, obj, ao2_cleanup); command_invoke(command, control, chan); ++count; } ao2_iterator_destroy(&i); return count; }
static u_char *ast_var_indications(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { static unsigned long long_ret; static char ret_buf[128]; struct ast_tone_zone *tz = NULL; if (header_generic(vp, name, length, exact, var_len, write_method)) return NULL; switch (vp->magic) { case ASTINDCOUNT: { struct ao2_iterator i; long_ret = 0; i = ast_tone_zone_iterator_init(); while ((tz = ao2_iterator_next(&i))) { tz = ast_tone_zone_unref(tz); long_ret++; } ao2_iterator_destroy(&i); return (u_char *) &long_ret; } case ASTINDCURRENT: tz = ast_get_indication_zone(NULL); if (tz) { ast_copy_string(ret_buf, tz->country, sizeof(ret_buf)); *var_len = strlen(ret_buf); tz = ast_tone_zone_unref(tz); return (u_char *) ret_buf; } *var_len = 0; return NULL; default: break; } return NULL; }
/*! * \internal * \brief Post initial MWI count events. * \since 12.1.0 * * \return Nothing */ static void mwi_initial_events(void) { struct ao2_container *mailboxes; const struct ast_mwi_mailbox_object *mailbox; struct ao2_iterator iter; /* Get all mailbox counts. */ mailboxes = ast_mwi_mailbox_get_all(); if (!mailboxes) { return; } /* Post all mailbox counts. */ iter = ao2_iterator_init(mailboxes, AO2_ITERATOR_UNLINK); for (; (mailbox = ao2_iterator_next(&iter)); ast_mwi_mailbox_unref(mailbox)) { mwi_post_event(mailbox); } ao2_iterator_destroy(&iter); ao2_ref(mailboxes, -1); }
/*! \brief Callback function for checking if channels in a bridge are subscribed to */ static int bridge_app_subscribed_involved(struct stasis_app *app, struct ast_bridge_snapshot *snapshot) { int subscribed = 0; struct ao2_iterator iter; char *uniqueid; if (bridge_app_subscribed(app, snapshot->uniqueid)) { return 1; } iter = ao2_iterator_init(snapshot->channels, 0); for (; (uniqueid = ao2_iterator_next(&iter)); ao2_ref(uniqueid, -1)) { if (bridge_app_subscribed(app, uniqueid)) { subscribed = 1; ao2_ref(uniqueid, -1); break; } } ao2_iterator_destroy(&iter); return subscribed; }
/*! \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; }
static char *cli_show_tasks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct ao2_iterator i; struct ast_sip_sched_task *schtd; const char *log_format = ast_logger_get_dateformat(); struct ast_tm tm; char queued[32]; char last_start[32]; char last_end[32]; int datelen; struct timeval now = ast_tvnow(); const char *separator = "======================================"; switch (cmd) { case CLI_INIT: e->command = "pjsip show scheduled_tasks"; e->usage = "Usage: pjsip show scheduled_tasks\n" " Show all scheduled tasks\n"; return NULL; case CLI_GENERATE: return NULL; } if (a->argc != 3) { return CLI_SHOWUSAGE; } ast_localtime(&now, &tm, NULL); datelen = ast_strftime(queued, sizeof(queued), log_format, &tm); ast_cli(a->fd, "PJSIP Scheduled Tasks:\n\n"); ast_cli(a->fd, " %1$-24s %2$-8s %3$-9s %4$-7s %6$-*5$s %7$-*5$s %8$-*5$s\n", "Task Name", "Interval", "Times Run", "State", datelen, "Queued", "Last Started", "Last Ended"); ast_cli(a->fd, " %1$-24.24s %2$-8.8s %3$-9.9s %4$-7.7s %6$-*5$.*5$s %7$-*5$.*5$s %8$-*5$.*5$s\n", separator, separator, separator, separator, datelen, separator, separator, separator); ao2_ref(tasks, +1); ao2_rdlock(tasks); i = ao2_iterator_init(tasks, 0); while ((schtd = ao2_iterator_next(&i))) { ast_localtime(&schtd->when_queued, &tm, NULL); ast_strftime(queued, sizeof(queued), log_format, &tm); if (ast_tvzero(schtd->last_start)) { strcpy(last_start, "not yet started"); } else { ast_localtime(&schtd->last_start, &tm, NULL); ast_strftime(last_start, sizeof(last_start), log_format, &tm); } if (ast_tvzero(schtd->last_end)) { if (ast_tvzero(schtd->last_start)) { strcpy(last_end, "not yet started"); } else { strcpy(last_end, "running"); } } else { ast_localtime(&schtd->last_end, &tm, NULL); ast_strftime(last_end, sizeof(last_end), log_format, &tm); } ast_cli(a->fd, " %1$-24.24s %2$-8.3f %3$-9d %4$-7s %6$-*5$s %7$-*5$s %8$-*5$s\n", schtd->name, schtd->interval / 1000.0, schtd->run_count, schtd->is_running ? "running" : "waiting", datelen, queued, last_start, last_end); ao2_cleanup(schtd); } ao2_iterator_destroy(&i); ao2_unlock(tasks); ao2_ref(tasks, -1); ast_cli(a->fd, "\n"); return CLI_SUCCESS; }
int ast_format_cap_has_joint(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2) { struct ao2_iterator it; struct ast_format *tmp; struct find_joint_data data = { .joint_found = 0, .joint_cap = NULL, }; it = ao2_iterator_init(cap1->formats, cap1->nolock ? AO2_ITERATOR_DONTLOCK : 0); while ((tmp = ao2_iterator_next(&it))) { data.format = tmp; ao2_callback(cap2->formats, OBJ_MULTIPLE | OBJ_NODATA | cap2->nolock, find_joint_cb, &data); ao2_ref(tmp, -1); } ao2_iterator_destroy(&it); return data.joint_found ? 1 : 0; } int ast_format_cap_identical(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2) { struct ao2_iterator it; struct ast_format *tmp; if (ao2_container_count(cap1->formats) != ao2_container_count(cap2->formats)) { return 0; /* if they are not the same size, they are not identical */ } it = ao2_iterator_init(cap1->formats, cap1->nolock ? AO2_ITERATOR_DONTLOCK : 0); while ((tmp = ao2_iterator_next(&it))) { if (!ast_format_cap_iscompatible(cap2, tmp)) { ao2_ref(tmp, -1); ao2_iterator_destroy(&it); return 0; } ao2_ref(tmp, -1); } ao2_iterator_destroy(&it); return 1; } struct ast_format_cap *ast_format_cap_joint(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2) { struct ao2_iterator it; struct ast_format_cap *result = ast_format_cap_alloc_nolock(); struct ast_format *tmp; struct find_joint_data data = { .joint_found = 0, .joint_cap = result, }; if (!result) { return NULL; } it = ao2_iterator_init(cap1->formats, cap1->nolock ? AO2_ITERATOR_DONTLOCK : 0); while ((tmp = ao2_iterator_next(&it))) { data.format = tmp; ao2_callback(cap2->formats, OBJ_MULTIPLE | OBJ_NODATA | cap2->nolock, find_joint_cb, &data); ao2_ref(tmp, -1); } ao2_iterator_destroy(&it); if (ao2_container_count(result->formats)) { return result; } result = ast_format_cap_destroy(result); return NULL; } static int joint_copy_helper(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2, struct ast_format_cap *result, int append) { struct ao2_iterator it; struct ast_format *tmp; struct find_joint_data data = { .joint_cap = result, .joint_found = 0, }; if (!append) { ast_format_cap_remove_all(result); } it = ao2_iterator_init(cap1->formats, cap1->nolock ? AO2_ITERATOR_DONTLOCK : 0); while ((tmp = ao2_iterator_next(&it))) { data.format = tmp; ao2_callback(cap2->formats, OBJ_MULTIPLE | OBJ_NODATA | cap2->nolock, find_joint_cb, &data); ao2_ref(tmp, -1); } ao2_iterator_destroy(&it); return ao2_container_count(result->formats) ? 1 : 0; } int ast_format_cap_joint_append(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2, struct ast_format_cap *result) { return joint_copy_helper(cap1, cap2, result, 1); } int ast_format_cap_joint_copy(const struct ast_format_cap *cap1, const struct ast_format_cap *cap2, struct ast_format_cap *result) { return joint_copy_helper(cap1, cap2, result, 0); } struct ast_format_cap *ast_format_cap_get_type(const struct ast_format_cap *cap, enum ast_format_type ftype) { struct ao2_iterator it; struct ast_format_cap *result = ast_format_cap_alloc_nolock(); struct ast_format *tmp; if (!result) { return NULL; } /* for each format in cap1, see if that format is * compatible with cap2. If so copy it to the result */ it = ao2_iterator_init(cap->formats, cap->nolock ? AO2_ITERATOR_DONTLOCK : 0); while ((tmp = ao2_iterator_next(&it))) { if (AST_FORMAT_GET_TYPE(tmp->id) == ftype) { /* copy format */ ast_format_cap_add(result, tmp); } ao2_ref(tmp, -1); } ao2_iterator_destroy(&it); if (ao2_container_count(result->formats)) { return result; } result = ast_format_cap_destroy(result); return NULL; } int ast_format_cap_has_type(const struct ast_format_cap *cap, enum ast_format_type type) { struct ao2_iterator it; struct ast_format *tmp; it = ao2_iterator_init(cap->formats, cap->nolock ? AO2_ITERATOR_DONTLOCK : 0); while ((tmp = ao2_iterator_next(&it))) { if (AST_FORMAT_GET_TYPE(tmp->id) == type) { ao2_ref(tmp, -1); ao2_iterator_destroy(&it); return 1; } ao2_ref(tmp, -1); } ao2_iterator_destroy(&it); return 0; } void ast_format_cap_iter_start(struct ast_format_cap *cap) { if (!cap->nolock) { ao2_lock(cap->formats); } cap->it = ao2_iterator_init(cap->formats, cap->nolock ? AO2_ITERATOR_DONTLOCK : 0); } void ast_format_cap_iter_end(struct ast_format_cap *cap) { ao2_iterator_destroy(&cap->it); if (!cap->nolock) { ao2_unlock(cap->formats); } } int ast_format_cap_iter_next(struct ast_format_cap *cap, struct ast_format *format) { struct ast_format *tmp = ao2_iterator_next(&cap->it); if (!tmp) { return -1; } ast_format_copy(format, tmp); ao2_ref(tmp, -1); return 0; } char *ast_getformatname_multiple(char *buf, size_t size, struct ast_format_cap *cap) { int x; unsigned len; char *start, *end = buf; struct ast_format tmp_fmt; size_t f_len; const struct ast_format_list *f_list = ast_format_list_get(&f_len); if (!size) { f_list = ast_format_list_destroy(f_list); return buf; } snprintf(end, size, "("); len = strlen(end); end += len; size -= len; start = end; for (x = 0; x < f_len; x++) { ast_format_copy(&tmp_fmt, &f_list[x].format); if (ast_format_cap_iscompatible(cap, &tmp_fmt)) { snprintf(end, size, "%s|", f_list[x].name); len = strlen(end); end += len; size -= len; } } if (start == end) { ast_copy_string(start, "nothing)", size); } else if (size > 1) { *(end - 1) = ')'; } f_list = ast_format_list_destroy(f_list); return buf; } uint64_t ast_format_cap_to_old_bitfield(const struct ast_format_cap *cap) { uint64_t res = 0; struct ao2_iterator it; struct ast_format *tmp; it = ao2_iterator_init(cap->formats, cap->nolock ? AO2_ITERATOR_DONTLOCK : 0); while ((tmp = ao2_iterator_next(&it))) { res |= ast_format_to_old_bitfield(tmp); ao2_ref(tmp, -1); } ao2_iterator_destroy(&it); return res; }
void ast_ari_endpoints_list_by_tech(struct ast_variable *headers, struct ast_ari_endpoints_list_by_tech_args *args, struct ast_ari_response *response) { RAII_VAR(struct stasis_cache *, cache, NULL, ao2_cleanup); RAII_VAR(struct ao2_container *, snapshots, NULL, ao2_cleanup); RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); struct ao2_iterator i; void *obj; if (!ast_get_channel_tech(args->tech)) { ast_ari_response_error(response, 404, "Not Found", "No Endpoints found - invalid tech %s", args->tech); return; } cache = ast_endpoint_cache(); if (!cache) { ast_ari_response_error( response, 500, "Internal Server Error", "Message bus not initialized"); return; } ao2_ref(cache, +1); snapshots = stasis_cache_dump(cache, ast_endpoint_snapshot_type()); if (!snapshots) { ast_ari_response_alloc_failed(response); return; } json = ast_json_array_create(); if (!json) { ast_ari_response_alloc_failed(response); return; } i = ao2_iterator_init(snapshots, 0); while ((obj = ao2_iterator_next(&i))) { RAII_VAR(struct stasis_message *, msg, obj, ao2_cleanup); struct ast_endpoint_snapshot *snapshot = stasis_message_data(msg); struct ast_json *json_endpoint; int r; if (strcasecmp(args->tech, snapshot->tech) != 0) { continue; } json_endpoint = ast_endpoint_snapshot_to_json(snapshot, stasis_app_get_sanitizer()); if (!json_endpoint) { continue; } r = ast_json_array_append( json, json_endpoint); if (r != 0) { ast_ari_response_alloc_failed(response); return; } } ao2_iterator_destroy(&i); ast_ari_response_ok(response, ast_json_ref(json)); }
static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct ao2_iterator i; struct ast_codec *codec; switch (cmd) { case CLI_INIT: e->command = "core show codecs [audio|video|image|text]"; e->usage = "Usage: core show codecs [audio|video|image|text]\n" " Displays codec mapping\n"; return NULL; case CLI_GENERATE: return NULL; } if ((a->argc < 3) || (a->argc > 4)) { return CLI_SHOWUSAGE; } if (!ast_opt_dont_warn) { ast_cli(a->fd, "Disclaimer: this command is for informational purposes only.\n" "\tIt does not indicate anything about your configuration.\n"); } ast_cli(a->fd, "%8s %5s %8s %s\n","ID","TYPE","NAME","DESCRIPTION"); ast_cli(a->fd, "-----------------------------------------------------------------------------------\n"); ao2_rdlock(codecs); i = ao2_iterator_init(codecs, AO2_ITERATOR_DONTLOCK); for (; (codec = ao2_iterator_next(&i)); ao2_ref(codec, -1)) { if (a->argc == 4) { if (!strcasecmp(a->argv[3], "audio")) { if (codec->type != AST_MEDIA_TYPE_AUDIO) { continue; } } else if (!strcasecmp(a->argv[3], "video")) { if (codec->type != AST_MEDIA_TYPE_VIDEO) { continue; } } else if (!strcasecmp(a->argv[3], "image")) { if (codec->type != AST_MEDIA_TYPE_IMAGE) { continue; } } else if (!strcasecmp(a->argv[3], "text")) { if (codec->type != AST_MEDIA_TYPE_TEXT) { continue; } } else { continue; } } ast_cli(a->fd, "%8u %5s %8s (%s)\n", codec->id, ast_codec_media_type2str(codec->type), codec->name, codec->description); } ao2_iterator_destroy(&i); ao2_unlock(codecs); return CLI_SUCCESS; }
static int astobj2_test_helper(int use_hash, int use_cmp, unsigned int lim, struct ast_test *test) { struct ao2_container *c1; struct ao2_container *c2; struct ao2_iterator it; struct test_obj *obj; struct test_obj tmp_obj; int bucket_size; int increment = 0; int destructor_count = 0; int num; int res = AST_TEST_PASS; /* This test needs at least 5 objects */ if (lim < 5) { lim = 5; } bucket_size = (ast_random() % ((lim / 4) + 1)) + 1; c1 = ao2_container_alloc(bucket_size, use_hash ? test_hash_cb : NULL, use_cmp ? test_cmp_cb : NULL); c2 = ao2_container_alloc(bucket_size, test_hash_cb, test_cmp_cb); if (!c1 || !c2) { ast_test_status_update(test, "ao2_container_alloc failed.\n"); res = AST_TEST_FAIL; goto cleanup; } /* Create objects and link into container */ destructor_count = lim; for (num = 1; num <= lim; num++) { if (!(obj = ao2_alloc(sizeof(struct test_obj), test_obj_destructor))) { ast_test_status_update(test, "ao2_alloc failed.\n"); res = AST_TEST_FAIL; goto cleanup; } snprintf(obj->c, sizeof(obj->c), "zombie #%d", num); obj->destructor_count = &destructor_count; obj->i = num; ao2_link(c1, obj); ao2_ref(obj, -1); if (ao2_container_count(c1) != num) { ast_test_status_update(test, "container did not link correctly\n"); res = AST_TEST_FAIL; } } ast_test_status_update(test, "Container created: random bucket size %d: number of items: %d\n", bucket_size, lim); /* Testing ao2_find with no flags */ num = 100; for (; num; num--) { int i = (ast_random() % ((lim / 2)) + 1); /* find a random object */ tmp_obj.i = i; if (!(obj = ao2_find(c1, &tmp_obj, 0))) { res = AST_TEST_FAIL; ast_test_status_update(test, "COULD NOT FIND:%d, ao2_find() with no flags failed.\n", i); } else { /* a correct match will only take place when the custom cmp function is used */ if (use_cmp && obj->i != i) { ast_test_status_update(test, "object %d does not match object %d\n", obj->i, tmp_obj.i); res = AST_TEST_FAIL; } ao2_ref(obj, -1); } } /* Testing ao2_find with OBJ_POINTER */ num = 75; for (; num; num--) { int i = (ast_random() % ((lim / 2)) + 1); /* find a random object */ snprintf(tmp_obj.c, sizeof(tmp_obj.c), "zombie #%d", i); tmp_obj.i = i; if (!(obj = ao2_find(c1, &tmp_obj, OBJ_POINTER))) { res = AST_TEST_FAIL; ast_test_status_update(test, "COULD NOT FIND:%d, ao2_find() with OBJ_POINTER flag failed.\n", i); } else { /* a correct match will only take place when the custom cmp function is used */ if (use_cmp && obj->i != i) { ast_test_status_update(test, "object %d does not match object %d\n", obj->i, tmp_obj.i); res = AST_TEST_FAIL; } ao2_ref(obj, -1); } } /* Testing ao2_find with OBJ_POINTER | OBJ_UNLINK | OBJ_CONTINUE. * In this test items are unlinked from c1 and placed in c2. Then * unlinked from c2 and placed back into c1. * * For this module and set of custom hash/cmp functions, an object * should only be found if the astobj2 default cmp function is used. * This test is designed to mimic the chan_iax.c call number use case. */ num = lim < 25 ? lim : 25; for (; num; num--) { if (!(obj = ao2_find(c1, NULL, OBJ_POINTER | OBJ_UNLINK | OBJ_CONTINUE))) { if (!use_cmp) { ast_test_status_update(test, "ao2_find with OBJ_POINTER | OBJ_UNLINK | OBJ_CONTINUE failed with default hash function.\n"); res = AST_TEST_FAIL; } } else { if (use_cmp) { ast_test_status_update(test, "ao2_find with OBJ_POINTER | OBJ_UNLINK | OBJ_CONTINUE failed with custom hash function.\n"); res = AST_TEST_FAIL; } ao2_link(c2, obj); ao2_ref(obj, -1); } } it = ao2_iterator_init(c2, 0); while ((obj = ao2_iterator_next(&it))) { ao2_unlink(c2, obj); ao2_link(c1, obj); ao2_ref(obj, -1); } ao2_iterator_destroy(&it); /* Test Callback with no flags. */ increment = 0; ao2_callback(c1, 0, increment_cb, &increment); if (increment != lim) { ast_test_status_update(test, "callback with no flags failed. Increment is %d\n", increment); res = AST_TEST_FAIL; } /* Test Callback with OBJ_NODATA. This should do nothing different than with no flags here. */ increment = 0; ao2_callback(c1, OBJ_NODATA, increment_cb, &increment); if (increment != lim) { ast_test_status_update(test, "callback with OBJ_NODATA failed. Increment is %d\n", increment); res = AST_TEST_FAIL; } /* Is the container count what we expect after all the finds and unlinks?*/ if (ao2_container_count(c1) != lim) { ast_test_status_update(test, "container count does not match what is expected after ao2_find tests.\n"); res = AST_TEST_FAIL; } /* Testing iterator. Unlink a single object and break. do not add item back */ it = ao2_iterator_init(c1, 0); num = (lim / 4) + 1; while ((obj = ao2_iterator_next(&it))) { if (obj->i == num) { ao2_ref(obj, -1); ao2_unlink(c1, obj); break; } ao2_ref(obj, -1); } ao2_iterator_destroy(&it); /* Is the container count what we expect after removing a single item? */ if (ao2_container_count(c1) != (lim - 1)) { ast_test_status_update(test, "unlink during iterator failed. Number %d was not removed.\n", num); res = AST_TEST_FAIL; } /* Test unlink all with OBJ_MULTIPLE, leave a single object for the container to destroy */ ao2_callback(c1, OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NODATA, all_but_one_cb, NULL); /* check to make sure all test_obj destructors were called except for 1 */ if (destructor_count != 1) { ast_test_status_update(test, "OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NODATA failed. destructor count %d\n", destructor_count); res = AST_TEST_FAIL; } cleanup: /* destroy containers */ if (c1) { ao2_ref(c1, -1); } if (c2) { ao2_ref(c2, -1); } if (destructor_count > 0) { ast_test_status_update(test, "all destructors were not called, destructor count is %d\n", destructor_count); res = AST_TEST_FAIL; } else if (destructor_count < 0) { ast_test_status_update(test, "Destructor was called too many times, destructor count is %d\n", destructor_count); res = AST_TEST_FAIL; } return res; }
static void send_mwi_notify(struct mwi_subscription *sub) { struct ast_sip_message_accumulator counter = { .old_msgs = 0, .new_msgs = 0, }; struct ast_sip_body_data data = { .body_type = AST_SIP_MESSAGE_ACCUMULATOR, .body_data = &counter, }; ao2_callback(sub->stasis_subs, OBJ_NODATA, get_message_count, &counter); if (sub->is_solicited) { ast_sip_subscription_notify(sub->sip_sub, &data, 0); return; } send_unsolicited_mwi_notify(sub, &counter); } static int unsubscribe_stasis(void *obj, void *arg, int flags) { struct mwi_stasis_subscription *mwi_stasis = obj; if (mwi_stasis->stasis_sub) { ast_debug(3, "Removing stasis subscription to mailbox %s\n", mwi_stasis->mailbox); mwi_stasis->stasis_sub = stasis_unsubscribe_and_join(mwi_stasis->stasis_sub); } return CMP_MATCH; } static void mwi_subscription_shutdown(struct ast_sip_subscription *sub) { struct mwi_subscription *mwi_sub; struct ast_datastore *mwi_datastore; mwi_datastore = ast_sip_subscription_get_datastore(sub, MWI_DATASTORE); if (!mwi_datastore) { return; } mwi_sub = mwi_datastore->data; ao2_callback(mwi_sub->stasis_subs, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe_stasis, NULL); ast_sip_subscription_remove_datastore(sub, MWI_DATASTORE); ao2_ref(mwi_datastore, -1); } static void mwi_ds_destroy(void *data) { struct mwi_subscription *sub = data; ao2_ref(sub, -1); } static struct ast_datastore_info mwi_ds_info = { .destroy = mwi_ds_destroy, }; static int add_mwi_datastore(struct mwi_subscription *sub) { struct ast_datastore *mwi_datastore; int res; mwi_datastore = ast_sip_subscription_alloc_datastore(&mwi_ds_info, MWI_DATASTORE); if (!mwi_datastore) { return -1; } ao2_ref(sub, +1); mwi_datastore->data = sub; /* * NOTE: Adding the datastore to the subscription creates a ref loop * that must be manually broken. */ res = ast_sip_subscription_add_datastore(sub->sip_sub, mwi_datastore); ao2_ref(mwi_datastore, -1); return res; } /*! * \brief Determines if an endpoint is receiving unsolicited MWI for a particular mailbox. * * \param endpoint The endpoint to check * \param mailbox The candidate mailbox * \retval 0 The endpoint does not receive unsolicited MWI for this mailbox * \retval 1 The endpoint receives unsolicited MWI for this mailbox */ static int endpoint_receives_unsolicited_mwi_for_mailbox(struct ast_sip_endpoint *endpoint, const char *mailbox) { struct ao2_iterator *mwi_subs; struct mwi_subscription *mwi_sub; const char *endpoint_id = ast_sorcery_object_get_id(endpoint); int ret = 0; mwi_subs = ao2_find(unsolicited_mwi, endpoint_id, OBJ_SEARCH_KEY | OBJ_MULTIPLE); if (!mwi_subs) { return 0; } for (; (mwi_sub = ao2_iterator_next(mwi_subs)) && !ret; ao2_cleanup(mwi_sub)) { struct mwi_stasis_subscription *mwi_stasis; mwi_stasis = ao2_find(mwi_sub->stasis_subs, mailbox, OBJ_SEARCH_KEY); if (mwi_stasis) { ret = 1; ao2_cleanup(mwi_stasis); } } ao2_iterator_destroy(mwi_subs); return ret; } /*! * \brief Determine if an endpoint is a candidate to be able to subscribe for MWI * * Currently, this just makes sure that the endpoint is not already receiving unsolicted * MWI for any of an AOR's configured mailboxes. * * \param obj The AOR to which the endpoint is subscribing. * \param arg The endpoint that is attempting to subscribe. * \param flags Unused. * \retval 0 Endpoint is a candidate to subscribe to MWI on the AOR. * \retval -1 The endpoint cannot subscribe to MWI on the AOR. */ static int mwi_validate_for_aor(void *obj, void *arg, int flags) { struct ast_sip_aor *aor = obj; struct ast_sip_endpoint *endpoint = arg; char *mailboxes; char *mailbox; if (ast_strlen_zero(aor->mailboxes)) { return 0; } mailboxes = ast_strdupa(aor->mailboxes); while ((mailbox = strsep(&mailboxes, ","))) { if (endpoint_receives_unsolicited_mwi_for_mailbox(endpoint, mailbox)) { ast_log(LOG_NOTICE, "Endpoint '%s' already configured for unsolicited MWI for mailbox '%s'. " "Denying MWI subscription to %s\n", ast_sorcery_object_get_id(endpoint), mailbox, ast_sorcery_object_get_id(aor)); return -1; } } return 0; } static int mwi_on_aor(void *obj, void *arg, int flags) { struct ast_sip_aor *aor = obj; struct mwi_subscription *sub = arg; char *mailboxes; char *mailbox; if (ast_strlen_zero(aor->mailboxes)) { return 0; } mailboxes = ast_strdupa(aor->mailboxes); while ((mailbox = strsep(&mailboxes, ","))) { struct mwi_stasis_subscription *mwi_stasis_sub; mwi_stasis_sub = mwi_stasis_subscription_alloc(mailbox, sub); if (!mwi_stasis_sub) { continue; } ao2_link(sub->stasis_subs, mwi_stasis_sub); ao2_ref(mwi_stasis_sub, -1); } return 0; } static struct mwi_subscription *mwi_create_subscription( struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub) { struct mwi_subscription *sub = mwi_subscription_alloc(endpoint, 1, sip_sub); if (!sub) { return NULL; } if (add_mwi_datastore(sub)) { ast_log(LOG_WARNING, "Unable to add datastore for MWI subscription to %s\n", sub->id); ao2_ref(sub, -1); return NULL; } return sub; } static struct mwi_subscription *mwi_subscribe_single( struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub, const char *name) { struct ast_sip_aor *aor; struct mwi_subscription *sub; aor = ast_sip_location_retrieve_aor(name); if (!aor) { /*! I suppose it's possible for the AOR to disappear on us * between accepting the subscription and sending the first * NOTIFY... */ ast_log(LOG_WARNING, "Unable to locate aor %s. MWI subscription failed.\n", name); return NULL; } sub = mwi_create_subscription(endpoint, sip_sub); if (sub) { mwi_on_aor(aor, sub, 0); } ao2_ref(aor, -1); return sub; } static struct mwi_subscription *mwi_subscribe_all( struct ast_sip_endpoint *endpoint, struct ast_sip_subscription *sip_sub) { struct mwi_subscription *sub; sub = mwi_create_subscription(endpoint, sip_sub); if (!sub) { return NULL; } ast_sip_for_each_aor(endpoint->aors, mwi_on_aor, sub); return sub; } static int mwi_new_subscribe(struct ast_sip_endpoint *endpoint, const char *resource) { RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup); if (ast_strlen_zero(resource)) { if (ast_sip_for_each_aor(endpoint->aors, mwi_validate_for_aor, endpoint)) { return 500; } return 200; } aor = ast_sip_location_retrieve_aor(resource); if (!aor) { ast_log(LOG_WARNING, "Unable to locate aor %s. MWI subscription failed.\n", resource); return 404; } if (ast_strlen_zero(aor->mailboxes)) { ast_log(LOG_NOTICE, "AOR %s has no configured mailboxes. MWI subscription failed.\n", resource); return 404; } if (mwi_validate_for_aor(aor, endpoint, 0)) { return 500; } return 200; }
/*! * \internal * \brief Get the requested mailboxes. * \since 12.1.0 * * \param s AMI session. * \param m AMI message. * * \retval 0 to keep AMI connection. * \retval -1 to disconnect AMI connection. */ static int mwi_mailbox_get(struct mansession *s, const struct message *m) { char id_text[256]; const char *id; const char *mailbox_id = astman_get_header(m, "Mailbox"); const struct ast_mwi_mailbox_object *mailbox; struct ao2_container *mailboxes; unsigned count; struct ao2_iterator iter; if (ast_strlen_zero(mailbox_id)) { astman_send_error(s, m, "Missing mailbox parameter in request"); return 0; } if (*mailbox_id == '/') { struct ast_str *regex_string; regex_string = ast_str_create(strlen(mailbox_id) + 1); if (!regex_string) { astman_send_error(s, m, "Memory Allocation Failure"); return 0; } /* Make "/regex/" into "regex" */ if (ast_regex_string_to_regex_pattern(mailbox_id, ®ex_string) != 0) { astman_send_error_va(s, m, "Mailbox regex format invalid in: %s", mailbox_id); ast_free(regex_string); return 0; } mailboxes = ast_mwi_mailbox_get_by_regex(ast_str_buffer(regex_string)); ast_free(regex_string); } else { mailboxes = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, NULL); if (mailboxes) { mailbox = ast_mwi_mailbox_get(mailbox_id); if (mailbox) { if (!ao2_link(mailboxes, (void *) mailbox)) { ao2_ref(mailboxes, -1); mailboxes = NULL; } ast_mwi_mailbox_unref(mailbox); } } } if (!mailboxes) { astman_send_error(s, m, "Mailbox container creation failure"); return 0; } astman_send_listack(s, m, "Mailboxes will follow", "start"); id = astman_get_header(m, "ActionID"); if (!ast_strlen_zero(id)) { snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id); } else { id_text[0] = '\0'; } /* Output mailbox list. */ count = 0; iter = ao2_iterator_init(mailboxes, AO2_ITERATOR_UNLINK); for (; (mailbox = ao2_iterator_next(&iter)); ast_mwi_mailbox_unref(mailbox)) { ++count; astman_append(s, "Event: MWIGet\r\n" "Mailbox: %s\r\n" "OldMessages: %u\r\n" "NewMessages: %u\r\n" "%s" "\r\n", ast_mwi_mailbox_get_id(mailbox), ast_mwi_mailbox_get_msgs_old(mailbox), ast_mwi_mailbox_get_msgs_new(mailbox), id_text); } ao2_iterator_destroy(&iter); ao2_ref(mailboxes, -1); astman_send_list_complete_start(s, m, "MWIGetComplete", count); astman_send_list_complete_end(s); return 0; }
static void manager_parking_status_all_lots(struct mansession *s, const struct message *m, const char *id_text) { struct parked_user *curuser; struct ao2_container *lot_container; struct ao2_iterator iter_lots; struct ao2_iterator iter_users; struct parking_lot *curlot; int total = 0; lot_container = get_parking_lot_container(); if (!lot_container) { ast_log(LOG_ERROR, "Failed to obtain parking lot list. Action canceled.\n"); astman_send_error(s, m, "Could not create parking lot list"); return; } astman_send_listack(s, m, "Parked calls will follow", "start"); iter_lots = ao2_iterator_init(lot_container, 0); while ((curlot = ao2_iterator_next(&iter_lots))) { iter_users = ao2_iterator_init(curlot->parked_users, 0); while ((curuser = ao2_iterator_next(&iter_users))) { RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup); RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free); payload = parked_call_payload_from_parked_user(curuser, PARKED_CALL); if (!payload) { ao2_ref(curuser, -1); ao2_iterator_destroy(&iter_users); ao2_ref(curlot, -1); goto abort_list; } parked_call_string = manager_build_parked_call_string(payload); if (!parked_call_string) { ao2_ref(curuser, -1); ao2_iterator_destroy(&iter_users); ao2_ref(curlot, -1); goto abort_list; } total++; astman_append(s, "Event: ParkedCall\r\n" "%s" /* The parked call string */ "%s" /* The action ID */ "\r\n", ast_str_buffer(parked_call_string), id_text); ao2_ref(curuser, -1); } ao2_iterator_destroy(&iter_users); ao2_ref(curlot, -1); } abort_list: ao2_iterator_destroy(&iter_lots); astman_send_list_complete_start(s, m, "ParkedCallsComplete", total); astman_append(s, "Total: %d\r\n", total); astman_send_list_complete_end(s); }
/*! \brief Function which passes through an aliased CLI command to the real one */ static char *cli_alias_passthrough(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct cli_alias *alias; struct cli_alias tmp = { .cli_entry.command = e->command, }; char *generator; const char *line; /* Try to find the alias based on the CLI entry */ if (!(alias = ao2_find(cli_aliases, &tmp, OBJ_POINTER))) { return 0; } switch (cmd) { case CLI_INIT: ao2_ref(alias, -1); return NULL; case CLI_GENERATE: line = a->line; line += (strlen(alias->alias)); if (!strncasecmp(alias->alias, alias->real_cmd, strlen(alias->alias))) { generator = NULL; } else if (!ast_strlen_zero(a->word)) { struct ast_str *real_cmd = ast_str_alloca(strlen(alias->real_cmd) + strlen(line) + 1); ast_str_append(&real_cmd, 0, "%s%s", alias->real_cmd, line); generator = ast_cli_generator(ast_str_buffer(real_cmd), a->word, a->n); } else { generator = ast_cli_generator(alias->real_cmd, a->word, a->n); } ao2_ref(alias, -1); return generator; } /* If they gave us extra arguments we need to construct a string to pass in */ if (a->argc != e->args) { struct ast_str *real_cmd = ast_str_alloca(2048); int i; ast_str_append(&real_cmd, 0, "%s", alias->real_cmd); /* Add the additional arguments that have been passed in */ for (i = e->args + 1; i <= a->argc; i++) { ast_str_append(&real_cmd, 0, " %s", a->argv[i - 1]); } ast_cli_command(a->fd, ast_str_buffer(real_cmd)); } else { ast_cli_command(a->fd, alias->real_cmd); } ao2_ref(alias, -1); return CLI_SUCCESS; } /*! \brief CLI Command to display CLI Aliases */ static char *alias_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { #define FORMAT "%-50.50s %-50.50s\n" struct cli_alias *alias; struct ao2_iterator i; switch (cmd) { case CLI_INIT: e->command = "cli show aliases"; e->usage = "Usage: cli show aliases\n" " Displays a list of aliased CLI commands.\n"; return NULL; case CLI_GENERATE: return NULL; } ast_cli(a->fd, FORMAT, "Alias Command", "Real Command"); i = ao2_iterator_init(cli_aliases, 0); for (; (alias = ao2_iterator_next(&i)); ao2_ref(alias, -1)) { ast_cli(a->fd, FORMAT, alias->alias, alias->real_cmd); } ao2_iterator_destroy(&i); return CLI_SUCCESS; #undef FORMAT } /*! \brief CLI commands to interact with things */ static struct ast_cli_entry cli_alias[] = { AST_CLI_DEFINE(alias_show, "Show CLI command aliases"), }; /*! \brief Function called to load or reload the configuration file */ static void load_config(int reload) { struct ast_config *cfg = NULL; struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; struct cli_alias *alias; struct ast_variable *v, *v1; if (!(cfg = ast_config_load(config_file, config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) { ast_log(LOG_ERROR, "res_clialiases configuration file '%s' not found\n", config_file); return; } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { return; } /* Destroy any existing CLI aliases */ if (reload) { ao2_callback(cli_aliases, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL); } for (v = ast_variable_browse(cfg, "general"); v; v = v->next) { if (strcmp(v->name, "template")) { ast_log(LOG_WARNING, "%s is not a correct option in [%s]\n", v->name, "general"); continue; } /* Read in those there CLI aliases */ for (v1 = ast_variable_browse(cfg, v->value); v1; v1 = v1->next) { if (!(alias = ao2_alloc((sizeof(*alias) + strlen(v1->name) + strlen(v1->value) + 2), alias_destroy))) { continue; } alias->alias = ((char *) alias) + sizeof(*alias); alias->real_cmd = ((char *) alias->alias) + strlen(v1->name) + 1; strcpy(alias->alias, v1->name); strcpy(alias->real_cmd, v1->value); alias->cli_entry.handler = cli_alias_passthrough; alias->cli_entry.command = alias->alias; alias->cli_entry.usage = "Aliased CLI Command\n"; ast_cli_register(&alias->cli_entry); ao2_link(cli_aliases, alias); ast_verbose(VERBOSE_PREFIX_2 "Aliased CLI command '%s' to '%s'\n", v1->name, v1->value); ao2_ref(alias, -1); } } ast_config_destroy(cfg); return; } /*! \brief Function called to reload the module */ static int reload_module(void) { load_config(1); return 0; }
/* * This is testing code for astobj */ static char *handle_astobj2_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct ao2_container *c1; struct ao2_container *c2; int i, lim; char *obj; static int prof_id = -1; struct ast_cli_args fake_args = { a->fd, 0, NULL }; switch (cmd) { case CLI_INIT: e->command = "astobj2 test"; e->usage = "Usage: astobj2 test <num>\n" " Runs astobj2 test. Creates 'num' objects,\n" " and test iterators, callbacks and maybe other stuff\n"; return NULL; case CLI_GENERATE: return NULL; } if (a->argc != 3) { return CLI_SHOWUSAGE; } if (prof_id == -1) { prof_id = ast_add_profile("ao2_alloc", 0); } ast_cli(a->fd, "argc %d argv %s %s %s\n", a->argc, a->argv[0], a->argv[1], a->argv[2]); lim = atoi(a->argv[2]); ast_cli(a->fd, "called astobj_test\n"); handle_astobj2_stats(e, CLI_HANDLER, &fake_args); /* * Allocate a list container. */ c1 = ao2_t_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL /* no sort */, NULL /* no callback */, "test"); ast_cli(a->fd, "container allocated as %p\n", c1); /* * fill the container with objects. * ao2_alloc() gives us a reference which we pass to the * container when we do the insert. */ for (i = 0; i < lim; i++) { ast_mark(prof_id, 1 /* start */); obj = ao2_t_alloc(80, NULL,"test"); ast_mark(prof_id, 0 /* stop */); ast_cli(a->fd, "object %d allocated as %p\n", i, obj); sprintf(obj, "-- this is obj %d --", i); ao2_link(c1, obj); /* At this point, the refcount on obj is 2 due to the allocation * and linking. We can go ahead and reduce the refcount by 1 * right here so that when the container is unreffed later, the * objects will be freed */ ao2_t_ref(obj, -1, "test"); } ast_cli(a->fd, "testing callbacks\n"); ao2_t_callback(c1, 0, print_cb, a, "test callback"); ast_cli(a->fd, "testing container cloning\n"); c2 = ao2_container_clone(c1, 0); if (ao2_container_count(c1) != ao2_container_count(c2)) { ast_cli(a->fd, "Cloned container does not have the same number of objects!\n"); } ao2_t_callback(c2, 0, print_cb, a, "test callback"); ast_cli(a->fd, "testing iterators, remove every second object\n"); { struct ao2_iterator ai; int x = 0; ai = ao2_iterator_init(c1, 0); while ( (obj = ao2_t_iterator_next(&ai,"test")) ) { ast_cli(a->fd, "iterator on <%s>\n", obj); if (x++ & 1) ao2_t_unlink(c1, obj,"test"); ao2_t_ref(obj, -1,"test"); } ao2_iterator_destroy(&ai); ast_cli(a->fd, "testing iterators again\n"); ai = ao2_iterator_init(c1, 0); while ( (obj = ao2_t_iterator_next(&ai,"test")) ) { ast_cli(a->fd, "iterator on <%s>\n", obj); ao2_t_ref(obj, -1,"test"); } ao2_iterator_destroy(&ai); } ast_cli(a->fd, "testing callbacks again\n"); ao2_t_callback(c1, 0, print_cb, a, "test callback"); ast_verbose("now you should see an error and possible assertion failure messages:\n"); ao2_t_ref(&i, -1, ""); /* i is not a valid object so we print an error here */ ast_cli(a->fd, "destroy container\n"); ao2_t_ref(c1, -1, ""); /* destroy container */ ao2_t_ref(c2, -1, ""); /* destroy container */ handle_astobj2_stats(e, CLI_HANDLER, &fake_args); return CLI_SUCCESS; }