Main Page | Alphabetical List | Data Structures | File List | Data Fields | Globals

manager.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- A telephony toolkit for Linux.
00003  *
00004  * Channel Management and more
00005  * 
00006  * Copyright (C) 1999, Mark Spencer
00007  *
00008  * Mark Spencer <markster@linux-support.net>
00009  *
00010  * This program is free software, distributed under the terms of
00011  * the GNU General Public License
00012  */
00013 
00014 #include <stdio.h>
00015 #include <stdlib.h>
00016 #include <pthread.h>
00017 #include <string.h>
00018 #include <sys/time.h>
00019 #include <sys/types.h>
00020 #include <netdb.h>
00021 #include <sys/socket.h>
00022 #include <netinet/in.h>
00023 #include <netinet/tcp.h>
00024 #include <arpa/inet.h>
00025 #include <signal.h>
00026 #include <errno.h>
00027 #include <unistd.h>
00028 #include <asterisk/channel.h>
00029 #include <asterisk/file.h>
00030 #include <asterisk/manager.h>
00031 #include <asterisk/config.h>
00032 #include <asterisk/lock.h>
00033 #include <asterisk/logger.h>
00034 #include <asterisk/options.h>
00035 #include <asterisk/cli.h>
00036 #include <asterisk/app.h>
00037 #include <asterisk/pbx.h>
00038 #include <asterisk/md5.h>
00039 #include <asterisk/acl.h>
00040 
00041 static int enabled = 0;
00042 static int portno = DEFAULT_MANAGER_PORT;
00043 static int asock = -1;
00044 static pthread_t t;
00045 static ast_mutex_t sessionlock = AST_MUTEX_INITIALIZER;
00046 
00047 static struct permalias {
00048    int num;
00049    char *label;
00050 } perms[] = {
00051    { EVENT_FLAG_SYSTEM, "system" },
00052    { EVENT_FLAG_CALL, "call" },
00053    { EVENT_FLAG_LOG, "log" },
00054    { EVENT_FLAG_VERBOSE, "verbose" },
00055    { EVENT_FLAG_COMMAND, "command" },
00056    { EVENT_FLAG_AGENT, "agent" },
00057    { EVENT_FLAG_USER, "user" },
00058    { -1, "all" },
00059 };
00060 
00061 static struct mansession *sessions = NULL;
00062 static struct manager_action *first_action = NULL;
00063 static ast_mutex_t actionlock = AST_MUTEX_INITIALIZER;
00064 
00065 static int handle_showmancmds(int fd, int argc, char *argv[])
00066 {
00067    struct manager_action *cur = first_action;
00068    char *format = "  %-15.15s  %-45.45s\n";
00069 
00070    ast_mutex_lock(&actionlock);
00071    ast_cli(fd, format, "Action", "Synopsis");
00072    while(cur) { /* Walk the list of actions */
00073       ast_cli(fd, format, cur->action, cur->synopsis);
00074       cur = cur->next;
00075    }
00076 
00077    ast_mutex_unlock(&actionlock);
00078    return RESULT_SUCCESS;
00079 }
00080 
00081 static int handle_showmanconn(int fd, int argc, char *argv[])
00082 {
00083    struct mansession *s;
00084    char *format = "  %-15.15s  %-15.15s\n";
00085    ast_mutex_lock(&sessionlock);
00086    s = sessions;
00087    ast_cli(fd, format, "Username", "IP Address");
00088    while(s) {
00089       ast_cli(fd, format,s->username, inet_ntoa(s->sin.sin_addr));
00090       s = s->next;
00091    }
00092 
00093    ast_mutex_unlock(&sessionlock);
00094    return RESULT_SUCCESS;
00095 }
00096 
00097 static char showmancmds_help[] = 
00098 "Usage: show manager commands\n"
00099 "  Prints a listing of all the available manager commands.\n";
00100 
00101 static char showmanconn_help[] = 
00102 "Usage: show manager connected\n"
00103 "  Prints a listing of the users that are connected to the\n"
00104 "manager interface.\n";
00105 
00106 static struct ast_cli_entry show_mancmds_cli =
00107    { { "show", "manager", "commands", NULL },
00108    handle_showmancmds, "Show manager commands", showmancmds_help };
00109 
00110 static struct ast_cli_entry show_manconn_cli =
00111    { { "show", "manager", "connected", NULL },
00112    handle_showmanconn, "Show connected manager users", showmanconn_help };
00113 
00114 static void destroy_session(struct mansession *s)
00115 {
00116    struct mansession *cur, *prev = NULL;
00117    ast_mutex_lock(&sessionlock);
00118    cur = sessions;
00119    while(cur) {
00120       if (cur == s)
00121          break;
00122       prev = cur;
00123       cur = cur->next;
00124    }
00125    if (cur) {
00126       if (prev)
00127          prev->next = cur->next;
00128       else
00129          sessions = cur->next;
00130       if (s->fd > -1)
00131          close(s->fd);
00132       free(s);
00133    } else
00134       ast_log(LOG_WARNING, "Trying to delete non-existant session %p?\n", s);
00135    ast_mutex_unlock(&sessionlock);
00136    
00137 }
00138 
00139 char *astman_get_header(struct message *m, char *var)
00140 {
00141    char cmp[80];
00142    int x;
00143    snprintf(cmp, sizeof(cmp), "%s: ", var);
00144    for (x=0;x<m->hdrcount;x++)
00145       if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
00146          return m->headers[x] + strlen(cmp);
00147    return "";
00148 }
00149 
00150 void astman_send_error(struct mansession *s, struct message *m, char *error)
00151 {
00152    char *id = astman_get_header(m,"ActionID");
00153    ast_mutex_lock(&s->lock);
00154    ast_cli(s->fd, "Response: Error\r\n");
00155    if (id && strlen(id))
00156       ast_cli(s->fd, "ActionID: %s\r\n",id);
00157    ast_cli(s->fd, "Message: %s\r\n\r\n", error);
00158    ast_mutex_unlock(&s->lock);
00159 }
00160 
00161 void astman_send_response(struct mansession *s, struct message *m, char *resp, char *msg)
00162 {
00163    char *id = astman_get_header(m,"ActionID");
00164    ast_mutex_lock(&s->lock);
00165    ast_cli(s->fd, "Response: %s\r\n", resp);
00166    if (id && strlen(id))
00167       ast_cli(s->fd, "ActionID: %s\r\n",id);
00168    if (msg)
00169       ast_cli(s->fd, "Message: %s\r\n\r\n", msg);
00170    else
00171       ast_cli(s->fd, "\r\n");
00172    ast_mutex_unlock(&s->lock);
00173 }
00174 
00175 void astman_send_ack(struct mansession *s, struct message *m, char *msg)
00176 {
00177    astman_send_response(s, m, "Success", msg);
00178 }
00179 
00180 static int get_perm(char *instr)
00181 {
00182    char tmp[256];
00183    char *c;
00184    int x;
00185    int ret = 0;
00186    char *stringp=NULL;
00187    if (!instr)
00188       return 0;
00189    strncpy(tmp, instr, sizeof(tmp) - 1);
00190    stringp=tmp;
00191    c = strsep(&stringp, ",");
00192    while(c) {
00193       for (x=0;x<sizeof(perms) / sizeof(perms[0]);x++) {
00194          if (!strcasecmp(perms[x].label, c)) 
00195             ret |= perms[x].num;
00196       }
00197       c = strsep(&stringp, ",");
00198    }
00199    return ret;
00200 }
00201 
00202 static int authenticate(struct mansession *s, struct message *m)
00203 {
00204    struct ast_config *cfg;
00205    char *cat;
00206    char *user = astman_get_header(m, "Username");
00207    char *pass = astman_get_header(m, "Secret");
00208    char *authtype = astman_get_header(m, "AuthType");
00209    char *key = astman_get_header(m, "Key");
00210 
00211    cfg = ast_load("manager.conf");
00212    if (!cfg)
00213       return -1;
00214    cat = ast_category_browse(cfg, NULL);
00215    while(cat) {
00216       if (strcasecmp(cat, "general")) {
00217          /* This is a user */
00218          if (!strcasecmp(cat, user)) {
00219             struct ast_variable *v;
00220             struct ast_ha *ha = NULL;
00221             char *password = NULL;
00222             v = ast_variable_browse(cfg, cat);
00223             while (v) {
00224                if (!strcasecmp(v->name, "secret")) {
00225                   password = v->value;
00226                } else if (!strcasecmp(v->name, "permit") ||
00227                      !strcasecmp(v->name, "deny")) {
00228                      ha = ast_append_ha(v->name, v->value, ha);
00229                }                 
00230                v = v->next;
00231             }
00232             if (ha && !ast_apply_ha(ha, &(s->sin))) {
00233                ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", inet_ntoa(s->sin.sin_addr), user);
00234                ast_free_ha(ha);
00235                ast_destroy(cfg);
00236                return -1;
00237             } else if (ha)
00238                ast_free_ha(ha);
00239             if (!strcasecmp(authtype, "MD5")) {
00240                if (key && strlen(key) && s->challenge) {
00241                   int x;
00242                   int len=0;
00243                   char md5key[256] = "";
00244                   struct MD5Context md5;
00245                   unsigned char digest[16];
00246                   MD5Init(&md5);
00247                   MD5Update(&md5, s->challenge, strlen(s->challenge));
00248                   MD5Update(&md5, password, strlen(password));
00249                   MD5Final(digest, &md5);
00250                   for (x=0;x<16;x++)
00251                      len += sprintf(md5key + len, "%2.2x", digest[x]);
00252                   if (!strcmp(md5key, key))
00253                      break;
00254                   else {
00255                      ast_destroy(cfg);
00256                      return -1;
00257                   }
00258                }
00259             } else if (password && !strcasecmp(password, pass)) {
00260                break;
00261             } else {
00262                ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", inet_ntoa(s->sin.sin_addr), user);
00263                ast_destroy(cfg);
00264                return -1;
00265             }  
00266          }
00267       }
00268       cat = ast_category_browse(cfg, cat);
00269    }
00270    if (cat) {
00271       strncpy(s->username, cat, sizeof(s->username) - 1);
00272       s->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
00273       s->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
00274       ast_destroy(cfg);
00275       return 0;
00276    }
00277    ast_log(LOG_NOTICE, "%s tried to authenticate with non-existant user '%s'\n", inet_ntoa(s->sin.sin_addr), user);
00278    ast_destroy(cfg);
00279    return -1;
00280 }
00281 
00282 static int action_ping(struct mansession *s, struct message *m)
00283 {
00284    astman_send_response(s, m, "Pong", NULL);
00285    return 0;
00286 }
00287 
00288 static int action_logoff(struct mansession *s, struct message *m)
00289 {
00290    astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
00291    return -1;
00292 }
00293 
00294 static int action_hangup(struct mansession *s, struct message *m)
00295 {
00296    struct ast_channel *c = NULL;
00297    char *name = astman_get_header(m, "Channel");
00298    if (!strlen(name)) {
00299       astman_send_error(s, m, "No channel specified");
00300       return 0;
00301    }
00302    c = ast_channel_walk(NULL);
00303    while(c) {
00304       if (!strcasecmp(c->name, name)) {
00305          break;
00306       }
00307       c = ast_channel_walk(c);
00308    }
00309    if (!c) {
00310       astman_send_error(s, m, "No such channel");
00311       return 0;
00312    }
00313    ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
00314    astman_send_ack(s, m, "Channel Hungup");
00315    return 0;
00316 }
00317 
00318 static int action_status(struct mansession *s, struct message *m)
00319 {
00320    char *id = astman_get_header(m,"ActionID");
00321    char idText[256] = "";
00322    struct ast_channel *c;
00323    char bridge[256];
00324    astman_send_ack(s, m, "Channel status will follow");
00325    c = ast_channel_walk(NULL);
00326         if (id && strlen(id))
00327                 snprintf(idText,256,"ActionID: %s\r\n",id);
00328    while(c) {
00329       if (c->bridge)
00330          snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->bridge->name);
00331       else
00332          strcpy(bridge, "");
00333       if (c->pbx) {
00334          ast_cli(s->fd,
00335          "Event: Status\r\n"
00336          "Channel: %s\r\n"
00337          "CallerID: %s\r\n"
00338          "State: %s\r\n"
00339          "Context: %s\r\n"
00340          "Extension: %s\r\n"
00341          "Priority: %d\r\n"
00342          "%s"
00343          "Uniqueid: %s\r\n"
00344          "%s"
00345          "\r\n",
00346          c->name, c->callerid ? c->callerid : "<unknown>", 
00347          ast_state2str(c->_state), c->context,
00348          c->exten, c->priority, bridge, c->uniqueid, idText);
00349       } else {
00350          ast_cli(s->fd,
00351          "Event: Status\r\n"
00352          "Channel: %s\r\n"
00353          "CallerID: %s\r\n"
00354          "State: %s\r\n"
00355          "%s"
00356          "Uniqueid: %s\r\n"
00357          "%s"
00358          "\r\n",
00359          c->name, c->callerid ? c->callerid : "<unknown>", 
00360          ast_state2str(c->_state), bridge, c->uniqueid, idText);
00361       }
00362       c = ast_channel_walk(c);
00363    }
00364    ast_cli(s->fd,
00365    "Event: StatusComplete\r\n"
00366    "%s"
00367    "\r\n",idText);
00368    return 0;
00369 }
00370 
00371 static int action_redirect(struct mansession *s, struct message *m)
00372 {
00373    char *name = astman_get_header(m, "Channel");
00374    char *name2 = astman_get_header(m, "ExtraChannel");
00375    char *exten = astman_get_header(m, "Exten");
00376    char *context = astman_get_header(m, "Context");
00377    char *priority = astman_get_header(m, "Priority");
00378    int pi = 0;
00379    int res;
00380    if (!name || !strlen(name)) {
00381       astman_send_error(s, m, "Channel not specified");
00382       return 0;
00383    }
00384    if (strlen(priority) && (sscanf(priority, "%d", &pi) != 1)) {
00385       astman_send_error(s, m, "Invalid priority\n");
00386       return 0;
00387    }
00388    res = ast_async_goto_by_name(name, context, exten, pi);
00389    if (!res) {
00390       if (strlen(name2)) {
00391          res = ast_async_goto_by_name(name2, context, exten, pi);
00392          if (!res)
00393             astman_send_ack(s, m, "Dual Redirect successful");
00394          else
00395             astman_send_error(s, m, "Secondary redirect failed");
00396       } else
00397          astman_send_ack(s, m, "Redirect successful");
00398    } else
00399       astman_send_error(s, m, "Redirect failed");
00400    return 0;
00401 }
00402 
00403 static int action_command(struct mansession *s, struct message *m)
00404 {
00405    char *cmd = astman_get_header(m, "Command");
00406    ast_mutex_lock(&s->lock);
00407    s->blocking = 1;
00408    ast_mutex_unlock(&s->lock);
00409    ast_cli(s->fd, "Response: Follows\r\n");
00410    /* FIXME: Wedge a ActionID response in here, waiting for later changes */
00411    ast_cli_command(s->fd, cmd);
00412    ast_cli(s->fd, "--END COMMAND--\r\n\r\n");
00413    ast_mutex_lock(&s->lock);
00414    s->blocking = 0;
00415    ast_mutex_unlock(&s->lock);
00416    return 0;
00417 }
00418 
00419 static int action_originate(struct mansession *s, struct message *m)
00420 {
00421    char *name = astman_get_header(m, "Channel");
00422    char *exten = astman_get_header(m, "Exten");
00423    char *context = astman_get_header(m, "Context");
00424    char *priority = astman_get_header(m, "Priority");
00425    char *timeout = astman_get_header(m, "Timeout");
00426    char *callerid = astman_get_header(m, "CallerID");
00427       char *variable = astman_get_header(m, "Variable");
00428       char *account = astman_get_header(m, "Account");
00429    char *app = astman_get_header(m, "Application");
00430    char *appdata = astman_get_header(m, "Data");
00431    char *tech, *data;
00432    int pi = 0;
00433    int res;
00434    int to = 30000;
00435    int reason = 0;
00436    char tmp[256];
00437    if (!name) {
00438       astman_send_error(s, m, "Channel not specified");
00439       return 0;
00440    }
00441    if (strlen(priority) && (sscanf(priority, "%d", &pi) != 1)) {
00442       astman_send_error(s, m, "Invalid priority\n");
00443       return 0;
00444    }
00445    if (strlen(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
00446       astman_send_error(s, m, "Invalid timeout\n");
00447       return 0;
00448    }
00449    strncpy(tmp, name, sizeof(tmp) - 1);
00450    tech = tmp;
00451    data = strchr(tmp, '/');
00452    if (!data) {
00453       astman_send_error(s, m, "Invalid channel\n");
00454       return 0;
00455    }
00456    *data = '\0';
00457    data++;
00458    if (strlen(app)) {
00459          res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 0, strlen(callerid) ? callerid : NULL, variable, account);
00460       } else {
00461          res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 0, strlen(callerid) ? callerid : NULL, variable, account);
00462    }   
00463    if (!res)
00464       astman_send_ack(s, m, "Originate successfully queued");
00465    else
00466       astman_send_error(s, m, "Originate failed");
00467    return 0;
00468 }
00469 
00470 static int action_mailboxstatus(struct mansession *s, struct message *m)
00471 {
00472    char *mailbox = astman_get_header(m, "Mailbox");
00473    char *id = astman_get_header(m,"ActionID");
00474    char idText[256] = "";
00475    if (!mailbox || !strlen(mailbox)) {
00476       astman_send_error(s, m, "Mailbox not specified");
00477       return 0;
00478    }
00479         if (id && strlen(id))
00480                 snprintf(idText,256,"ActionID: %s\r\n",id);
00481    ast_cli(s->fd, "Response: Success\r\n"
00482                "%s"
00483                "Message: Mailbox Status\r\n"
00484                "Mailbox: %s\r\n"
00485                "Waiting: %d\r\n\r\n", idText, mailbox, ast_app_has_voicemail(mailbox));
00486    return 0;
00487 }
00488 
00489 static int action_mailboxcount(struct mansession *s, struct message *m)
00490 {
00491    char *mailbox = astman_get_header(m, "Mailbox");
00492    char *id = astman_get_header(m,"ActionID");
00493    char idText[256] = "";
00494    int newmsgs = 0, oldmsgs = 0;
00495    if (!mailbox || !strlen(mailbox)) {
00496       astman_send_error(s, m, "Mailbox not specified");
00497       return 0;
00498    }
00499    ast_app_messagecount(mailbox, &newmsgs, &oldmsgs);
00500         if (id && strlen(id)) {
00501                 snprintf(idText,256,"ActionID: %s\r\n",id);
00502         }
00503    ast_cli(s->fd, "Response: Success\r\n"
00504                "%s"
00505                "Message: Mailbox Message Count\r\n"
00506                "Mailbox: %s\r\n"
00507                "NewMessages: %d\r\n"
00508                "OldMessages: %d\r\n" 
00509                "\r\n",
00510                 idText,mailbox, newmsgs, oldmsgs);
00511    return 0;
00512 }
00513 
00514 static int action_extensionstate(struct mansession *s, struct message *m)
00515 {
00516    char *exten = astman_get_header(m, "Exten");
00517    char *context = astman_get_header(m, "Context");
00518    char *id = astman_get_header(m,"ActionID");
00519    char idText[256] = "";
00520    char hint[256] = "";
00521    int status;
00522    if (!exten || !strlen(exten)) {
00523       astman_send_error(s, m, "Extension not specified");
00524       return 0;
00525    }
00526    if (!context || !strlen(context))
00527       context = "default";
00528    status = ast_extension_state(NULL, context, exten);
00529    ast_get_hint(hint, sizeof(hint) - 1, NULL, context, exten);
00530         if (id && strlen(id)) {
00531                 snprintf(idText,256,"ActionID: %s\r\n",id);
00532         }
00533    ast_cli(s->fd, "Response: Success\r\n"
00534                     "%s"
00535                "Message: Extension Status\r\n"
00536                "Exten: %s\r\n"
00537                "Context: %s\r\n"
00538                "Hint: %s\r\n"
00539                "Status: %d\r\n\r\n",
00540                idText,exten, context, hint, status);
00541    return 0;
00542 }
00543 
00544 static int action_timeout(struct mansession *s, struct message *m)
00545 {
00546    struct ast_channel *c = NULL;
00547    char *name = astman_get_header(m, "Channel");
00548    int timeout = atoi(astman_get_header(m, "Timeout"));
00549    if (!strlen(name)) {
00550       astman_send_error(s, m, "No channel specified");
00551       return 0;
00552    }
00553    if (!timeout) {
00554       astman_send_error(s, m, "No timeout specified");
00555       return 0;
00556    }
00557    c = ast_channel_walk(NULL);
00558    while(c) {
00559       if (!strcasecmp(c->name, name)) {
00560          break;
00561       }
00562       c = ast_channel_walk(c);
00563    }
00564    if (!c) {
00565       astman_send_error(s, m, "No such channel");
00566       return 0;
00567    }
00568    ast_channel_setwhentohangup(c, timeout);
00569    astman_send_ack(s, m, "Timeout Set");
00570    return 0;
00571 }
00572 
00573 static int process_message(struct mansession *s, struct message *m)
00574 {
00575    char action[80];
00576    struct manager_action *tmp = first_action;
00577    char *id = astman_get_header(m,"ActionID");
00578    char idText[256] = "";
00579 
00580    strncpy(action, astman_get_header(m, "Action"), sizeof(action));
00581    ast_log( LOG_DEBUG, "Manager received command '%s'\n", action );
00582 
00583    if (!strlen(action)) {
00584       astman_send_error(s, m, "Missing action in request");
00585       return 0;
00586    }
00587         if (id && strlen(id)) {
00588                 snprintf(idText,256,"ActionID: %s\r\n",id);
00589         }
00590    if (!s->authenticated) {
00591       if (!strcasecmp(action, "Challenge")) {
00592          char *authtype;
00593          authtype = astman_get_header(m, "AuthType");
00594          if (!strcasecmp(authtype, "MD5")) {
00595             if (!s->challenge || !strlen(s->challenge)) {
00596                ast_mutex_lock(&s->lock);
00597                snprintf(s->challenge, sizeof(s->challenge), "%d", rand());
00598                ast_mutex_unlock(&s->lock);
00599             }
00600             ast_cli(s->fd, "Response: Success\r\n"
00601                   "%s"
00602                   "Challenge: %s\r\n\r\n",
00603                   idText,s->challenge);
00604             return 0;
00605          } else {
00606             astman_send_error(s, m, "Must specify AuthType");
00607             return 0;
00608          }
00609       } else if (!strcasecmp(action, "Login")) {
00610          if (authenticate(s, m)) {
00611             sleep(1);
00612             astman_send_error(s, m, "Authentication failed");
00613             return -1;
00614          } else {
00615             s->authenticated = 1;
00616             if (option_verbose > 1) 
00617                ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged on from %s\n", s->username, inet_ntoa(s->sin.sin_addr));
00618             ast_log(LOG_EVENT, "Manager '%s' logged on from %s\n", s->username, inet_ntoa(s->sin.sin_addr));
00619             astman_send_ack(s, m, "Authentication accepted");
00620          }
00621       } else if (!strcasecmp(action, "Logoff")) {
00622          astman_send_ack(s, m, "See ya");
00623          return -1;
00624       } else
00625          astman_send_error(s, m, "Authentication Required");
00626    } else {
00627       while( tmp ) {       
00628          if (!strcasecmp(action, tmp->action)) {
00629             if ((s->writeperm & tmp->authority) == tmp->authority) {
00630                if (tmp->func(s, m))
00631                   return -1;
00632             } else {
00633                astman_send_error(s, m, "Permission denied");
00634             }
00635             return 0;
00636          }
00637          tmp = tmp->next;
00638       }
00639       astman_send_error(s, m, "Invalid/unknown command");
00640    }
00641    return 0;
00642 }
00643 
00644 static int get_input(struct mansession *s, char *output)
00645 {
00646    /* output must have at least sizeof(s->inbuf) space */
00647    int res;
00648    int x;
00649    fd_set fds;
00650    for (x=1;x<s->inlen;x++) {
00651       if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
00652          /* Copy output data up to and including \r\n */
00653          memcpy(output, s->inbuf, x + 1);
00654          /* Add trailing \0 */
00655          output[x+1] = '\0';
00656          /* Move remaining data back to the front */
00657          memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
00658          s->inlen -= (x + 1);
00659          return 1;
00660       }
00661    } 
00662    if (s->inlen >= sizeof(s->inbuf) - 1) {
00663       ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf);
00664       s->inlen = 0;
00665    }
00666    FD_ZERO(&fds);
00667    FD_SET(s->fd, &fds);
00668    res = ast_select(s->fd + 1, &fds, NULL, NULL, NULL);
00669    if (res < 0) {
00670       ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
00671    } else if (res > 0) {
00672       ast_mutex_lock(&s->lock);
00673       res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
00674       ast_mutex_unlock(&s->lock);
00675       if (res < 1)
00676          return -1;
00677    }
00678    s->inlen += res;
00679    s->inbuf[s->inlen] = '\0';
00680    return 0;
00681 }
00682 
00683 static void *session_do(void *data)
00684 {
00685    struct mansession *s = data;
00686    struct message m;
00687    int res;
00688    
00689    ast_mutex_lock(&s->lock);
00690    ast_cli(s->fd, "Asterisk Call Manager/1.0\r\n");
00691    ast_mutex_unlock(&s->lock);
00692    memset(&m, 0, sizeof(&m));
00693    for (;;) {
00694       res = get_input(s, m.headers[m.hdrcount]);
00695       if (res > 0) {
00696          /* Strip trailing \r\n */
00697          if (strlen(m.headers[m.hdrcount]) < 2)
00698             continue;
00699          m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
00700          if (!strlen(m.headers[m.hdrcount])) {
00701             if (process_message(s, &m))
00702                break;
00703             memset(&m, 0, sizeof(&m));
00704          } else if (m.hdrcount < MAX_HEADERS - 1)
00705             m.hdrcount++;
00706       } else if (res < 0)
00707          break;
00708    }
00709    if (s->authenticated) {
00710       if (option_verbose > 1) 
00711          ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, inet_ntoa(s->sin.sin_addr));
00712       ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, inet_ntoa(s->sin.sin_addr));
00713    } else {
00714       if (option_verbose > 1)
00715          ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", inet_ntoa(s->sin.sin_addr));
00716       ast_log(LOG_EVENT, "Failed attempt from %s\n", inet_ntoa(s->sin.sin_addr));
00717    }
00718    destroy_session(s);
00719    return NULL;
00720 }
00721 
00722 static void *accept_thread(void *ignore)
00723 {
00724    int as;
00725    struct sockaddr_in sin;
00726    int sinlen;
00727    struct mansession *s;
00728    struct protoent *p;
00729    int arg = 1;
00730    pthread_attr_t attr;
00731 
00732    pthread_attr_init(&attr);
00733    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00734 
00735    for (;;) {
00736       sinlen = sizeof(sin);
00737       as = accept(asock, (struct sockaddr *)&sin, &sinlen);
00738       if (as < 0) {
00739          ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
00740          continue;
00741       }
00742       p = getprotobyname("tcp");
00743       if( p ) {
00744          if( setsockopt(as, p->p_proto, TCP_NODELAY, (char *)&arg, sizeof(arg) ) < 0 ) {
00745             ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
00746          }
00747       }
00748       s = malloc(sizeof(struct mansession));
00749       if (!s) {
00750          ast_log(LOG_WARNING, "Failed to allocate management session: %s\n", strerror(errno));
00751          continue;
00752       } 
00753       memset(s, 0, sizeof(struct mansession));
00754       memcpy(&s->sin, &sin, sizeof(sin));
00755       ast_mutex_init(&s->lock);
00756       s->fd = as;
00757       ast_mutex_lock(&sessionlock);
00758       s->next = sessions;
00759       sessions = s;
00760       ast_mutex_unlock(&sessionlock);
00761       if (pthread_create(&t, &attr, session_do, s))
00762          destroy_session(s);
00763    }
00764    pthread_attr_destroy(&attr);
00765    return NULL;
00766 }
00767 
00768 int manager_event(int category, char *event, char *fmt, ...)
00769 {
00770    struct mansession *s;
00771    char tmp[4096];
00772    va_list ap;
00773 
00774    ast_mutex_lock(&sessionlock);
00775    s = sessions;
00776    while(s) {
00777       if ((s->readperm & category) == category) {
00778          ast_mutex_lock(&s->lock);
00779          if (!s->blocking) {
00780             ast_cli(s->fd, "Event: %s\r\n", event);
00781             va_start(ap, fmt);
00782             vsnprintf(tmp, sizeof(tmp), fmt, ap);
00783             va_end(ap);
00784             write(s->fd, tmp, strlen(tmp));
00785             ast_cli(s->fd, "\r\n");
00786          }
00787          ast_mutex_unlock(&s->lock);
00788       }
00789       s = s->next;
00790    }
00791    ast_mutex_unlock(&sessionlock);
00792    return 0;
00793 }
00794 
00795 int ast_manager_unregister( char *action ) {
00796    struct manager_action *cur = first_action, *prev = first_action;
00797 
00798    ast_mutex_lock(&actionlock);
00799    while( cur ) {       
00800       if (!strcasecmp(action, cur->action)) {
00801          prev->next = cur->next;
00802          free(cur);
00803          if (option_verbose > 1) 
00804             ast_verbose(VERBOSE_PREFIX_2 "Manager unregistered action %s\n", action);
00805          ast_mutex_unlock(&actionlock);
00806          return 0;
00807       }
00808       prev = cur;
00809       cur = cur->next;
00810    }
00811    ast_mutex_unlock(&actionlock);
00812    return 0;
00813 }
00814 
00815 static int manager_state_cb(char *context, char *exten, int state, void *data)
00816 {
00817    /* Notify managers of change */
00818    manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nStatus: %d\r\n", exten, context, state);
00819    return 0;
00820 }
00821 
00822 int ast_manager_register( char *action, int auth, 
00823    int (*func)(struct mansession *s, struct message *m), char *synopsis)
00824 {
00825    struct manager_action *cur = first_action, *prev = NULL;
00826 
00827    ast_mutex_lock(&actionlock);
00828    while(cur) { /* Walk the list of actions */
00829       if (!strcasecmp(cur->action, action)) {
00830          ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", action);
00831          ast_mutex_unlock(&actionlock);
00832          return -1;
00833       }
00834       prev = cur; 
00835       cur = cur->next;
00836    }
00837    cur = malloc( sizeof(struct manager_action) );
00838    if( !cur ) {
00839       ast_log(LOG_WARNING, "Manager: out of memory trying to register action\n");
00840       ast_mutex_unlock(&actionlock);
00841       return -1;
00842    }
00843    strncpy( cur->action, action, 255 );
00844    cur->authority = auth;
00845    cur->func = func;
00846    cur->synopsis = synopsis;
00847    cur->next = NULL;
00848 
00849    if( prev ) prev->next = cur;
00850    else first_action = cur;
00851 
00852    if (option_verbose > 1) 
00853       ast_verbose(VERBOSE_PREFIX_2 "Manager registered action %s\n", action);
00854    ast_mutex_unlock(&actionlock);
00855    return 0;
00856 }
00857 
00858 static int registered = 0;
00859 
00860 int init_manager(void)
00861 {
00862    struct ast_config *cfg;
00863    char *val;
00864    int oldportno = portno;
00865    static struct sockaddr_in ba;
00866    int x = 1;
00867    if (!registered) {
00868       /* Register default actions */
00869       ast_manager_register( "Ping", 0, action_ping, "Ping" );
00870       ast_manager_register( "Logoff", 0, action_logoff, "Logoff Manager" );
00871       ast_manager_register( "Hangup", EVENT_FLAG_CALL, action_hangup, "Hangup Channel" );
00872       ast_manager_register( "Status", EVENT_FLAG_CALL, action_status, "Status" );
00873       ast_manager_register( "Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect" );
00874       ast_manager_register( "Originate", EVENT_FLAG_CALL, action_originate, "Originate Call" );
00875       ast_manager_register( "MailboxStatus", EVENT_FLAG_CALL, action_mailboxstatus, "Check Mailbox" );
00876       ast_manager_register( "Command", EVENT_FLAG_COMMAND, action_command, "Execute Command" );
00877       ast_manager_register( "ExtensionState", EVENT_FLAG_CALL, action_extensionstate, "Check Extension Status" );
00878       ast_manager_register( "AbsoluteTimeout", EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout" );
00879       ast_manager_register( "MailboxCount", EVENT_FLAG_CALL, action_mailboxcount, "Check Mailbox Message Count" );
00880 
00881       ast_cli_register(&show_mancmds_cli);
00882       ast_cli_register(&show_manconn_cli);
00883       ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
00884       registered = 1;
00885    }
00886    portno = DEFAULT_MANAGER_PORT;
00887    cfg = ast_load("manager.conf");
00888    if (!cfg) {
00889       ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
00890       return 0;
00891    }
00892    memset(&ba, 0, sizeof(ba));
00893    val = ast_variable_retrieve(cfg, "general", "enabled");
00894    if (val)
00895       enabled = ast_true(val);
00896 
00897    if ((val = ast_variable_retrieve(cfg, "general", "portno"))) {
00898       if (sscanf(val, "%d", &portno) != 1) {
00899          ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
00900          portno = DEFAULT_MANAGER_PORT;
00901       }
00902    }
00903    
00904    ba.sin_family = AF_INET;
00905    ba.sin_port = htons(portno);
00906    memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
00907    
00908    if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
00909       if (!inet_aton(val, &ba.sin_addr)) { 
00910          ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
00911          memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
00912       }
00913    }
00914 
00915    if ((asock > -1) && ((portno != oldportno) || !enabled)) {
00916 #if 0
00917       /* Can't be done yet */
00918       close(asock);
00919       asock = -1;
00920 #else
00921       ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
00922 #endif
00923    }
00924    ast_destroy(cfg);
00925    
00926    /* If not enabled, do nothing */
00927    if (!enabled) {
00928       return 0;
00929    }
00930    if (asock < 0) {
00931       asock = socket(AF_INET, SOCK_STREAM, 0);
00932       if (asock < 0) {
00933          ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
00934          return -1;
00935       }
00936       setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
00937       if (bind(asock, (struct sockaddr *)&ba, sizeof(ba))) {
00938          ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
00939          close(asock);
00940          asock = -1;
00941          return -1;
00942       }
00943       if (listen(asock, 2)) {
00944          ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
00945          close(asock);
00946          asock = -1;
00947          return -1;
00948       }
00949       if (option_verbose)
00950          ast_verbose("Asterisk Management interface listening on port %d\n", portno);
00951       pthread_create(&t, NULL, accept_thread, NULL);
00952    }
00953    return 0;
00954 }
00955 
00956 int reload_manager(void)
00957 {
00958    manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n");
00959    return init_manager();
00960 }

Generated on Fri Feb 27 12:19:43 2004 for Asterisk by doxygen 1.3.5