aboutsummaryrefslogtreecommitdiffstats
path: root/asterisk.c
diff options
context:
space:
mode:
authormarkster <markster@f38db490-d61c-443f-a65b-d21fe96a405b>2001-05-09 03:11:22 +0000
committermarkster <markster@f38db490-d61c-443f-a65b-d21fe96a405b>2001-05-09 03:11:22 +0000
commitae6ebd68ee6ff49606de0588ebcad60f66314538 (patch)
treeec551da1938d368e95dedc1e49a91311e41c4c8e /asterisk.c
parentf9e47ebafc66c3b163d38744949107b5eef9ccf8 (diff)
Version 0.1.8 from FTP
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@320 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'asterisk.c')
-rwxr-xr-xasterisk.c439
1 files changed, 425 insertions, 14 deletions
diff --git a/asterisk.c b/asterisk.c
index 94e6802d0..a73307be0 100755
--- a/asterisk.c
+++ b/asterisk.c
@@ -19,34 +19,240 @@
#include <asterisk/channel.h>
#include <asterisk/ulaw.h>
#include <asterisk/callerid.h>
+#include <asterisk/module.h>
#include <stdio.h>
#include <signal.h>
#include <sched.h>
#include <pthread.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
+#include <string.h>
+#include <errno.h>
#include <readline/readline.h>
#include <readline/history.h>
#include "asterisk.h"
+#define AST_MAX_CONNECTS 128
+#define NUM_MSGS 64
+
int option_verbose=0;
int option_debug=0;
int option_nofork=0;
int option_quiet=0;
int option_console=0;
int option_highpriority=0;
+int option_remote=0;
+int option_exec=0;
int fully_booted = 0;
+static int ast_socket = -1; /* UNIX Socket for allowing remote control */
+static int ast_consock = -1; /* UNIX Socket for controlling another asterisk */
+static int mainpid;
+struct console {
+ int fd; /* File descriptor */
+ int p[2]; /* Pipe */
+ pthread_t t; /* Thread of handler */
+};
+
+struct console consoles[AST_MAX_CONNECTS];
+
char defaultlanguage[MAX_LANGUAGE] = DEFAULT_LANGUAGE;
+static int fdprint(int fd, char *s)
+{
+ return write(fd, s, strlen(s) + 1);
+}
+
+static void network_verboser(char *s, int pos, int replace, int complete)
+{
+ int x;
+ for (x=0;x<AST_MAX_CONNECTS; x++) {
+ if (consoles[x].fd > -1)
+ fdprint(consoles[x].p[1], s);
+ }
+}
+
+static pthread_t lthread;
+
+static void *netconsole(void *vconsole)
+{
+ struct console *con = vconsole;
+ char hostname[256];
+ char tmp[512];
+ int res;
+ int max;
+ fd_set rfds;
+
+ if (gethostname(hostname, sizeof(hostname)))
+ strncpy(hostname, "<Unknown>", sizeof(hostname));
+ snprintf(tmp, sizeof(tmp), "%s/%d/%s\n", hostname, mainpid, ASTERISK_VERSION);
+ fdprint(con->fd, tmp);
+ for(;;) {
+ FD_ZERO(&rfds);
+ FD_SET(con->fd, &rfds);
+ FD_SET(con->p[0], &rfds);
+ max = con->fd;
+ if (con->p[0] > max)
+ max = con->p[0];
+ res = select(max + 1, &rfds, NULL, NULL, NULL);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "select returned < 0: %s\n", strerror(errno));
+ continue;
+ }
+ if (FD_ISSET(con->fd, &rfds)) {
+ res = read(con->fd, tmp, sizeof(tmp));
+ if (res < 1)
+ break;
+ tmp[res] = 0;
+ ast_cli_command(con->fd, tmp);
+ }
+ if (FD_ISSET(con->p[0], &rfds)) {
+ res = read(con->p[0], tmp, sizeof(tmp));
+ if (res < 1) {
+ ast_log(LOG_ERROR, "read returned %d\n", res);
+ break;
+ }
+ res = write(con->fd, tmp, res);
+ if (res < 1)
+ break;
+ }
+ }
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Remote UNIX connection disconnected\n");
+ close(con->fd);
+ close(con->p[0]);
+ close(con->p[1]);
+ con->fd = -1;
+
+ return NULL;
+}
+
+static void *listener(void *unused)
+{
+ struct sockaddr_un sun;
+ int s;
+ int len;
+ int x;
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ for(;;) {
+ len = sizeof(sun);
+ s = accept(ast_socket, (struct sockaddr *)&sun, &len);
+ if (s < 0) {
+ ast_log(LOG_WARNING, "Accept retured %d: %s\n", s, strerror(errno));
+ } else {
+ for (x=0;x<AST_MAX_CONNECTS;x++) {
+ if (consoles[x].fd < 0) {
+ if (pipe(consoles[x].p)) {
+ ast_log(LOG_ERROR, "Unable to create pipe: %s\n", strerror(errno));
+ consoles[x].fd = -1;
+ fdprint(s, "Server failed to create pipe\n");
+ close(s);
+ break;
+ }
+ consoles[x].fd = s;
+ if (pthread_create(&consoles[x].t, &attr, netconsole, &consoles[x])) {
+ ast_log(LOG_ERROR, "Unable to spawn thread to handle connection\n");
+ consoles[x].fd = -1;
+ fdprint(s, "Server failed to spawn thread\n");
+ close(s);
+ }
+ break;
+ }
+ }
+ if (x >= AST_MAX_CONNECTS) {
+ fdprint(s, "No more connections allowed\n");
+ ast_log(LOG_WARNING, "No more connections allowed\n");
+ close(s);
+ } else if (consoles[x].fd > -1) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Remote UNIX connection\n");
+ }
+ }
+ }
+ return NULL;
+}
+
+static int ast_makesocket(void)
+{
+ struct sockaddr_un sun;
+ int res;
+ int x;
+ for (x=0;x<AST_MAX_CONNECTS;x++)
+ consoles[x].fd = -1;
+ unlink(AST_SOCKET);
+ ast_socket = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (ast_socket < 0) {
+ ast_log(LOG_WARNING, "Unable to create control socket: %s\n", strerror(errno));
+ return -1;
+ }
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_LOCAL;
+ strncpy(sun.sun_path, AST_SOCKET, sizeof(sun.sun_path));
+ res = bind(ast_socket, (struct sockaddr *)&sun, sizeof(sun));
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to bind socket to %s: %s\n", AST_SOCKET, strerror(errno));
+ close(ast_socket);
+ ast_socket = -1;
+ return -1;
+ }
+ res = listen(ast_socket, 2);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to listen on socket %s: %s\n", AST_SOCKET, strerror(errno));
+ close(ast_socket);
+ ast_socket = -1;
+ return -1;
+ }
+ ast_register_verbose(network_verboser);
+ pthread_create(&lthread, NULL, listener, NULL);
+ return 0;
+}
+
+static int ast_tryconnect(void)
+{
+ struct sockaddr_un sun;
+ int res;
+ ast_consock = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (ast_consock < 0) {
+ ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
+ return 0;
+ }
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_LOCAL;
+ strncpy(sun.sun_path, AST_SOCKET, sizeof(sun.sun_path));
+ res = connect(ast_consock, (struct sockaddr *)&sun, sizeof(sun));
+ if (res) {
+ close(ast_consock);
+ ast_consock = -1;
+ return 0;
+ } else
+ return 1;
+}
+
static void urg_handler(int num)
{
/* Called by soft_hangup to interrupt the select, read, or other
system call. We don't actually need to do anything though. */
- if (option_debug)
+ if (option_debug)
ast_log(LOG_DEBUG, "Urgent handler\n");
signal(num, urg_handler);
return;
}
+static void hup_handler(int num)
+{
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Received HUP signal -- Reloading configs\n");
+ ast_module_reload();
+}
+
+
+static void pipe_handler(int num)
+{
+ /* Ignore sigpipe */
+}
static void set_title(char *text)
{
/* Set an X-term or screen title */
@@ -86,19 +292,26 @@ static int set_priority(int pri)
static void quit_handler(int num)
{
- static pthread_mutex_t quitlock = PTHREAD_MUTEX_INITIALIZER;
char filename[80] = "";
- if (getenv("HOME"))
- snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
- /* Quit only once */
- pthread_mutex_lock(&quitlock);
+ if (option_console || option_remote) {
+ if (getenv("HOME"))
+ snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
+ if (strlen(filename))
+ write_history(filename);
+ rl_callback_handler_remove();
+ }
/* Called on exit */
- if (option_verbose)
+ if (option_verbose && option_console)
ast_verbose("Asterisk ending (%d).\n", num);
else if (option_debug)
ast_log(LOG_DEBUG, "Asterisk ending (%d).\n", num);
- if (strlen(filename))
- write_history(filename);
+ if (ast_socket > -1)
+ close(ast_socket);
+ if (ast_consock > -1)
+ close(ast_consock);
+ if (ast_socket > -1)
+ unlink(AST_SOCKET);
+
exit(0);
}
@@ -136,10 +349,39 @@ static void consolehandler(char *s)
fprintf(stdout, "\nUse \"quit\" to exit\n");
}
+
+static char cmd[1024];
+
+static void remoteconsolehandler(char *s)
+{
+ /* Called when readline data is available */
+ if (s && strlen(s))
+ add_history(s);
+ /* Give the console access to the shell */
+ if (s) {
+ if (s[0] == '!') {
+ if (s[1])
+ system(s+1);
+ else
+ system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
+ } else
+ strncpy(cmd, s, sizeof(cmd));
+ if (!strcasecmp(s, "help"))
+ fprintf(stdout, " !<command> Executes a given shell command\n");
+ if (!strcasecmp(s, "quit"))
+ quit_handler(0);
+ } else
+ fprintf(stdout, "\nUse \"quit\" to exit\n");
+}
+
static char quit_help[] =
"Usage: quit\n"
" Exits Asterisk.\n";
+static char shutdown_help[] =
+"Usage: shutdown\n"
+" Shuts down a running Asterisk PBX.\n";
+
static int handle_quit(int fd, int argc, char *argv[])
{
if (argc != 1)
@@ -150,22 +392,135 @@ static int handle_quit(int fd, int argc, char *argv[])
#define ASTERISK_PROMPT "*CLI> "
+#define ASTERISK_PROMPT2 "%s*CLI> "
+
static struct ast_cli_entry quit = { { "quit", NULL }, handle_quit, "Exit Asterisk", quit_help };
+static struct ast_cli_entry astshutdown = { { "shutdown", NULL }, handle_quit, "Shut down an Asterisk PBX", shutdown_help };
+
static char *cli_generator(char *text, int state)
{
return ast_cli_generator(rl_line_buffer, text, state);
}
+static char *console_cli_generator(char *text, int state)
+{
+ char buf[1024];
+ int res;
+#if 0
+ fprintf(stderr, "Searching for '%s', %s %d\n", rl_line_buffer, text, state);
+#endif
+ snprintf(buf, sizeof(buf),"_COMMAND COMPLETE \"%s\" \"%s\" %d", rl_line_buffer, text, state);
+ fdprint(ast_consock, buf);
+ res = read(ast_consock, buf, sizeof(buf));
+ buf[res] = '\0';
+#if 0
+ printf("res is %d, buf is '%s'\n", res, buf);
+#endif
+ if (strncmp(buf, "NULL", 4))
+ return strdup(buf);
+ else
+ return NULL;
+}
+
+static void ast_remotecontrol(char * data)
+{
+ char buf[80];
+ int res;
+ int max;
+ int lastpos = 0;
+ fd_set rfds;
+ char filename[80] = "";
+ char *hostname;
+ char *cpid;
+ char *version;
+ int pid;
+ char tmp[80];
+ read(ast_consock, buf, sizeof(buf));
+ if (data) {
+ write(ast_consock, data, strlen(data) + 1);
+ return;
+ }
+ hostname = strtok(buf, "/");
+ cpid = strtok(NULL, "/");
+ version = strtok(NULL, "/");
+ if (!version)
+ version = "<Version Unknown>";
+ strtok(hostname, ".");
+ if (cpid)
+ pid = atoi(cpid);
+ else
+ pid = -1;
+ snprintf(tmp, sizeof(tmp), "set verbose atleast %d", option_verbose);
+ fdprint(ast_consock, tmp);
+ ast_verbose("Connected to Asterisk %s currently running on %s (pid = %d)\n", version, hostname, pid);
+ snprintf(tmp, sizeof(tmp), ASTERISK_PROMPT2, hostname);
+ if (getenv("HOME"))
+ snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
+ if (strlen(filename))
+ read_history(filename);
+ ast_cli_register(&quit);
+ ast_cli_register(&astshutdown);
+ rl_callback_handler_install(tmp, remoteconsolehandler);
+ rl_completion_entry_function = (Function *)console_cli_generator;
+ for(;;) {
+ FD_ZERO(&rfds);
+ FD_SET(ast_consock, &rfds);
+ FD_SET(STDIN_FILENO, &rfds);
+ max = ast_consock;
+ if (STDIN_FILENO > max)
+ max = STDIN_FILENO;
+ res = select(max + 1, &rfds, NULL, NULL, NULL);
+ if (res < 0) {
+ if (errno == EINTR)
+ continue;
+ ast_log(LOG_ERROR, "select failed: %s\n", strerror(errno));
+ break;
+ }
+ if (FD_ISSET(STDIN_FILENO, &rfds)) {
+ rl_callback_read_char();
+ if (strlen(cmd)) {
+ res = write(ast_consock, cmd, strlen(cmd) + 1);
+ if (res < 1) {
+ ast_log(LOG_WARNING, "Unable to write: %s\n", strerror(errno));
+ break;
+ }
+ strcpy(cmd, "");
+ }
+ }
+ if (FD_ISSET(ast_consock, &rfds)) {
+ res = read(ast_consock, buf, sizeof(buf));
+ if (res < 1)
+ break;
+ buf[res] = 0;
+ if (!lastpos)
+ write(STDOUT_FILENO, "\r", 2);
+ write(STDOUT_FILENO, buf, res);
+ if ((buf[res-1] == '\n') || (buf[res-2] == '\n')) {
+ rl_forced_update_display();
+ lastpos = 0;
+ } else {
+ lastpos = 1;
+ }
+ }
+ }
+ printf("\nDisconnected from Asterisk server\n");
+}
+
int main(int argc, char *argv[])
{
char c;
fd_set rfds;
int res;
+ int pid;
char filename[80] = "";
char hostname[256];
+ char * xarg = NULL;
+ sigset_t sigs;
+
if (gethostname(hostname, sizeof(hostname)))
strncpy(hostname, "<Unknown>", sizeof(hostname));
+ mainpid = getpid();
ast_ulaw_init();
callerid_init();
if (getenv("HOME"))
@@ -176,7 +531,7 @@ int main(int argc, char *argv[])
exit(1);
}
/* Check for options */
- while((c=getopt(argc, argv, "dvqpc")) != EOF) {
+ while((c=getopt(argc, argv, "fdvqprcx:")) != EOF) {
switch(c) {
case 'd':
option_debug++;
@@ -186,6 +541,13 @@ int main(int argc, char *argv[])
option_console++;
option_nofork++;
break;
+ case 'f':
+ option_nofork++;
+ break;
+ case 'r':
+ option_remote++;
+ option_nofork++;
+ break;
case 'p':
option_highpriority++;
break;
@@ -196,14 +558,60 @@ int main(int argc, char *argv[])
case 'q':
option_quiet++;
break;
+ case 'x':
+ option_exec++;
+ xarg = optarg;
+ break;
case '?':
exit(1);
}
}
- ast_register_verbose(console_verboser);
+
+ if (ast_tryconnect()) {
+ /* One is already running */
+ if (option_remote) {
+ if (option_exec) {
+ ast_remotecontrol(xarg);
+ quit_handler(0);
+ exit(0);
+ }
+ ast_register_verbose(console_verboser);
+ ast_verbose( "Asterisk " ASTERISK_VERSION ", Copyright (C) 1999-2001 Linux Support Services, Inc.\n");
+ ast_verbose( "Written by Mark Spencer <markster@linux-support.net>\n");
+ ast_verbose( "=========================================================================\n");
+ ast_remotecontrol(NULL);
+ quit_handler(0);
+ exit(0);
+ } else {
+ ast_log(LOG_ERROR, "Asterisk already running on %s. Use 'asterisk -r' to connect.\n", AST_SOCKET);
+ exit(1);
+ }
+ } else if (option_remote || option_exec) {
+ ast_log(LOG_ERROR, "Unable to connect to remote asterisk\n");
+ exit(1);
+ }
+ if (!option_verbose && !option_console && !option_debug) {
+ pid = fork();
+ if (pid < 0) {
+ ast_log(LOG_ERROR, "Unable to fork(): %s\n", strerror(errno));
+ exit(1);
+ }
+ if (pid)
+ exit(0);
+ }
+ ast_makesocket();
+ sigemptyset(&sigs);
+ sigaddset(&sigs, SIGHUP);
+ sigaddset(&sigs, SIGTERM);
+ sigaddset(&sigs, SIGINT);
+ sigaddset(&sigs, SIGPIPE);
+ sigaddset(&sigs, SIGWINCH);
+ pthread_sigmask(SIG_BLOCK, &sigs, NULL);
+ if (option_console || option_verbose || option_remote)
+ ast_register_verbose(console_verboser);
/* Print a welcome message if desired */
if (option_verbose || option_console) {
- ast_verbose( "Asterisk, Copyright (C) 1999 Mark Spencer\n");
+ ast_verbose( "Asterisk " ASTERISK_VERSION ", Copyright (C) 1999-2001 Linux Support Services, Inc.\n");
ast_verbose( "Written by Mark Spencer <markster@linux-support.net>\n");
ast_verbose( "=========================================================================\n");
}
@@ -212,7 +620,8 @@ int main(int argc, char *argv[])
signal(SIGURG, urg_handler);
signal(SIGINT, quit_handler);
signal(SIGTERM, quit_handler);
- signal(SIGHUP, quit_handler);
+ signal(SIGHUP, hup_handler);
+ signal(SIGPIPE, pipe_handler);
if (set_priority(option_highpriority))
exit(1);
if (init_logger())
@@ -228,12 +637,14 @@ int main(int argc, char *argv[])
if (option_verbose || option_console)
ast_verbose( "Asterisk Ready.\n");
fully_booted = 1;
+ pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
+ ast_cli_register(&astshutdown);
if (option_console) {
/* Console stuff now... */
/* Register our quit function */
char title[256];
set_icon("Asterisk");
- snprintf(title, sizeof(title), "Asterisk Console on '%s' (pid %d)", hostname, getpid());
+ snprintf(title, sizeof(title), "Asterisk Console on '%s' (pid %d)", hostname, mainpid);
set_title(title);
ast_cli_register(&quit);
consolethread = pthread_self();