Index: include/asterisk/manager.h =================================================================== --- include/asterisk/manager.h (revision 180682) +++ include/asterisk/manager.h (working copy) @@ -106,6 +106,15 @@ */ void ast_manager_unregister_hook(struct manager_custom_hook *hook); + +/*! Registered hooks can call this function to invoke actions and they will receive responses through registered callback + \param int hookid the file identifier specified in manager_custom_hook struct when registering a hook + \param char *msg ami action mesage string e.g. "Action: SipPeers\r\n" + \return 0 on Success +*/ +int hook_send_action(int hookid, char *msg); + + struct mansession; struct message { Index: main/manager.c =================================================================== --- main/manager.c (revision 180682) +++ main/manager.c (working copy) @@ -219,6 +219,7 @@ struct mansession_session *session; FILE *f; int fd; + int ishook; /* this flag will tell us if mansession was created by a call to hook_send_action */ }; #define NEW_EVENT(m) (AST_LIST_NEXT(m->session->last_ev, eq_next)) @@ -936,13 +937,106 @@ return head; } +/* access for hooks to send action messages to ami */ + +int hook_send_action(int hookid, char *msg) +{ + const char *action; + int ret = 0; + struct manager_custom_hook *hook; + struct manager_action *tmp; + struct mansession_session *session = ast_calloc(1, sizeof(*session)); + struct mansession s = {.session = NULL, }; + struct message m = { 0 }; + char header_buf[sizeof(session->inbuf)] = { '\0' }; + char *src = msg; + int x = 0; + + /* verify if this hookid exists */ + AST_RWLIST_RDLOCK(&manager_hooks); + AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) { + if (hook->file == (void*)hookid) { + x = 1; + break; + } + } + AST_RWLIST_UNLOCK(&manager_hooks); + if (!x) + return -1; + + /* convert msg string to message struct */ + int curlen = strlen(msg); + for (x = 0; x < curlen; x++) { + int cr; /* set if we have \r */ + if (src[x] == '\r' && x+1 < curlen && src[x+1] == '\n') + cr = 2; /* Found. Update length to include \r\n */ + else if (src[x] == '\n') + cr = 1; /* also accept \n only */ + else + continue; + /* don't copy empty lines */ + if (x) { + memmove(header_buf, src, x); /*... but trim \r\n */ + header_buf[x] = '\0'; /* terminate the string */ + m.headers[m.hdrcount++] = ast_strdupa(header_buf); + } + x += cr; + curlen -= x; /* remaining size */ + src += x; /* update pointer */ + x = -1; /* reset loop */ + } + + action = astman_get_header(&m,"Action"); + if (action && strcasecmp(action,"login")) { + + AST_RWLIST_RDLOCK(&actions); + AST_RWLIST_TRAVERSE(&actions, tmp, list) { + if (strcasecmp(action, tmp->action)) + continue; + /* + * we have to simulate a session for this action request + * to be able to pass it down for processing + * This is necessary to meet the previous design of manager.c + */ + s.ishook = 1; + s.fd = hookid; + s.f = (void*)1; /* set this to something so our request will make it through all functions that test it*/ + + ret = tmp->func(&s, &m); + break; + } + AST_RWLIST_UNLOCK(&actions); + } + return ret; +} + /*! * helper function to send a string to the socket. * Return -1 on error (e.g. buffer full). */ static int send_string(struct mansession *s, char *string) { - if (s->f) { + struct manager_custom_hook *hook; + char *str; + /* It's a reslut from one of the hook's action invocation */ + if (s->ishook) + { + AST_RWLIST_RDLOCK(&manager_hooks); + AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) { + if (hook->file == (void*)s->fd) { + /* + * to send responses, we're using the same function + * as for receiving events. We call the event "HookResponse" + */ + str = ast_strdupa(string); + hook->helper((int)hook->file, "HookResponse",str); + break; + } + } + AST_RWLIST_UNLOCK(&manager_hooks); + return 0; + } + else if (s->f) { return ast_careful_fwrite(s->f, s->fd, string, strlen(string), s->session->writetimeout); } else { return ast_careful_fwrite(s->session->f, s->session->fd, string, strlen(string), s->session->writetimeout); @@ -3279,9 +3373,6 @@ struct timeval now; struct ast_str *buf; - /* Abort if there aren't any manager sessions */ - if (!num_sessions) - return 0; if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE))) return -1; @@ -3312,28 +3403,37 @@ ast_str_append(&buf, 0, "\r\n"); - append_event(ast_str_buffer(buf), category); + /* + * Skip sending of event if there aren't any manager sessions + */ + if (num_sessions) { - /* Wake up any sleeping sessions */ - AST_LIST_LOCK(&sessions); - AST_LIST_TRAVERSE(&sessions, session, list) { - ast_mutex_lock(&session->__lock); - if (session->waiting_thread != AST_PTHREADT_NULL) - pthread_kill(session->waiting_thread, SIGURG); - else + append_event(buf->str, category); + + /* Wake up any sleeping sessions */ + AST_LIST_LOCK(&sessions); + AST_LIST_TRAVERSE(&sessions, session, list) { + ast_mutex_lock(&session->__lock); + if (session->waiting_thread != AST_PTHREADT_NULL) + pthread_kill(session->waiting_thread, SIGURG); + else /* We have an event to process, but the mansession is * not waiting for it. We still need to indicate that there * is an event waiting so that get_input processes the pending * event instead of polling. */ - session->pending_event = 1; - ast_mutex_unlock(&session->__lock); + session->pending_event = 1; + ast_mutex_unlock(&session->__lock); + } + AST_LIST_UNLOCK(&sessions); } - AST_LIST_UNLOCK(&sessions); + /* + * but, proceed to inform any registered hooks of the event + */ AST_RWLIST_RDLOCK(&manager_hooks); AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) { - hook->helper(category, event, ast_str_buffer(buf)); + hook->helper(category, event, buf->str); } AST_RWLIST_UNLOCK(&manager_hooks);