Fix command completion and remove compile time warnings
[asterisk/asterisk.git] / asterisk.c
index 7adb027..a003093 100755 (executable)
@@ -28,6 +28,7 @@
 #include <asterisk/pbx.h>
 #include <asterisk/enum.h>
 #include <asterisk/rtp.h>
+#include <asterisk/app.h>
 #include <sys/resource.h>
 #include <fcntl.h>
 #include <stdio.h>
@@ -65,7 +66,7 @@ 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;
+int ast_mainpid;
 struct console {
        int fd;                                 /* File descriptor */
        int p[2];                               /* Pipe */
@@ -153,6 +154,42 @@ static int fdprint(int fd, const char *s)
        return write(fd, s, strlen(s) + 1);
 }
 
+int ast_safe_system(const char *s)
+{
+       /* XXX This function needs some optimization work XXX */
+       pid_t pid;
+       int x;
+       int res;
+       struct rusage rusage;
+       int status;
+       pid = fork();
+       if (pid == 0) {
+               /* Close file descriptors and launch system command */
+               for (x=STDERR_FILENO + 1; x<4096;x++) {
+                       close(x);
+               }
+               res = execl("/bin/sh", "/bin/sh", "-c", s, NULL);
+               exit(1);
+       } else if (pid > 0) {
+               for(;;) {
+                       res = wait4(pid, &status, 0, &rusage);
+                       if (res > -1) {
+                               if (WIFEXITED(status))
+                                       res = WEXITSTATUS(status);
+                               else
+                                       res = -1;
+                       } else {
+                               if (errno != EINTR) 
+                                       break;
+                       }
+               }
+       } else {
+               ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
+               res = -1;
+       }
+       return res;
+}
+
 /*
  * write the string to all attached console clients
  */
@@ -196,7 +233,7 @@ static void *netconsole(void *vconsole)
        
        if (gethostname(hostname, sizeof(hostname)))
                strncpy(hostname, "<Unknown>", sizeof(hostname)-1);
-       snprintf(tmp, sizeof(tmp), "%s/%d/%s\n", hostname, mainpid, ASTERISK_VERSION);
+       snprintf(tmp, sizeof(tmp), "%s/%d/%s\n", hostname, ast_mainpid, ASTERISK_VERSION);
        fdprint(con->fd, tmp);
        for(;;) {
                FD_ZERO(&rfds); 
@@ -607,9 +644,9 @@ static void consolehandler(char *s)
                /* The real handler for bang */
                if (s[0] == '!') {
                        if (s[1])
-                               system(s+1);
+                               ast_safe_system(s+1);
                        else
-                               system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
+                               ast_safe_system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
                } else 
                ast_cli_command(STDOUT_FILENO, s);
        } else
@@ -627,9 +664,9 @@ static int remoteconsolehandler(char *s)
                /* The real handler for bang */
                if (s[0] == '!') {
                        if (s[1])
-                               system(s+1);
+                               ast_safe_system(s+1);
                        else
-                               system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
+                               ast_safe_system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
                        ret = 1;
                }
                if ((strncasecmp(s, "quit", 4) == 0 || strncasecmp(s, "exit", 4) == 0) &&
@@ -842,9 +879,96 @@ static int ast_el_read_char(EditLine *el, char *cp)
 
 static char *cli_prompt(EditLine *el)
 {
-       static char prompt[80];
-
-       if (remotehostname)
+       static char prompt[200];
+       char *pfmt;
+
+       if ((pfmt = getenv("ASTERISK_PROMPT"))) {
+               char *t = pfmt, *p = prompt;
+               memset(prompt, 0, sizeof(prompt));
+               while (*t != '\0' && *p < sizeof(prompt)) {
+                       if (*t == '%') {
+                               t++;
+                               switch (*t) {
+                                       char hostname[256];
+                                       struct timeval tv;
+                                       struct tm tm;
+                                       FILE *LOADAVG;
+                                       case 'd': /* date */
+                                               memset(&tm, 0, sizeof(struct tm));
+                                               gettimeofday(&tv, NULL);
+                                               if (localtime_r(&(tv.tv_sec), &tm)) {
+                                                       strftime(p, sizeof(prompt) - strlen(prompt), "%Y-%m-%d", &tm);
+                                               }
+                                               break;
+                                       case 'h': /* hostname */
+                                               if (!gethostname(hostname, sizeof(hostname) - 1)) {
+                                                       strncat(p, hostname, sizeof(prompt) - strlen(prompt));
+                                               } else {
+                                                       strncat(p, "localhost", sizeof(prompt) - strlen(prompt));
+                                               }
+                                               break;
+#ifdef linux
+                                       case 'l': /* load avg */
+                                               t++;
+                                               if ((LOADAVG = fopen("/proc/loadavg", "r"))) {
+                                                       float avg1, avg2, avg3;
+                                                       int actproc, totproc, npid, which;
+                                                       fscanf(LOADAVG, "%f %f %f %d/%d %d",
+                                                               &avg1, &avg2, &avg3, &actproc, &totproc, &npid);
+                                                       if (sscanf(t, "%d", &which) == 1) {
+                                                               switch (which) {
+                                                                       case 1:
+                                                                               snprintf(p, sizeof(prompt) - strlen(prompt), "%.2f", avg1);
+                                                                               break;
+                                                                       case 2:
+                                                                               snprintf(p, sizeof(prompt) - strlen(prompt), "%.2f", avg2);
+                                                                               break;
+                                                                       case 3:
+                                                                               snprintf(p, sizeof(prompt) - strlen(prompt), "%.2f", avg3);
+                                                                               break;
+                                                                       case 4:
+                                                                               snprintf(p, sizeof(prompt) - strlen(prompt), "%d/%d", actproc, totproc);
+                                                                               break;
+                                                                       case 5:
+                                                                               snprintf(p, sizeof(prompt) - strlen(prompt), "%d", npid);
+                                                                               break;
+                                                               }
+                                                       }
+                                               }
+                                               break;
+#endif
+                                       case 't': /* time */
+                                               memset(&tm, 0, sizeof(struct tm));
+                                               gettimeofday(&tv, NULL);
+                                               if (localtime_r(&(tv.tv_sec), &tm)) {
+                                                       strftime(p, sizeof(prompt) - strlen(prompt), "%H:%M:%S", &tm);
+                                               }
+                                               break;
+                                       case '#': /* process console or remote? */
+                                               if (! option_remote) {
+                                                       strncat(p, "#", sizeof(prompt) - strlen(prompt));
+                                               } else {
+                                                       strncat(p, ">", sizeof(prompt) - strlen(prompt));
+                                               }
+                                               break;
+                                       case '%': /* literal % */
+                                               strncat(p, "%", sizeof(prompt) - strlen(prompt));
+                                               break;
+                                       case '\0': /* % is last character - prevent bug */
+                                               t--;
+                                               break;
+                               }
+                               while (*p != '\0') {
+                                       p++;
+                               }
+                               t++;
+                       } else {
+                               *p = *t;
+                               p++;
+                               t++;
+                       }
+               }
+       } else if (remotehostname)
                snprintf(prompt, sizeof(prompt), ASTERISK_PROMPT2, remotehostname);
        else
                snprintf(prompt, sizeof(prompt), ASTERISK_PROMPT);
@@ -861,12 +985,14 @@ static char **ast_el_strtoarr(char *buf)
         match_list_len = 1;
        while ( (retstr = strsep(&buf, " ")) != NULL) {
 
+               if (!strcmp(retstr, AST_CLI_COMPLETE_EOF))
+                       break;
                 if (matches + 1 >= match_list_len) {
                         match_list_len <<= 1;
                         match_list = realloc(match_list, match_list_len * sizeof(char *));
                }
 
-               match_list[matches++] = retstr;
+               match_list[matches++] = strdup(retstr);
        }
 
         if (!match_list)
@@ -940,7 +1066,7 @@ static char *cli_complete(EditLine *el, int ch)
        int nummatches = 0;
        char **matches;
        int retval = CC_ERROR;
-       char buf[1024];
+       char buf[2048];
        int res;
 
        LineInfo *lf = (LineInfo *)el_line(el);
@@ -967,12 +1093,32 @@ static char *cli_complete(EditLine *el, int ch)
                nummatches = atoi(buf);
 
                if (nummatches > 0) {
+                       char *mbuf;
+                       int mlen = 0, maxmbuf = 2048;
+                       /* Start with a 2048 byte buffer */
+                       mbuf = malloc(maxmbuf);
+                       if (!mbuf)
+                               return (char *)(CC_ERROR);
                        snprintf(buf, sizeof(buf),"_COMMAND MATCHESARRAY \"%s\" \"%s\"", lf->buffer, ptr); 
                        fdprint(ast_consock, buf);
-                       res = read(ast_consock, buf, sizeof(buf));
-                       buf[res] = '\0';
+                       res = 0;
+                       while (!strstr(mbuf, AST_CLI_COMPLETE_EOF) && res != -1) {
+                               if (mlen + 1024 > maxmbuf) {
+                                       /* Every step increment buffer 1024 bytes */
+                                       maxmbuf += 1024;
+                                       mbuf = realloc(mbuf, maxmbuf);
+                                       if (!mbuf)
+                                               return (char *)(CC_ERROR);
+                               }
+                               /* Only read 1024 bytes at a time */
+                               res = read(ast_consock, mbuf + mlen, 1024);
+                               if (res > 0)
+                                       mlen += res;
+                       }
+                       mbuf[mlen] = '\0';
 
-                       matches = ast_el_strtoarr(buf);
+                       matches = ast_el_strtoarr(mbuf);
+                       free(mbuf);
                } else
                        matches = (char **) NULL;
 
@@ -1048,6 +1194,8 @@ static int ast_el_initialize(void)
        el_set(el, EL_BIND, "^I", "ed-complete", NULL);
        /* Bind ? to command completion */
        el_set(el, EL_BIND, "?", "ed-complete", NULL);
+       /* Bind ^D to redisplay */
+       el_set(el, EL_BIND, "^D", "ed-redisplay", NULL);
 
        return 0;
 }
@@ -1264,9 +1412,14 @@ int main(int argc, char *argv[])
                _argv[x] = argv[x];
        _argv[x] = NULL;
 
+       /* if the progname is rasterisk consider it a remote console */
+       if ( argv[0] && (strstr(argv[0], "rasterisk")) != NULL)  {
+               option_remote++;
+               option_nofork++;
+       }
        if (gethostname(hostname, sizeof(hostname)))
                strncpy(hostname, "<Unknown>", sizeof(hostname)-1);
-       mainpid = getpid();
+       ast_mainpid = getpid();
        ast_ulaw_init();
        ast_alaw_init();
        callerid_init();
@@ -1495,7 +1648,7 @@ int main(int argc, char *argv[])
                /* Register our quit function */
                char title[256];
                set_icon("Asterisk");
-               snprintf(title, sizeof(title), "Asterisk Console on '%s' (pid %d)", hostname, mainpid);
+               snprintf(title, sizeof(title), "Asterisk Console on '%s' (pid %d)", hostname, ast_mainpid);
                set_title(title);
            ast_cli_register(&quit);
            ast_cli_register(&astexit);