ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
io.c
Go to the documentation of this file.
1
6#include <ascii-chat/log/io.h>
9#include <unistd.h>
10#include <fcntl.h>
11#include <string.h>
12
13#define LOG_IO_BUFFER_SIZE 8192
14
16 log_io_t capture = {.saved_stdout_fd = -1, .saved_stderr_fd = -1, .pipe_fd = -1};
17
18 // Save original stdout and stderr
19 capture.saved_stdout_fd = dup(STDOUT_FILENO);
20 capture.saved_stderr_fd = dup(STDERR_FILENO);
21
22 if (capture.saved_stdout_fd < 0 || capture.saved_stderr_fd < 0) {
23 if (capture.saved_stdout_fd >= 0)
24 close(capture.saved_stdout_fd);
25 if (capture.saved_stderr_fd >= 0)
26 close(capture.saved_stderr_fd);
27 return (log_io_t){.saved_stdout_fd = -1, .saved_stderr_fd = -1, .pipe_fd = -1};
28 }
29
30 // Create a pipe for capturing output
31 int pipefd[2];
32#ifdef _WIN32
33 if (_pipe(pipefd, LOG_IO_BUFFER_SIZE, _O_BINARY) < 0) {
34#else
35 if (pipe(pipefd) < 0) {
36#endif
37 close(capture.saved_stdout_fd);
38 close(capture.saved_stderr_fd);
39 return (log_io_t){.saved_stdout_fd = -1, .saved_stderr_fd = -1, .pipe_fd = -1};
40 }
41
42 // pipefd[0] is read end, pipefd[1] is write end
43 int write_fd = pipefd[1];
44 capture.pipe_fd = pipefd[0];
45
46#ifndef _WIN32
47 // Make the read end non-blocking so we can drain without hanging
48 int flags = fcntl(capture.pipe_fd, F_GETFL, 0);
49 if (flags >= 0) {
50 fcntl(capture.pipe_fd, F_SETFL, flags | O_NONBLOCK);
51 }
52
53 // Make the write end non-blocking. LOG_IO redirects the process-global stderr,
54 // so ALL threads write to this pipe. If the pipe buffer fills (64KB on Linux),
55 // blocking writes would stall other threads and potentially deadlock (e.g. x265
56 // internal threads block on stderr while avcodec_open2 can't return). Non-blocking
57 // writes silently drop excess output (EAGAIN) which is acceptable for diagnostics.
58 int wflags = fcntl(write_fd, F_GETFL, 0);
59 if (wflags >= 0) {
60 fcntl(write_fd, F_SETFL, wflags | O_NONBLOCK);
61 }
62#endif
63
64 // Redirect both stdout and stderr to the pipe
65 if (dup2(write_fd, STDOUT_FILENO) < 0 || dup2(write_fd, STDERR_FILENO) < 0) {
66 close(write_fd);
67 close(capture.pipe_fd);
68 close(capture.saved_stdout_fd);
69 close(capture.saved_stderr_fd);
70 return (log_io_t){.saved_stdout_fd = -1, .saved_stderr_fd = -1, .pipe_fd = -1};
71 }
72
73 // Close the write end in the parent process (we only need it in redirected streams)
74 close(write_fd);
75
76 return capture;
77}
78
79void log_io_stop(log_io_t capture, const char *prefix) {
80 if (capture.saved_stdout_fd < 0 || capture.saved_stderr_fd < 0) {
81 return; // Capture was not started successfully
82 }
83
84 // Flush stdout/stderr BEFORE restoring to ensure all output is in the pipe
85 fflush(stdout);
86 fflush(stderr);
87
88 // Restore stdout and stderr
89 dup2(capture.saved_stdout_fd, STDOUT_FILENO);
90 dup2(capture.saved_stderr_fd, STDERR_FILENO);
91 close(capture.saved_stdout_fd);
92 close(capture.saved_stderr_fd);
93
94 // Read and log the captured output
95 if (capture.pipe_fd >= 0) {
96 char buffer[LOG_IO_BUFFER_SIZE];
97 ssize_t bytes_read;
98
99 // Read all available data from the pipe
100 while ((bytes_read = read(capture.pipe_fd, buffer, sizeof(buffer) - 1)) > 0) {
101 buffer[bytes_read] = '\0';
102
103 // Split by lines and log each line
104 char *line_start = buffer;
105 char *line_end;
106
107 while ((line_end = strchr(line_start, '\n')) != NULL) {
108 // Null-terminate the line (overwriting the newline)
109 *line_end = '\0';
110
111 // Log the line if it's not empty
112 if (line_start[0] != '\0') {
113 if (prefix) {
114 log_debug("[%s] %s", prefix, line_start);
115 } else {
116 log_debug("%s", line_start);
117 }
118 }
119
120 // Move to the next line
121 line_start = line_end + 1;
122 }
123
124 // Log any remaining partial line
125 if (line_start[0] != '\0') {
126 if (prefix) {
127 log_debug("[%s] %s", prefix, line_start);
128 } else {
129 log_debug("%s", line_start);
130 }
131 }
132 }
133
134 close(capture.pipe_fd);
135 }
136}
Platform abstraction layer umbrella header providing unified cross-platform API.
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
log_io_t log_io_start(void)
Start capturing stdout and stderr.
Definition io.c:15
void log_io_stop(log_io_t capture, const char *prefix)
Stop capturing and log the accumulated output.
Definition io.c:79
#define LOG_IO_BUFFER_SIZE
Definition io.c:13
Capture stdout/stderr and redirect to logging system.
📝 Logging API with multiple log levels and terminal output control
Opaque handle for IO capture state.
Definition io.h:29
int saved_stdout_fd
Saved stdout file descriptor.
Definition io.h:30
int pipe_fd
Read end of pipe for captured output.
Definition io.h:32
int saved_stderr_fd
Saved stderr file descriptor.
Definition io.h:31