]> git.ipfire.org Git - pakfire.git/commitdiff
pty: Move filtering escape characters to the PTY
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 14 Feb 2025 17:04:32 +0000 (17:04 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 14 Feb 2025 17:07:05 +0000 (17:07 +0000)
This is probably the place where this should live. We are now processing
cursor positioning events inside the same line and we are filtering out
any escape sequences that we don't know.

It is a bit sad to lose the colors, but I don't have to have two places
where we process this. At least this is going to be more robust and we
can be sure to not forward anything malicious to the controlling
terminal.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/pakfire/log_file.c
src/pakfire/pty.c

index 2733d94650bfc327360653643f314c4905e07cc1..7a37a075468570d3ede346aeac491f405af0ba3d 100644 (file)
@@ -188,104 +188,7 @@ int pakfire_log_file_close(struct pakfire_log_file* self) {
        return 0;
 }
 
-#define pakfire_log_file_process_line(file, line, buffer) \
-       __pakfire_log_file_process_line(file, line, sizeof(line), buffer)
-
-static int __pakfire_log_file_process_line(struct pakfire_log_file* self,
-               char* line, ssize_t length, const char* p) {
-       char* e = NULL;
-       int num;
-
-       // The cursor position
-       int cursor = 0;
-
-       while (*p) {
-               // Match the beginning of an ESC sequence
-               switch (*p) {
-                       case '\x1B':
-                               p++;
-
-                               // Match CSI sequences
-                               switch (*p) {
-                                       case '[':
-                                               p++;
-
-                                               // Parse the number
-                                               num = strtoul(p, &e, 10);
-
-                                               // Continue from after the number
-                                               p = e;
-
-                                               switch (*p) {
-                                                       // Move the cursor to the left
-                                                       case 'D':
-                                                               cursor -= (num > 0) ? num : 1;
-
-                                                               // Don't go too far left
-                                                               if (cursor < 0)
-                                                                       cursor = 0;
-                                                               break;
-
-                                                       // Move the cursor to the right
-                                                       case 'C':
-                                                               cursor += (num > 0) ? num : 1;
-
-                                                               // Don't go too far right
-                                                               if (cursor >= length)
-                                                                       cursor = length - 1;
-                                                               break;
-
-                                                       // Move to a certain column
-                                                       case 'G':
-                                                               cursor = (num > 0) ? num - 1 : 0;
-
-                                                               // Don't go too far right
-                                                               if (cursor >= length)
-                                                                       cursor = length - 1;
-                                                               break;
-                                               }
-
-                                               // Skip the final character
-                                               p++;
-                                               break;
-                               }
-                               break;
-
-                       // Backspace
-                       case '\x08':
-                               if (cursor > 0) {
-                                       cursor--;
-
-                                       // Erase the previous character
-                                       line[cursor] = ' ';
-                               }
-                               p++;
-                               break;
-
-                       // Carriage Return
-                       case '\x0d':
-                               // Erase everything up to the cursor
-                               for (int i = 0; i < cursor; i++)
-                                       line[i] = '\0';
-
-                               cursor = 0;
-                               p++;
-                               break;
-
-                       // Normal characters
-                       default:
-                               if (cursor < length - 1)
-                                       line[cursor++] = *p;
-                               p++;
-                               break;
-               }
-       }
-
-       return 0;
-}
-
 int pakfire_log_file_write(struct pakfire_log_file* self, const char* buffer, ssize_t length) {
-       char line[MAX_LINE_LENGTH] = {};
        int r;
 
        // Check inputs
@@ -296,14 +199,13 @@ int pakfire_log_file_write(struct pakfire_log_file* self, const char* buffer, ss
        if (unlikely(!self->f))
                return -EBADF;
 
-       // Process any ANSI Escape codes
-       r = pakfire_log_file_process_line(self, line, buffer);
-       if (r < 0)
-               return r;
+       // Automatically determine the length
+       if (unlikely(length < 0))
+               length = strlen(buffer);
 
        // Write the buffer to the file
-       r = fprintf(self->f, "%s", line);
-       if (unlikely(r < 0)) {
+       r = fwrite(buffer, 1, length, self->f);
+       if (unlikely(r < length)) {
                ERROR(self->ctx, "Could not write to log file: %m\n");
                return -errno;
        }
index 30d6f26fe7e111b56c2cfc39b06390a0f85c48e3..852266768be25d3f2cf6c6093c3d8b2d4eb01ad9 100644 (file)
 #include <pakfire/string.h>
 #include <pakfire/util.h>
 
+#define MAX_LINE_LENGTH 4096
+
 struct pakfire_pty_stdio {
        // File Descriptor
        int fd;
        unsigned close_fd:1;
 
        // Buffer
-       char buffer[4096];
+       char buffer[MAX_LINE_LENGTH];
        size_t buffered;
 
        // Terminal Attributes
@@ -423,6 +425,141 @@ static int pakfire_pty_fill_buffer(struct pakfire_pty* pty, int fd, struct pakfi
        return bytes_read;
 }
 
+#define pakfire_pty_sanitize_line(pty, line, buffer, buffer_length) \
+       __pakfire_pty_sanitize_line(pty, line, sizeof(line), buffer, buffer_length)
+
+static int __pakfire_pty_sanitize_line(struct pakfire_pty* self,
+               char* line, ssize_t line_length, const char* buffer, ssize_t length) {
+       ssize_t i = 0;
+       int num;
+
+       // The cursor position
+       int cursor = 0;
+
+       while (i < length) {
+               switch (buffer[i]) {
+                       // Escape
+                       case '\x1b':
+                               // Skip the escape character
+                               i++;
+
+                               // CSI
+                               if (i < length && buffer[i] == '[') {
+                                       i++;
+
+                                       // Reset number
+                                       num = 0;
+
+                                       // Parse the number
+                                       while (i < length) {
+                                               // Skip any ';'
+                                               if (buffer[i] == ';') {
+                                                       i++;
+                                                       continue;
+
+                                               // Parse any digits
+                                               } else if (isdigit(buffer[i])) {
+                                                       num = num * 10 + (buffer[i] - '0');
+                                                       i++;
+                                                       continue;
+                                               }
+
+                                               // Break on all other characters
+                                               break;
+                                       }
+
+                                       // Parse the command
+                                       if (i < length) {
+                                               char command = buffer[i++];
+
+                                               switch (command) {
+                                                       // Move the cursor to the left
+                                                       case 'D':
+                                                               cursor -= (num > 0) ? num : 1;
+
+                                                               // Don't go too far left
+                                                               if (cursor < 0)
+                                                                       cursor = 0;
+                                                               break;
+
+                                                       // Move the cursor to the right
+                                                       case 'C':
+                                                               cursor += (num > 0) ? num : 1;
+
+                                                               // Don't go too far right
+                                                               if (cursor >= length)
+                                                                       cursor = length - 1;
+                                                               break;
+
+                                                       // Move to a certain column
+                                                       case 'G':
+                                                               cursor = (num > 0) ? num - 1 : 0;
+
+                                                               // Don't go too far right
+                                                               if (cursor >= length)
+                                                                       cursor = length - 1;
+                                                               break;
+
+                                                       // Ignore any other sequences
+                                                       default:
+                                                               break;
+                                               }
+                                       }
+                               }
+                               break;
+
+                       // Backspace
+                       case '\x08':
+                               if (cursor > 0) {
+                                       cursor--;
+
+                                       // Erase the previous character
+                                       line[cursor] = ' ';
+                               }
+                               i++;
+                               break;
+
+                       // Carriage Return
+                       case '\x0d':
+                               // Erase everything up to the cursor
+                               memset(line, '\0', cursor);
+
+                               cursor = 0;
+                               i++;
+                               break;
+
+                       // Normal characters
+                       default:
+                               if (cursor < line_length - 1)
+                                       line[cursor++] = buffer[i];
+                               i++;
+                               break;
+               }
+       }
+
+       return 0;
+}
+
+static ssize_t pakfire_pty_send_line(struct pakfire_pty* self,
+               struct pakfire_pty_stdio* stdio, const char* buffer, ssize_t length) {
+       char line[MAX_LINE_LENGTH] = {};
+       int r;
+
+       // Sanitize the line
+       r = pakfire_pty_sanitize_line(self, line, buffer, length);
+       if (r < 0)
+               return r;
+
+       // Call the callback
+       r = stdio->callbacks.stdout_callback(self->ctx,
+                       stdio->callbacks.data, line, strlen(line));
+       if (r < 0)
+               return r;
+
+       // We have consumed the entire line
+       return length;
+}
+
 /*
        Writes as much data as possible from the buffer
 */
@@ -448,9 +585,8 @@ static int pakfire_pty_drain_buffer(struct pakfire_pty* pty, int fd, struct pakf
                        eol = stdio->buffer + stdio->buffered - 1;
                }
 
-               // Call the callback
-               bytes_written = stdio->callbacks.stdout_callback(
-                               pty->ctx, stdio->callbacks.data, stdio->buffer, eol - stdio->buffer + 1);
+               // Send the line
+               bytes_written = pakfire_pty_send_line(pty, stdio, stdio->buffer, eol - stdio->buffer + 1);
 
                // Abort on error
                if (bytes_written < 0)