Exemplo n.º 1
0
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);
}
Exemplo n.º 2
0
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;
}
Exemplo n.º 3
0
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;
}
Exemplo n.º 4
0
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;

}
Exemplo n.º 5
0
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;
}
Exemplo n.º 6
0
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;
}