]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Handle multiple commands per connection via netconsole
authorTerry Wilson <twilson@digium.com>
Thu, 19 Apr 2012 14:26:33 +0000 (14:26 +0000)
committerTerry Wilson <twilson@digium.com>
Thu, 19 Apr 2012 14:26:33 +0000 (14:26 +0000)
Asterisk would accept multiple NULL-delimited CLI commands via the
netconsole socket, but would occasionally miss a command due to the
command not being completely read into the buffer. This patch ensures
that any partial commands get moved to the front of the read buffer,
appended to, and properly sent.

(closes issue ASTERISK-18308)
Review: https://reviewboard.asterisk.org/r/1876/

git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.8@362536 65c4cc65-6c06-0410-ace0-fbb531ad65f3

main/asterisk.c

index 89f47420aac3c484195db298876838e2248a531c..9c6da434e2791d182964959761c5fbab17e6a7b1 100644 (file)
@@ -1257,14 +1257,17 @@ static void *netconsole(void *vconsole)
 {
        struct console *con = vconsole;
        char hostname[MAXHOSTNAMELEN] = "";
-       char tmp[512];
+       char inbuf[512];
+       char outbuf[512];
+       const char *end_buf = inbuf + sizeof(inbuf);
+       char *start_read = inbuf;
        int res;
        struct pollfd fds[2];
-       
+
        if (gethostname(hostname, sizeof(hostname)-1))
                ast_copy_string(hostname, "<Unknown>", sizeof(hostname));
-       snprintf(tmp, sizeof(tmp), "%s/%ld/%s\n", hostname, (long)ast_mainpid, ast_get_version());
-       fdprint(con->fd, tmp);
+       snprintf(outbuf, sizeof(outbuf), "%s/%ld/%s\n", hostname, (long)ast_mainpid, ast_get_version());
+       fdprint(con->fd, outbuf);
        for (;;) {
                fds[0].fd = con->fd;
                fds[0].events = POLLIN;
@@ -1280,24 +1283,49 @@ static void *netconsole(void *vconsole)
                        continue;
                }
                if (fds[0].revents) {
-                       res = read_credentials(con->fd, tmp, sizeof(tmp) - 1, con);
-                       if (res < 1) {
+                       int cmds_read, bytes_read;
+                       if ((bytes_read = read_credentials(con->fd, start_read, end_buf - start_read, con)) < 1) {
                                break;
                        }
-                       tmp[res] = 0;
-                       if (strncmp(tmp, "cli quit after ", 15) == 0) {
-                               ast_cli_command_multiple_full(con->uid, con->gid, con->fd, res - 15, tmp + 15);
+                       /* XXX This will only work if it is the first command, and I'm not sure fixing it is worth the effort. */
+                       if (strncmp(inbuf, "cli quit after ", 15) == 0) {
+                               ast_cli_command_multiple_full(con->uid, con->gid, con->fd, bytes_read - 15, inbuf + 15);
                                break;
                        }
-                       ast_cli_command_multiple_full(con->uid, con->gid, con->fd, res, tmp);
+                       /* ast_cli_command_multiple_full will only process individual commands terminated by a
+                        * NULL and not trailing partial commands. */
+                       if (!(cmds_read = ast_cli_command_multiple_full(con->uid, con->gid, con->fd, bytes_read + start_read - inbuf, inbuf))) {
+                               /* No commands were read. We either have a short read on the first command
+                                * with space left, or a command that is too long */
+                               if (start_read + bytes_read < end_buf) {
+                                       start_read += bytes_read;
+                               } else {
+                                       ast_log(LOG_ERROR, "Command too long! Skipping\n");
+                                       start_read = inbuf;
+                               }
+                               continue;
+                       }
+                       if (start_read[bytes_read - 1] == '\0') {
+                               /* The read ended on a command boundary, start reading again at the head of inbuf */
+                               start_read = inbuf;
+                               continue;
+                       }
+                       /* If we get this far, we have left over characters that have not been processed.
+                        * Advance to the character after the last command read by ast_cli_command_multiple_full.
+                        * We are guaranteed to have at least cmds_read NULLs */
+                       while (cmds_read-- && (start_read = rawmemchr(start_read, '\0'))) {
+                               start_read++;
+                       }
+                       memmove(inbuf, start_read, end_buf - start_read);
+                       start_read = end_buf - start_read + inbuf;
                }
                if (fds[1].revents) {
-                       res = read_credentials(con->p[0], tmp, sizeof(tmp), con);
+                       res = read_credentials(con->p[0], outbuf, sizeof(outbuf), con);
                        if (res < 1) {
                                ast_log(LOG_ERROR, "read returned %d\n", res);
                                break;
                        }
-                       res = write(con->fd, tmp, res);
+                       res = write(con->fd, outbuf, res);
                        if (res < 1)
                                break;
                }