2 * Asterisk -- A telephony toolkit for Linux.
4 * Top level source file for asterisk
6 * Copyright (C) 1999, Mark Spencer
8 * Mark Spencer <markster@linux-support.net>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
16 #include <asterisk/logger.h>
17 #include <asterisk/options.h>
18 #include <asterisk/cli.h>
19 #include <asterisk/channel.h>
20 #include <asterisk/ulaw.h>
21 #include <asterisk/callerid.h>
22 #include <asterisk/module.h>
27 #include <sys/socket.h>
29 #include <sys/select.h>
32 #include <readline/readline.h>
33 #include <readline/history.h>
36 #define AST_MAX_CONNECTS 128
44 int option_highpriority=0;
49 static int ast_socket = -1; /* UNIX Socket for allowing remote control */
50 static int ast_consock = -1; /* UNIX Socket for controlling another asterisk */
53 int fd; /* File descriptor */
55 pthread_t t; /* Thread of handler */
58 struct console consoles[AST_MAX_CONNECTS];
60 char defaultlanguage[MAX_LANGUAGE] = DEFAULT_LANGUAGE;
62 static int fdprint(int fd, char *s)
64 return write(fd, s, strlen(s) + 1);
67 static void network_verboser(char *s, int pos, int replace, int complete)
70 for (x=0;x<AST_MAX_CONNECTS; x++) {
71 if (consoles[x].fd > -1)
72 fdprint(consoles[x].p[1], s);
76 static pthread_t lthread;
78 static void *netconsole(void *vconsole)
80 struct console *con = vconsole;
87 if (gethostname(hostname, sizeof(hostname)))
88 strncpy(hostname, "<Unknown>", sizeof(hostname));
89 snprintf(tmp, sizeof(tmp), "%s/%d/%s\n", hostname, mainpid, ASTERISK_VERSION);
90 fdprint(con->fd, tmp);
93 FD_SET(con->fd, &rfds);
94 FD_SET(con->p[0], &rfds);
98 res = select(max + 1, &rfds, NULL, NULL, NULL);
100 ast_log(LOG_WARNING, "select returned < 0: %s\n", strerror(errno));
103 if (FD_ISSET(con->fd, &rfds)) {
104 res = read(con->fd, tmp, sizeof(tmp));
108 ast_cli_command(con->fd, tmp);
110 if (FD_ISSET(con->p[0], &rfds)) {
111 res = read(con->p[0], tmp, sizeof(tmp));
113 ast_log(LOG_ERROR, "read returned %d\n", res);
116 res = write(con->fd, tmp, res);
121 if (option_verbose > 2)
122 ast_verbose(VERBOSE_PREFIX_3 "Remote UNIX connection disconnected\n");
131 static void *listener(void *unused)
133 struct sockaddr_un sun;
138 pthread_attr_init(&attr);
139 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
142 s = accept(ast_socket, (struct sockaddr *)&sun, &len);
144 ast_log(LOG_WARNING, "Accept retured %d: %s\n", s, strerror(errno));
146 for (x=0;x<AST_MAX_CONNECTS;x++) {
147 if (consoles[x].fd < 0) {
148 if (pipe(consoles[x].p)) {
149 ast_log(LOG_ERROR, "Unable to create pipe: %s\n", strerror(errno));
151 fdprint(s, "Server failed to create pipe\n");
156 if (pthread_create(&consoles[x].t, &attr, netconsole, &consoles[x])) {
157 ast_log(LOG_ERROR, "Unable to spawn thread to handle connection\n");
159 fdprint(s, "Server failed to spawn thread\n");
165 if (x >= AST_MAX_CONNECTS) {
166 fdprint(s, "No more connections allowed\n");
167 ast_log(LOG_WARNING, "No more connections allowed\n");
169 } else if (consoles[x].fd > -1) {
170 if (option_verbose > 2)
171 ast_verbose(VERBOSE_PREFIX_3 "Remote UNIX connection\n");
178 static int ast_makesocket(void)
180 struct sockaddr_un sun;
183 for (x=0;x<AST_MAX_CONNECTS;x++)
186 ast_socket = socket(PF_LOCAL, SOCK_STREAM, 0);
187 if (ast_socket < 0) {
188 ast_log(LOG_WARNING, "Unable to create control socket: %s\n", strerror(errno));
191 memset(&sun, 0, sizeof(sun));
192 sun.sun_family = AF_LOCAL;
193 strncpy(sun.sun_path, AST_SOCKET, sizeof(sun.sun_path));
194 res = bind(ast_socket, (struct sockaddr *)&sun, sizeof(sun));
196 ast_log(LOG_WARNING, "Unable to bind socket to %s: %s\n", AST_SOCKET, strerror(errno));
201 res = listen(ast_socket, 2);
203 ast_log(LOG_WARNING, "Unable to listen on socket %s: %s\n", AST_SOCKET, strerror(errno));
208 ast_register_verbose(network_verboser);
209 pthread_create(<hread, NULL, listener, NULL);
213 static int ast_tryconnect(void)
215 struct sockaddr_un sun;
217 ast_consock = socket(PF_LOCAL, SOCK_STREAM, 0);
218 if (ast_consock < 0) {
219 ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
222 memset(&sun, 0, sizeof(sun));
223 sun.sun_family = AF_LOCAL;
224 strncpy(sun.sun_path, AST_SOCKET, sizeof(sun.sun_path));
225 res = connect(ast_consock, (struct sockaddr *)&sun, sizeof(sun));
234 static void urg_handler(int num)
236 /* Called by soft_hangup to interrupt the select, read, or other
237 system call. We don't actually need to do anything though. */
239 ast_log(LOG_DEBUG, "Urgent handler\n");
240 signal(num, urg_handler);
244 static void hup_handler(int num)
246 if (option_verbose > 1)
247 ast_verbose(VERBOSE_PREFIX_2 "Received HUP signal -- Reloading configs\n");
252 static void pipe_handler(int num)
256 static void set_title(char *text)
258 /* Set an X-term or screen title */
259 if (getenv("TERM") && strstr(getenv("TERM"), "xterm"))
260 fprintf(stdout, "\033]2;%s\007", text);
263 static void set_icon(char *text)
265 if (getenv("TERM") && strstr(getenv("TERM"), "xterm"))
266 fprintf(stdout, "\033]1;%s\007", text);
269 static int set_priority(int pri)
271 struct sched_param sched;
272 memset(&sched, 0, sizeof(sched));
273 /* We set ourselves to a high priority, that we might pre-empt everything
274 else. If your PBX has heavy activity on it, this is a good thing. */
276 sched.sched_priority = 10;
277 if (sched_setscheduler(0, SCHED_RR, &sched)) {
278 ast_log(LOG_WARNING, "Unable to set high priority\n");
282 ast_verbose("Set to realtime thread\n");
284 sched.sched_priority = 0;
285 if (sched_setscheduler(0, SCHED_OTHER, &sched)) {
286 ast_log(LOG_WARNING, "Unable to set normal priority\n");
293 static void quit_handler(int num)
295 char filename[80] = "";
296 if (option_console || option_remote) {
298 snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
299 if (strlen(filename))
300 write_history(filename);
301 rl_callback_handler_remove();
304 if (option_verbose && option_console)
305 ast_verbose("Asterisk ending (%d).\n", num);
306 else if (option_debug)
307 ast_log(LOG_DEBUG, "Asterisk ending (%d).\n", num);
310 if (ast_consock > -1)
318 static pthread_t consolethread = -1;
320 static void console_verboser(char *s, int pos, int replace, int complete)
322 /* Return to the beginning of the line */
324 fprintf(stdout, "\r");
325 fprintf(stdout, s + pos);
328 /* Wake up a select()ing console */
329 pthread_kill(consolethread, SIGURG);
332 static void consolehandler(char *s)
334 /* Called when readline data is available */
337 /* Give the console access to the shell */
343 system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
345 ast_cli_command(STDOUT_FILENO, s);
346 if (!strcasecmp(s, "help"))
347 fprintf(stdout, " !<command> Executes a given shell command\n");
349 fprintf(stdout, "\nUse \"quit\" to exit\n");
353 static char cmd[1024];
355 static void remoteconsolehandler(char *s)
357 /* Called when readline data is available */
360 /* Give the console access to the shell */
366 system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
368 strncpy(cmd, s, sizeof(cmd));
369 if (!strcasecmp(s, "help"))
370 fprintf(stdout, " !<command> Executes a given shell command\n");
371 if (!strcasecmp(s, "quit"))
374 fprintf(stdout, "\nUse \"quit\" to exit\n");
377 static char quit_help[] =
379 " Exits Asterisk.\n";
381 static char shutdown_help[] =
383 " Shuts down a running Asterisk PBX.\n";
385 static int handle_quit(int fd, int argc, char *argv[])
388 return RESULT_SHOWUSAGE;
390 return RESULT_SUCCESS;
393 #define ASTERISK_PROMPT "*CLI> "
395 #define ASTERISK_PROMPT2 "%s*CLI> "
397 static struct ast_cli_entry quit = { { "quit", NULL }, handle_quit, "Exit Asterisk", quit_help };
399 static struct ast_cli_entry astshutdown = { { "shutdown", NULL }, handle_quit, "Shut down an Asterisk PBX", shutdown_help };
401 static char *cli_generator(char *text, int state)
403 return ast_cli_generator(rl_line_buffer, text, state);
406 static char *console_cli_generator(char *text, int state)
411 fprintf(stderr, "Searching for '%s', %s %d\n", rl_line_buffer, text, state);
413 snprintf(buf, sizeof(buf),"_COMMAND COMPLETE \"%s\" \"%s\" %d", rl_line_buffer, text, state);
414 fdprint(ast_consock, buf);
415 res = read(ast_consock, buf, sizeof(buf));
418 printf("res is %d, buf is '%s'\n", res, buf);
420 if (strncmp(buf, "NULL", 4))
426 static void ast_remotecontrol(char * data)
433 char filename[80] = "";
439 read(ast_consock, buf, sizeof(buf));
441 write(ast_consock, data, strlen(data) + 1);
444 hostname = strtok(buf, "/");
445 cpid = strtok(NULL, "/");
446 version = strtok(NULL, "/");
448 version = "<Version Unknown>";
449 strtok(hostname, ".");
454 snprintf(tmp, sizeof(tmp), "set verbose atleast %d", option_verbose);
455 fdprint(ast_consock, tmp);
456 ast_verbose("Connected to Asterisk %s currently running on %s (pid = %d)\n", version, hostname, pid);
457 snprintf(tmp, sizeof(tmp), ASTERISK_PROMPT2, hostname);
459 snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
460 if (strlen(filename))
461 read_history(filename);
462 ast_cli_register(&quit);
463 ast_cli_register(&astshutdown);
464 rl_callback_handler_install(tmp, remoteconsolehandler);
465 rl_completion_entry_function = (Function *)console_cli_generator;
468 FD_SET(ast_consock, &rfds);
469 FD_SET(STDIN_FILENO, &rfds);
471 if (STDIN_FILENO > max)
473 res = select(max + 1, &rfds, NULL, NULL, NULL);
477 ast_log(LOG_ERROR, "select failed: %s\n", strerror(errno));
480 if (FD_ISSET(STDIN_FILENO, &rfds)) {
481 rl_callback_read_char();
483 res = write(ast_consock, cmd, strlen(cmd) + 1);
485 ast_log(LOG_WARNING, "Unable to write: %s\n", strerror(errno));
491 if (FD_ISSET(ast_consock, &rfds)) {
492 res = read(ast_consock, buf, sizeof(buf));
497 write(STDOUT_FILENO, "\r", 2);
498 write(STDOUT_FILENO, buf, res);
499 if ((buf[res-1] == '\n') || (buf[res-2] == '\n')) {
500 rl_forced_update_display();
507 printf("\nDisconnected from Asterisk server\n");
510 int main(int argc, char *argv[])
516 char filename[80] = "";
521 if (gethostname(hostname, sizeof(hostname)))
522 strncpy(hostname, "<Unknown>", sizeof(hostname));
527 snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
528 /* Check if we're root */
530 ast_log(LOG_ERROR, "Must be run as root\n");
533 /* Check for options */
534 while((c=getopt(argc, argv, "fdvqprcx:")) != EOF) {
552 option_highpriority++;
570 if (ast_tryconnect()) {
571 /* One is already running */
574 ast_remotecontrol(xarg);
578 ast_register_verbose(console_verboser);
579 ast_verbose( "Asterisk " ASTERISK_VERSION ", Copyright (C) 1999-2001 Linux Support Services, Inc.\n");
580 ast_verbose( "Written by Mark Spencer <markster@linux-support.net>\n");
581 ast_verbose( "=========================================================================\n");
582 ast_remotecontrol(NULL);
586 ast_log(LOG_ERROR, "Asterisk already running on %s. Use 'asterisk -r' to connect.\n", AST_SOCKET);
589 } else if (option_remote || option_exec) {
590 ast_log(LOG_ERROR, "Unable to connect to remote asterisk\n");
593 if (!option_verbose && !option_console && !option_debug) {
596 ast_log(LOG_ERROR, "Unable to fork(): %s\n", strerror(errno));
604 sigaddset(&sigs, SIGHUP);
605 sigaddset(&sigs, SIGTERM);
606 sigaddset(&sigs, SIGINT);
607 sigaddset(&sigs, SIGPIPE);
608 sigaddset(&sigs, SIGWINCH);
609 pthread_sigmask(SIG_BLOCK, &sigs, NULL);
610 if (option_console || option_verbose || option_remote)
611 ast_register_verbose(console_verboser);
612 /* Print a welcome message if desired */
613 if (option_verbose || option_console) {
614 ast_verbose( "Asterisk " ASTERISK_VERSION ", Copyright (C) 1999-2001 Linux Support Services, Inc.\n");
615 ast_verbose( "Written by Mark Spencer <markster@linux-support.net>\n");
616 ast_verbose( "=========================================================================\n");
618 if (option_console && !option_verbose)
619 ast_verbose("[ Booting...");
620 signal(SIGURG, urg_handler);
621 signal(SIGINT, quit_handler);
622 signal(SIGTERM, quit_handler);
623 signal(SIGHUP, hup_handler);
624 signal(SIGPIPE, pipe_handler);
625 if (set_priority(option_highpriority))
633 /* We might have the option of showing a console, but for now just
635 if (option_console && !option_verbose)
637 if (option_verbose || option_console)
638 ast_verbose( "Asterisk Ready.\n");
640 pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
641 ast_cli_register(&astshutdown);
642 if (option_console) {
643 /* Console stuff now... */
644 /* Register our quit function */
646 set_icon("Asterisk");
647 snprintf(title, sizeof(title), "Asterisk Console on '%s' (pid %d)", hostname, mainpid);
649 ast_cli_register(&quit);
650 consolethread = pthread_self();
651 if (strlen(filename))
652 read_history(filename);
653 rl_callback_handler_install(ASTERISK_PROMPT, consolehandler);
654 rl_completion_entry_function = (Function *)cli_generator;
657 FD_SET(STDIN_FILENO, &rfds);
658 res = select(STDIN_FILENO + 1, &rfds, NULL, NULL, NULL);
660 rl_callback_read_char();
661 } else if (res < 1) {
662 rl_forced_update_display();
668 select(0,NULL,NULL,NULL,NULL);