static switch_status_t handle_msg_sendmsg(listener_t *listener, int arity, ei_x_buff * buf, ei_x_buff * rbuf) { char uuid[SWITCH_UUID_FORMATTED_LENGTH + 1]; int headerlength; if (ei_decode_string_or_binary(buf->buff, &buf->index, SWITCH_UUID_FORMATTED_LENGTH, uuid) || ei_decode_list_header(buf->buff, &buf->index, &headerlength)) { ei_x_encode_tuple_header(rbuf, 2); ei_x_encode_atom(rbuf, "error"); ei_x_encode_atom(rbuf, "badarg"); } else { switch_core_session_t *session; if (!zstr_buf(uuid) && (session = switch_core_session_locate(uuid))) { switch_event_t *event; if (switch_event_create(&event, SWITCH_EVENT_SEND_MESSAGE) == SWITCH_STATUS_SUCCESS) { char key[1024]; char value[1024]; int i = 0; switch_bool_t fail = SWITCH_FALSE; while (!ei_decode_tuple_header(buf->buff, &buf->index, &arity) && arity == 2) { i++; if (ei_decode_string(buf->buff, &buf->index, key) || ei_decode_string(buf->buff, &buf->index, value)) { fail = SWITCH_TRUE; break; } switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, key, value); } if (headerlength != i || fail) { ei_x_encode_tuple_header(rbuf, 2); ei_x_encode_atom(rbuf, "error"); ei_x_encode_atom(rbuf, "badarg"); } else { if (switch_core_session_queue_private_event(session, &event, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) { ei_x_encode_atom(rbuf, "ok"); } else { ei_x_encode_tuple_header(rbuf, 2); ei_x_encode_atom(rbuf, "error"); ei_x_encode_atom(rbuf, "badmem"); } } } /* release the lock returned by session locate */ switch_core_session_rwunlock(session); } else { ei_x_encode_tuple_header(rbuf, 2); ei_x_encode_atom(rbuf, "error"); ei_x_encode_atom(rbuf, "nosession"); } } return SWITCH_STATUS_SUCCESS; }
static switch_status_t handle_msg_api(listener_t *listener, erlang_msg * msg, int arity, ei_x_buff * buf, ei_x_buff * rbuf) { char api_cmd[MAXATOMLEN]; int type; int size; char *arg; switch_bool_t fail = SWITCH_FALSE; if (arity < 3) { fail = SWITCH_TRUE; } ei_get_type(buf->buff, &buf->index, &type, &size); if ((size > (sizeof(api_cmd) - 1)) || ei_decode_atom(buf->buff, &buf->index, api_cmd)) { fail = SWITCH_TRUE; } ei_get_type(buf->buff, &buf->index, &type, &size); arg = malloc(size + 1); if (ei_decode_string_or_binary(buf->buff, &buf->index, size, arg)) { fail = SWITCH_TRUE; } if (!fail) { struct api_command_struct acs = { 0 }; acs.listener = listener; acs.api_cmd = api_cmd; acs.arg = arg; acs.bg = 0; acs.pid = msg->from; api_exec(NULL, (void *) &acs); switch_safe_free(arg); /* don't reply */ return SWITCH_STATUS_FALSE; } else { ei_x_encode_tuple_header(rbuf, 2); ei_x_encode_atom(rbuf, "error"); ei_x_encode_atom(rbuf, "badarg"); return SWITCH_STATUS_SUCCESS; } }
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; }
/* {handlecall,<uuid>,<handler process registered name>} or {handlecall,<uuid>} to send messages back to the sender */ static switch_status_t handle_msg_handlecall(listener_t *listener, erlang_msg * msg, int arity, ei_x_buff * buf, ei_x_buff * rbuf) { char reg_name[MAXATOMLEN]; char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; if (arity < 2 || arity > 3 || (arity == 3 && ei_decode_atom(buf->buff, &buf->index, reg_name)) || 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 { switch_core_session_t *session; if (!zstr_buf(uuid_str)) { if ((session = switch_core_session_locate(uuid_str))) { /* create a new session list element and attach it to this listener */ if ((arity == 2 && attach_call_to_pid(listener, &msg->from, session)) || (arity == 3 && attach_call_to_registered_process(listener, reg_name, session))) { ei_x_encode_atom(rbuf, "ok"); } else { ei_x_encode_tuple_header(rbuf, 2); ei_x_encode_atom(rbuf, "error"); ei_x_encode_atom(rbuf, "session_attach_failed"); } /* release the lock returned by session locate */ switch_core_session_rwunlock(session); } else { ei_x_encode_tuple_header(rbuf, 2); ei_x_encode_atom(rbuf, "error"); ei_x_encode_atom(rbuf, "badsession"); } } else { ei_x_encode_tuple_header(rbuf, 2); ei_x_encode_atom(rbuf, "error"); ei_x_encode_atom(rbuf, "baduuid"); } } 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 { 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; }