Version 0.2.0 from FTP
[asterisk/asterisk.git] / manager.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Channel Management and more
5  * 
6  * Copyright (C) 1999, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <pthread.h>
17 #include <string.h>
18 #include <sys/time.h>
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <signal.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include <asterisk/channel.h>
26 #include <asterisk/file.h>
27 #include <asterisk/manager.h>
28 #include <asterisk/config.h>
29 #include <asterisk/lock.h>
30 #include <asterisk/logger.h>
31 #include <asterisk/options.h>
32 #include <asterisk/cli.h>
33 #include <asterisk/pbx.h>
34
35 static int enabled = 0;
36 static int portno = DEFAULT_MANAGER_PORT;
37 static int asock = -1;
38 static pthread_t t;
39 static pthread_mutex_t sessionlock = AST_MUTEX_INITIALIZER;
40
41 static struct permalias {
42         int num;
43         char *label;
44 } perms[] = {
45         { EVENT_FLAG_SYSTEM, "system" },
46         { EVENT_FLAG_CALL, "call" },
47         { EVENT_FLAG_LOG, "log" },
48         { EVENT_FLAG_VERBOSE, "verbose" },
49         { EVENT_FLAG_COMMAND, "command" },
50         { EVENT_FLAG_AGENT, "agent" },
51         { -1, "all" },
52 };
53
54 #define MAX_HEADERS 80
55 #define MAX_LEN 256
56
57 static struct mansession {
58         pthread_t t;
59         pthread_mutex_t lock;
60         struct sockaddr_in sin;
61         int fd;
62         char username[80];
63         int authenticated;
64         int readperm;
65         int writeperm;
66         char inbuf[MAX_LEN];
67         int inlen;
68         
69         struct mansession *next;
70 } *sessions = NULL;
71
72
73 struct message {
74         int hdrcount;
75         char headers[MAX_HEADERS][MAX_LEN];
76 };
77
78 static void destroy_session(struct mansession *s)
79 {
80         struct mansession *cur, *prev = NULL;
81         ast_pthread_mutex_lock(&sessionlock);
82         cur = sessions;
83         while(cur) {
84                 if (cur == s)
85                         break;
86                 prev = cur;
87                 cur = cur->next;
88         }
89         if (cur) {
90                 if (prev)
91                         prev->next = cur->next;
92                 else
93                         sessions = cur->next;
94                 if (s->fd > -1)
95                         close(s->fd);
96                 free(s);
97         } else
98                 ast_log(LOG_WARNING, "Trying to delete non-existant session %p?\n", s);
99         ast_pthread_mutex_unlock(&sessionlock);
100         
101 }
102
103 static char *get_header(struct message *m, char *var)
104 {
105         char cmp[80];
106         int x;
107         snprintf(cmp, sizeof(cmp), "%s: ", var);
108         for (x=0;x<m->hdrcount;x++)
109                 if (!strncasecmp(cmp, m->headers[x], strlen(cmp)))
110                         return m->headers[x] + strlen(cmp);
111         return "";
112 }
113
114 static void send_error(struct mansession *s, char *error)
115 {
116         ast_pthread_mutex_lock(&s->lock);
117         ast_cli(s->fd, "Response: Error\r\n");
118         ast_cli(s->fd, "Message: %s\r\n\r\n", error);
119         ast_pthread_mutex_unlock(&s->lock);
120 }
121
122 static void send_response(struct mansession *s, char *resp, char *msg)
123 {
124         ast_pthread_mutex_lock(&s->lock);
125         ast_cli(s->fd, "Response: %s\r\n", resp);
126         if (msg)
127                 ast_cli(s->fd, "Message: %s\r\n\r\n", msg);
128         else
129                 ast_cli(s->fd, "\r\n");
130         ast_pthread_mutex_unlock(&s->lock);
131 }
132
133 static void send_ack(struct mansession *s, char *msg)
134 {
135         send_response(s, "Success", msg);
136 }
137
138 static int get_perm(char *instr)
139 {
140         char tmp[256];
141         char *c;
142         int x;
143         int ret = 0;
144         strncpy(tmp, instr, sizeof(tmp) - 1);
145         c = strtok(tmp, ",");
146         while(c) {
147                 for (x=0;x<sizeof(perms) / sizeof(perms[0]);x++) {
148                         if (!strcasecmp(perms[x].label, c)) 
149                                 ret |= perms[x].num;
150                 }
151                 c = strtok(NULL, ",");
152         }
153         return ret;
154 }
155
156 static int authenticate(struct mansession *s, struct message *m)
157 {
158         struct ast_config *cfg;
159         char *cat;
160         char *user = get_header(m, "Username");
161         char *pass = get_header(m, "Secret");
162         cfg = ast_load("manager.conf");
163         if (!cfg)
164                 return -1;
165         cat = ast_category_browse(cfg, NULL);
166         while(cat) {
167                 if (strcasecmp(cat, "general")) {
168                         /* This is a user */
169                         if (!strcasecmp(cat, user)) {
170                                 char *password = ast_variable_retrieve(cfg, cat, "secret");
171                                 if (password && !strcasecmp(password, pass)) {
172                                         break;
173                                 } else {
174                                         ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", inet_ntoa(s->sin.sin_addr), user);
175                                         ast_destroy(cfg);
176                                         return -1;
177                                 }       
178                         }
179                 }
180                 cat = ast_category_browse(cfg, cat);
181         }
182         if (cat) {
183                 strncpy(s->username, cat, sizeof(s->username) - 1);
184                 s->readperm = get_perm(ast_variable_retrieve(cfg, cat, "read"));
185                 s->writeperm = get_perm(ast_variable_retrieve(cfg, cat, "write"));
186                 ast_destroy(cfg);
187                 return 0;
188         }
189         ast_log(LOG_NOTICE, "%s tried to authenticate with non-existant user '%s'\n", inet_ntoa(s->sin.sin_addr), user);
190         ast_destroy(cfg);
191         return -1;
192 }
193
194 static int action_ping(struct mansession *s, struct message *m)
195 {
196         send_response(s, "Pong", NULL);
197         return 0;
198 }
199
200 static int action_logoff(struct mansession *s, struct message *m)
201 {
202         send_response(s, "Goodbye", "Thanks for all the fish.");
203         return -1;
204 }
205
206 static int action_hangup(struct mansession *s, struct message *m)
207 {
208         struct ast_channel *c = NULL;
209         char *name = get_header(m, "Channel");
210         if (!strlen(name)) {
211                 send_error(s, "No channel specified");
212                 return 0;
213         }
214         c = ast_channel_walk(NULL);
215         while(c) {
216                 if (!strcasecmp(c->name, name)) {
217                         break;
218                 }
219                 c = ast_channel_walk(c);
220         }
221         if (!c) {
222                 send_error(s, "No such channel");
223                 return 0;
224         }
225         ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
226         send_ack(s, "Channel Hungup");
227         return 0;
228 }
229
230 static int action_status(struct mansession *s, struct message *m)
231 {
232         struct ast_channel *c;
233         char bridge[256];
234         send_ack(s, "Channel status will follow");
235         c = ast_channel_walk(NULL);
236         while(c) {
237                 if (c->bridge)
238                         snprintf(bridge, sizeof(bridge), "Link: %s\r\n", c->bridge->name);
239                 else
240                         strcpy(bridge, "");
241                 if (c->pbx) {
242                         ast_cli(s->fd,
243                         "Event: Status\r\n"
244                         "Channel: %s\r\n"
245                         "CallerID: %s\r\n"
246                         "State: %s\r\n"
247                         "Context: %s\r\n"
248                         "Extension: %s\r\n"
249                         "Priority: %d\r\n"
250                         "%s"
251                         "\r\n",
252                         c->name, c->callerid ? c->callerid : "<unknown>", 
253                         ast_state2str(c->_state), c->context,
254                         c->exten, c->priority, bridge);
255                 } else {
256                         ast_cli(s->fd,
257                         "Event: Status\r\n"
258                         "Channel: %s\r\n"
259                         "CallerID: %s\r\n"
260                         "State: %s\r\n"
261                         "%s"
262                         "\r\n",
263                         c->name, c->callerid ? c->callerid : "<unknown>", 
264                         ast_state2str(c->_state), bridge);
265                 }
266                 c = ast_channel_walk(c);
267         }
268         return 0;
269 }
270
271 static int action_redirect(struct mansession *s, struct message *m)
272 {
273         char *name = get_header(m, "Channel");
274         char *name2 = get_header(m, "ExtraChannel");
275         char *exten = get_header(m, "Exten");
276         char *context = get_header(m, "Context");
277         char *priority = get_header(m, "Priority");
278         int pi = 0;
279         int res;
280         if (!name || !strlen(name)) {
281                 send_error(s, "Channel not specified");
282                 return 0;
283         }
284         if (strlen(priority) && (sscanf(priority, "%d", &pi) != 1)) {
285                 send_error(s, "Invalid priority\n");
286                 return 0;
287         }
288         res = ast_async_goto_by_name(name, context, exten, pi);
289         if (!res) {
290                 if (strlen(name2)) {
291                         res = ast_async_goto_by_name(name2, context, exten, pi);
292                         if (!res)
293                                 send_ack(s, "Dual Redirect successful");
294                         else
295                                 send_error(s, "Secondary redirect failed");
296                 } else
297                         send_ack(s, "Redirect successful");
298         } else
299                 send_error(s, "Redirect failed");
300         return 0;
301 }
302
303 static int action_originate(struct mansession *s, struct message *m)
304 {
305         char *name = get_header(m, "Channel");
306         char *exten = get_header(m, "Exten");
307         char *context = get_header(m, "Context");
308         char *priority = get_header(m, "Priority");
309         char *timeout = get_header(m, "Timeout");
310         char *tech, *data;
311         int pi = 0;
312         int res;
313         int to = 30000;
314         int reason = 0;
315         char tmp[256];
316         if (!name) {
317                 send_error(s, "Channel not specified");
318                 return 0;
319         }
320         if (strlen(priority) && (sscanf(priority, "%d", &pi) != 1)) {
321                 send_error(s, "Invalid priority\n");
322                 return 0;
323         }
324         if (strlen(timeout) && (sscanf(priority, "%d", &to) != 1)) {
325                 send_error(s, "Invalid timeout\n");
326                 return 0;
327         }
328         strncpy(tmp, name, sizeof(tmp) - 1);
329         tech = tmp;
330         data = strchr(tmp, '/');
331         if (!data) {
332                 send_error(s, "Invalid channel\n");
333                 return 0;
334         }
335         *data = '\0';
336         data++;
337         res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 0);
338         if (!res)
339                 send_ack(s, "Originate successfully queued");
340         else
341                 send_error(s, "Originate failed");
342         return 0;
343 }
344
345 static struct action {
346         char *action;
347         int authority;
348         int (*func)(struct mansession *s, struct message *m);
349 } actions[] = {
350         { "Ping", 0, action_ping },
351         { "Logoff", 0, action_logoff },
352         { "Hangup", EVENT_FLAG_CALL, action_hangup },
353         { "Status", EVENT_FLAG_CALL, action_status },
354         { "Redirect", EVENT_FLAG_CALL, action_redirect },
355         { "Originate", EVENT_FLAG_CALL, action_originate },
356 };
357
358 static int process_message(struct mansession *s, struct message *m)
359 {
360         int x;
361         char action[80];
362         strncpy(action, get_header(m, "Action"), sizeof(action));
363         if (!strlen(action)) {
364                 send_error(s, "Missing action in request");
365                 return 0;
366         }
367         if (!s->authenticated) {
368                 if (!strcasecmp(action, "Login")) {
369                         if (authenticate(s, m)) {
370                                 sleep(1);
371                                 send_error(s, "Authentication failed");
372                                 return -1;
373                         } else {
374                                 s->authenticated = 1;
375                                 if (option_verbose > 1) 
376                                         ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged on from %s\n", s->username, inet_ntoa(s->sin.sin_addr));
377                                 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, inet_ntoa(s->sin.sin_addr));
378                                 send_ack(s, "Authentication accepted");
379                         }
380                 } else 
381                         send_error(s, "Authentication Required");
382         } else {
383                 for (x=0;x<sizeof(actions) / sizeof(actions[0]);x++) {
384                         if (!strcasecmp(action, actions[x].action)) {
385                                 if ((s->writeperm & actions[x].authority) == actions[x].authority) {
386                                         if (actions[x].func(s, m))
387                                                 return -1;
388                                 } else {
389                                         send_error(s, "Permission denied");
390                                 }
391                                 break;
392                         }
393                 }
394                 if (x >= sizeof(actions) / sizeof(actions[0]))
395                         send_error(s, "Invalid/unknown command");
396         }
397 #if 0
398         for (x=0;x<m->hdrcount;x++) {
399                 printf("Header: %s\n", m->headers[x]);
400         }
401 #endif  
402         return 0;
403 }
404
405 static int get_input(struct mansession *s, char *output)
406 {
407         /* output must have at least sizeof(s->inbuf) space */
408         int res;
409         int x;
410         fd_set fds;
411         for (x=1;x<s->inlen;x++) {
412                 if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) {
413                         /* Copy output data up to and including \r\n */
414                         memcpy(output, s->inbuf, x + 1);
415                         /* Add trailing \0 */
416                         output[x+1] = '\0';
417                         /* Move remaining data back to the front */
418                         memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x);
419                         s->inlen -= (x + 1);
420                         return 1;
421                 }
422         } 
423         if (s->inlen >= sizeof(s->inbuf) - 1) {
424                 ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf);
425                 s->inlen = 0;
426         }
427         FD_ZERO(&fds);
428         FD_SET(s->fd, &fds);
429         res = select(s->fd + 1, &fds, NULL, NULL, NULL);
430         if (res < 0) {
431                 ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
432         } else if (res > 0) {
433                 ast_pthread_mutex_lock(&s->lock);
434                 res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
435                 ast_pthread_mutex_unlock(&s->lock);
436                 if (res < 1)
437                         return -1;
438         }
439         s->inlen += res;
440         s->inbuf[s->inlen] = '\0';
441         return 0;
442 }
443
444 static void *session_do(void *data)
445 {
446         struct mansession *s = data;
447         struct message m;
448         int res;
449         
450         ast_pthread_mutex_lock(&s->lock);
451         ast_cli(s->fd, "Asterisk Call Manager/1.0\r\n");
452         ast_pthread_mutex_unlock(&s->lock);
453         memset(&m, 0, sizeof(&m));
454         for (;;) {
455                 res = get_input(s, m.headers[m.hdrcount]);
456                 if (res > 0) {
457                         /* Strip trailing \r\n */
458                         if (strlen(m.headers[m.hdrcount]) < 2)
459                                 continue;
460                         m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0';
461                         if (!strlen(m.headers[m.hdrcount])) {
462                                 if (process_message(s, &m))
463                                         break;
464                                 memset(&m, 0, sizeof(&m));
465                         } else if (m.hdrcount < MAX_HEADERS - 1)
466                                 m.hdrcount++;
467                 } else if (res < 0)
468                         break;
469         }
470         if (s->authenticated) {
471                 if (option_verbose > 1) 
472                         ast_verbose(VERBOSE_PREFIX_2 "Manager '%s' logged off from %s\n", s->username, inet_ntoa(s->sin.sin_addr));
473                 ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, inet_ntoa(s->sin.sin_addr));
474         } else {
475                 if (option_verbose > 1)
476                         ast_verbose(VERBOSE_PREFIX_2 "Connect attempt from '%s' unable to authenticate\n", inet_ntoa(s->sin.sin_addr));
477                 ast_log(LOG_EVENT, "Failed attempt from %s\n", inet_ntoa(s->sin.sin_addr));
478         }
479         destroy_session(s);
480         return NULL;
481 }
482
483 static void *accept_thread(void *ignore)
484 {
485         int as;
486         struct sockaddr_in sin;
487         int sinlen;
488         struct mansession *s;
489         for (;;) {
490                 sinlen = sizeof(sin);
491                 as = accept(asock, &sin, &sinlen);
492                 if (as < 0) {
493                         ast_log(LOG_NOTICE, "Accept returned -1: %s\n", strerror(errno));
494                         continue;
495                 }
496                 s = malloc(sizeof(struct mansession));
497                 if (!s) {
498                         ast_log(LOG_WARNING, "Failed to allocate management session: %s\n", strerror(errno));
499                         continue;
500                 } 
501                 memset(s, 0, sizeof(struct mansession));
502                 memcpy(&s->sin, &sin, sizeof(sin));
503                 ast_pthread_mutex_init(&s->lock);
504                 s->fd = as;
505                 ast_pthread_mutex_lock(&sessionlock);
506                 s->next = sessions;
507                 sessions = s;
508                 ast_pthread_mutex_unlock(&sessionlock);
509                 if (pthread_create(&t, NULL, session_do, s))
510                         destroy_session(s);
511         }
512                 
513 }
514
515 int manager_event(int category, char *event, char *fmt, ...)
516 {
517         struct mansession *s;
518         char tmp[4096];
519         va_list ap;
520
521         ast_pthread_mutex_lock(&sessionlock);
522         s = sessions;
523         while(s) {
524                 if ((s->readperm & category) == category) {
525                         ast_pthread_mutex_lock(&s->lock);
526                         ast_cli(s->fd, "Event: %s\r\n", event);
527                         va_start(ap, fmt);
528                         vsnprintf(tmp, sizeof(tmp), fmt, ap);
529                         va_end(ap);
530                         write(s->fd, tmp, strlen(tmp));
531                         ast_cli(s->fd, "\r\n");
532                         ast_pthread_mutex_unlock(&s->lock);
533                 }
534                 s = s->next;
535         }
536         ast_pthread_mutex_unlock(&sessionlock);
537         return 0;
538 }
539
540 int init_manager(void)
541 {
542         struct ast_config *cfg;
543         char *val;
544         int oldportno = portno;
545         static struct sockaddr_in ba;
546         int x = 1;
547
548         portno = DEFAULT_MANAGER_PORT;
549         cfg = ast_load("manager.conf");
550         if (!cfg) {
551                 ast_log(LOG_NOTICE, "Unable to open management configuration manager.conf.  Call management disabled.\n");
552                 return 0;
553         }
554         memset(&ba, 0, sizeof(ba));
555         val = ast_variable_retrieve(cfg, "general", "enabled");
556         if (val)
557                 enabled = ast_true(val);
558
559         if ((val = ast_variable_retrieve(cfg, "general", "portno"))) {
560                 if (sscanf(val, "%d", &portno) != 1) {
561                         ast_log(LOG_WARNING, "Invalid port number '%s'\n", val);
562                         portno = DEFAULT_MANAGER_PORT;
563                 }
564         }
565         
566         ba.sin_family = AF_INET;
567         ba.sin_port = htons(portno);
568         memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
569         
570         if ((val = ast_variable_retrieve(cfg, "general", "bindaddr"))) {
571                 if (!inet_aton(val, &ba.sin_addr)) { 
572                         ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
573                         memset(&ba.sin_addr, 0, sizeof(ba.sin_addr));
574                 }
575         }
576
577         if ((asock > -1) && ((portno != oldportno) || !enabled)) {
578 #if 0
579                 /* Can't be done yet */
580                 close(asock);
581                 asock = -1;
582 #else
583                 ast_log(LOG_WARNING, "Unable to change management port / enabled\n");
584 #endif
585         }
586         /* If not enabled, do nothing */
587         if (!enabled)
588                 return 0;
589         if (asock < 0) {
590                 asock = socket(AF_INET, SOCK_STREAM, 0);
591                 if (asock < 0) {
592                         ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
593                         return -1;
594                 }
595                 setsockopt(asock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
596                 if (bind(asock, &ba, sizeof(ba))) {
597                         ast_log(LOG_WARNING, "Unable to bind socket: %s\n", strerror(errno));
598                         close(asock);
599                         asock = -1;
600                         return -1;
601                 }
602                 if (listen(asock, 2)) {
603                         ast_log(LOG_WARNING, "Unable to listen on socket: %s\n", strerror(errno));
604                         close(asock);
605                         asock = -1;
606                         return -1;
607                 }
608                 if (option_verbose)
609                         ast_verbose("Asterisk Management interface listening on port %d\n", portno);
610                 pthread_create(&t, NULL, accept_thread, NULL);
611         }
612         ast_destroy(cfg);
613         return 0;
614 }
615
616 int reload_manager(void)
617 {
618         return init_manager();
619 }