void ei_encode_switch_event_headers(ei_x_buff * ebuf, switch_event_t *event) { int i; char *uuid = switch_event_get_header(event, "unique-id"); switch_event_header_t *hp; for (i = 0, hp = event->headers; hp; hp = hp->next, i++); if (event->body) i++; ei_x_encode_list_header(ebuf, i + 1); if (uuid) { _ei_x_encode_string(ebuf, switch_event_get_header(event, "unique-id")); } else { ei_x_encode_atom(ebuf, "undefined"); } for (hp = event->headers; hp; hp = hp->next) { ei_x_encode_tuple_header(ebuf, 2); _ei_x_encode_string(ebuf, hp->name); switch_url_decode(hp->value); _ei_x_encode_string(ebuf, hp->value); } if (event->body) { ei_x_encode_tuple_header(ebuf, 2); _ei_x_encode_string(ebuf, "body"); _ei_x_encode_string(ebuf, event->body); } ei_x_encode_empty_list(ebuf); }
static switch_status_t handle_msg_bgapi(listener_t *listener, erlang_msg * msg, int arity, ei_x_buff * buf, ei_x_buff * rbuf) { char api_cmd[MAXATOMLEN]; char arg[ARGLEN]; if (arity < 3 || ei_decode_atom(buf->buff, &buf->index, api_cmd) || ei_decode_string_or_binary(buf->buff, &buf->index, ARGLEN - 1, arg)) { ei_x_encode_tuple_header(rbuf, 2); ei_x_encode_atom(rbuf, "error"); ei_x_encode_atom(rbuf, "badarg"); } else { struct api_command_struct *acs = NULL; switch_memory_pool_t *pool; switch_thread_t *thread; switch_threadattr_t *thd_attr = NULL; switch_uuid_t uuid; switch_core_new_memory_pool(&pool); acs = switch_core_alloc(pool, sizeof(*acs)); switch_assert(acs); acs->pool = pool; acs->listener = listener; acs->api_cmd = switch_core_strdup(acs->pool, api_cmd); acs->arg = switch_core_strdup(acs->pool, arg); acs->bg = 1; acs->pid = msg->from; switch_threadattr_create(&thd_attr, acs->pool); switch_threadattr_detach_set(thd_attr, 1); switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); switch_uuid_get(&uuid); switch_uuid_format(acs->uuid_str, &uuid); switch_thread_create(&thread, thd_attr, api_exec, acs, acs->pool); ei_x_encode_tuple_header(rbuf, 2); ei_x_encode_atom(rbuf, "ok"); _ei_x_encode_string(rbuf, acs->uuid_str); } return SWITCH_STATUS_SUCCESS; }
static switch_xml_t fetch_handler(const char *section, const char *tag_name, const char *key_name, const char *key_value, switch_event_t *params, void *user_data) { switch_xml_t xml = NULL; switch_uuid_t uuid; switch_time_t now = 0; ei_xml_agent_t *agent = (ei_xml_agent_t *) user_data; ei_xml_client_t *client; fetch_handler_t *fetch_handler; xml_fetch_reply_t reply, *pending, *prev = NULL; now = switch_micro_time_now(); if (!switch_test_flag(&globals, LFLAG_RUNNING)) { return xml; } /* read-lock the agent */ switch_thread_rwlock_rdlock(agent->lock); /* serialize access to current, used to round-robin requests */ /* TODO: check globals for round-robin boolean or loop all clients */ switch_mutex_lock(agent->current_client_mutex); if (!agent->current_client) { client = agent->clients; } else { client = agent->current_client; } if (client) { agent->current_client = client->next; } switch_mutex_unlock(agent->current_client_mutex); /* no client, no work required */ if (!client || !client->fetch_handlers) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "No %s XML erlang handler currently available\n" ,section); switch_thread_rwlock_unlock(agent->lock); return xml; } /* prepare the reply collector */ switch_uuid_get(&uuid); switch_uuid_format(reply.uuid_str, &uuid); reply.next = NULL; reply.xml_str = NULL; /* add our reply placeholder to the replies list */ switch_mutex_lock(agent->replies_mutex); if (!agent->replies) { agent->replies = &reply; } else { reply.next = agent->replies; agent->replies = &reply; } switch_mutex_unlock(agent->replies_mutex); fetch_handler = client->fetch_handlers; while (fetch_handler != NULL) { ei_send_msg_t *send_msg; switch_malloc(send_msg, sizeof(*send_msg)); memcpy(&send_msg->pid, &fetch_handler->pid, sizeof(erlang_pid)); ei_x_new_with_version(&send_msg->buf); ei_x_encode_tuple_header(&send_msg->buf, 7); ei_x_encode_atom(&send_msg->buf, "fetch"); ei_x_encode_atom(&send_msg->buf, section); _ei_x_encode_string(&send_msg->buf, tag_name ? tag_name : "undefined"); _ei_x_encode_string(&send_msg->buf, key_name ? key_name : "undefined"); _ei_x_encode_string(&send_msg->buf, key_value ? key_value : "undefined"); _ei_x_encode_string(&send_msg->buf, reply.uuid_str); if (params) { ei_encode_switch_event_headers(&send_msg->buf, params); } else { ei_x_encode_empty_list(&send_msg->buf); } if (switch_queue_trypush(client->ei_node->send_msgs, send_msg) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to send %s XML request to %s <%d.%d.%d>\n" ,section ,fetch_handler->pid.node ,fetch_handler->pid.creation ,fetch_handler->pid.num ,fetch_handler->pid.serial); ei_x_free(&send_msg->buf); switch_safe_free(send_msg); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Sending %s XML request (%s) to %s <%d.%d.%d>\n" ,section ,reply.uuid_str ,fetch_handler->pid.node ,fetch_handler->pid.creation ,fetch_handler->pid.num ,fetch_handler->pid.serial); } fetch_handler = fetch_handler->next; } /* wait for a reply (if there isnt already one...amazingly improbable but lets not take shortcuts */ switch_mutex_lock(agent->replies_mutex); switch_thread_rwlock_unlock(agent->lock); if (!reply.xml_str) { switch_time_t timeout; timeout = switch_micro_time_now() + 3000000; while (switch_micro_time_now() < timeout) { /* unlock the replies list and go to sleep, calculate a three second timeout before we started the loop * plus 100ms to add a little hysteresis between the timeout and the while loop */ switch_thread_cond_timedwait(agent->new_reply, agent->replies_mutex, (timeout - switch_micro_time_now() + 100000)); /* if we woke up (and therefore have locked replies again) check if we got our reply * otherwise we either timed-out (the while condition will fail) or one of * our sibling processes got a reply and we should go back to sleep */ if (reply.xml_str) { break; } } } /* find our reply placeholder in the linked list and remove it */ pending = agent->replies; while (pending != NULL) { if (pending->uuid_str == reply.uuid_str) { break; } prev = pending; pending = pending->next; } if (pending) { if (!prev) { agent->replies = reply.next; } else { prev->next = reply.next; } } /* we are done with the replies link-list */ switch_mutex_unlock(agent->replies_mutex); /* after all that did we get what we were after?! */ if (reply.xml_str) { /* HELL YA WE DID */ reply.xml_str = expand_vars(reply.xml_str); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Received %s XML (%s) after %dms: %s\n" ,section ,reply.uuid_str ,(unsigned int) (switch_micro_time_now() - now) / 1000 ,reply.xml_str); xml = switch_xml_parse_str_dynamic(reply.xml_str, SWITCH_FALSE); } else { /* facepalm */ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Request for %s XML (%s) timed-out after %dms\n" ,section ,reply.uuid_str ,(unsigned int) (switch_micro_time_now() - now) / 1000); } return xml; }
static void *SWITCH_THREAD_FUNC api_exec(switch_thread_t *thread, void *obj) { switch_bool_t r = SWITCH_TRUE; struct api_command_struct *acs = (struct api_command_struct *) obj; switch_stream_handle_t stream = { 0 }; char *reply, *freply = NULL; switch_status_t status; if (!acs) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Internal error.\n"); return NULL; } if (!acs->listener || !acs->listener->rwlock || switch_thread_rwlock_tryrdlock(acs->listener->rwlock) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error! cannot get read lock.\n"); goto done; } SWITCH_STANDARD_STREAM(stream); if ((status = switch_api_execute(acs->api_cmd, acs->arg, NULL, &stream)) == SWITCH_STATUS_SUCCESS) { reply = stream.data; } else { freply = switch_mprintf("%s: Command not found!\n", acs->api_cmd); reply = freply; r = SWITCH_FALSE; } if (!reply) { reply = "Command returned no output!"; r = SWITCH_FALSE; } if (*reply == '-') r = SWITCH_FALSE; if (acs->bg) { switch_event_t *event; if (switch_event_create(&event, SWITCH_EVENT_BACKGROUND_JOB) == SWITCH_STATUS_SUCCESS) { ei_x_buff ebuf; switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Job-UUID", acs->uuid_str); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Job-Command", acs->api_cmd); ei_x_new_with_version(&ebuf); if (acs->arg) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Job-Command-Arg", acs->arg); } switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Job-Successful", r ? "true" : "false"); switch_event_add_body(event, "%s", reply); switch_event_fire(&event); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Sending bgapi reply to %s\n", acs->pid.node); ei_x_encode_tuple_header(&ebuf, 3); if (r) ei_x_encode_atom(&ebuf, "bgok"); else ei_x_encode_atom(&ebuf, "bgerror"); _ei_x_encode_string(&ebuf, acs->uuid_str); _ei_x_encode_string(&ebuf, reply); switch_mutex_lock(acs->listener->sock_mutex); ei_send(acs->listener->sockfd, &acs->pid, ebuf.buff, ebuf.index); switch_mutex_unlock(acs->listener->sock_mutex); #ifdef EI_DEBUG ei_x_print_msg(&ebuf, &acs->pid, 1); #endif ei_x_free(&ebuf); } } else { ei_x_buff rbuf; ei_x_new_with_version(&rbuf); ei_x_encode_tuple_header(&rbuf, 2); if (!strlen(reply)) { reply = "Command returned no output!"; r = SWITCH_FALSE; } if (r) { ei_x_encode_atom(&rbuf, "ok"); } else { ei_x_encode_atom(&rbuf, "error"); } _ei_x_encode_string(&rbuf, reply); switch_mutex_lock(acs->listener->sock_mutex); ei_send(acs->listener->sockfd, &acs->pid, rbuf.buff, rbuf.index); switch_mutex_unlock(acs->listener->sock_mutex); #ifdef EI_DEBUG ei_x_print_msg(&rbuf, &acs->pid, 1); #endif ei_x_free(&rbuf); } switch_safe_free(stream.data); switch_safe_free(freply); if (acs->listener->rwlock) { switch_thread_rwlock_unlock(acs->listener->rwlock); } done: if (acs->bg) { switch_memory_pool_t *pool = acs->pool; acs = NULL; switch_core_destroy_memory_pool(&pool); pool = NULL; } return NULL; }
static switch_status_t handle_msg_fetch_reply(listener_t *listener, ei_x_buff * buf, ei_x_buff * rbuf) { char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; fetch_reply_t *p; if (ei_decode_string_or_binary(buf->buff, &buf->index, SWITCH_UUID_FORMATTED_LENGTH, uuid_str)) { ei_x_encode_tuple_header(rbuf, 2); ei_x_encode_atom(rbuf, "error"); ei_x_encode_atom(rbuf, "badarg"); } else { ei_x_buff *nbuf = malloc(sizeof(nbuf)); nbuf->buff = malloc(buf->buffsz); memcpy(nbuf->buff, buf->buff, buf->buffsz); nbuf->index = buf->index; nbuf->buffsz = buf->buffsz; switch_mutex_lock(globals.fetch_reply_mutex); if ((p = switch_core_hash_find(globals.fetch_reply_hash, uuid_str))) { /* Get the status and release the lock ASAP. */ enum { is_timeout, is_waiting, is_filled } status; if (p->state == reply_not_ready) { switch_thread_cond_wait(p->ready_or_found, globals.fetch_reply_mutex); } if (p->state == reply_waiting) { /* update the key with a reply */ status = is_waiting; p->reply = nbuf; p->state = reply_found; strncpy(p->winner, listener->peer_nodename, MAXNODELEN); switch_thread_cond_broadcast(p->ready_or_found); } else if (p->state == reply_timeout) { status = is_timeout; } else { status = is_filled; } put_reply_unlock(p, uuid_str); /* Relay the status back to the fetch responder. */ if (status == is_waiting) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Found waiting slot for %s\n", uuid_str); ei_x_encode_tuple_header(rbuf, 2); ei_x_encode_atom(rbuf, "ok"); _ei_x_encode_string(rbuf, uuid_str); /* Return here to avoid freeing the reply. */ return SWITCH_STATUS_SUCCESS; } else if (status == is_timeout) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Handler for %s timed out\n", uuid_str); ei_x_encode_tuple_header(rbuf, 3); ei_x_encode_atom(rbuf, "error"); _ei_x_encode_string(rbuf, uuid_str); ei_x_encode_atom(rbuf, "timeout"); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Found filled slot for %s\n", uuid_str); ei_x_encode_tuple_header(rbuf, 3); ei_x_encode_atom(rbuf, "error"); _ei_x_encode_string(rbuf, uuid_str); ei_x_encode_atom(rbuf, "duplicate_response"); } } else { /* nothing in the hash */ switch_mutex_unlock(globals.fetch_reply_mutex); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Empty slot for %s\n", uuid_str); ei_x_encode_tuple_header(rbuf, 2); ei_x_encode_atom(rbuf, "error"); ei_x_encode_atom(rbuf, "invalid_uuid"); } switch_safe_free(nbuf->buff); switch_safe_free(nbuf); } return SWITCH_STATUS_SUCCESS; }
static switch_status_t handle_msg_fetch_reply(listener_t *listener, ei_x_buff * buf, ei_x_buff * rbuf) { char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; fetch_reply_t *p; if (ei_decode_string_or_binary(buf->buff, &buf->index, SWITCH_UUID_FORMATTED_LENGTH, uuid_str)) { ei_x_encode_tuple_header(rbuf, 2); ei_x_encode_atom(rbuf, "error"); ei_x_encode_atom(rbuf, "badarg"); } else { /* TODO - maybe use a rwlock instead */ if ((p = switch_core_hash_find_locked(globals.fetch_reply_hash, uuid_str, globals.fetch_reply_mutex))) { /* try to lock the mutex, so no other responder can */ if (switch_mutex_trylock(p->mutex) == SWITCH_STATUS_SUCCESS) { if (p->state == reply_waiting) { /* alright, we've got the lock and we're the first to reply */ /* clone the reply so it doesn't get destroyed on us */ ei_x_buff *nbuf = malloc(sizeof(*nbuf)); nbuf->buff = malloc(buf->buffsz); memcpy(nbuf->buff, buf->buff, buf->buffsz); nbuf->index = buf->index; nbuf->buffsz = buf->buffsz; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Got reply for %s\n", uuid_str); /* copy info into the reply struct */ p->state = reply_found; p->reply = nbuf; strncpy(p->winner, listener->peer_nodename, MAXNODELEN); /* signal waiting thread that its time to wake up */ switch_thread_cond_signal(p->ready_or_found); /* reply OK */ ei_x_encode_tuple_header(rbuf, 2); ei_x_encode_atom(rbuf, "ok"); _ei_x_encode_string(rbuf, uuid_str); /* unlock */ switch_mutex_unlock(p->mutex); } else { if (p->state == reply_found) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Reply for already complete request %s\n", uuid_str); ei_x_encode_tuple_header(rbuf, 3); ei_x_encode_atom(rbuf, "error"); _ei_x_encode_string(rbuf, uuid_str); ei_x_encode_atom(rbuf, "duplicate_response"); } else if (p->state == reply_timeout) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Reply for timed out request %s\n", uuid_str); ei_x_encode_tuple_header(rbuf, 3); ei_x_encode_atom(rbuf, "error"); _ei_x_encode_string(rbuf, uuid_str); ei_x_encode_atom(rbuf, "timeout"); } else if (p->state == reply_not_ready) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Request %s is not ready?!\n", uuid_str); ei_x_encode_tuple_header(rbuf, 3); ei_x_encode_atom(rbuf, "error"); _ei_x_encode_string(rbuf, uuid_str); ei_x_encode_atom(rbuf, "not_ready"); } switch_mutex_unlock(p->mutex); } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Could not lock mutex for reply %s\n", uuid_str); ei_x_encode_tuple_header(rbuf, 3); ei_x_encode_atom(rbuf, "error"); _ei_x_encode_string(rbuf, uuid_str); ei_x_encode_atom(rbuf, "duplicate_response"); } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Could not find request for reply %s\n", uuid_str); ei_x_encode_tuple_header(rbuf, 2); ei_x_encode_atom(rbuf, "error"); ei_x_encode_atom(rbuf, "invalid_uuid"); } } return SWITCH_STATUS_SUCCESS; }