ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
io.c File Reference

IO capture implementation. More...

Go to the source code of this file.

Macros

#define LOG_IO_BUFFER_SIZE   8192

Functions

log_io_t log_io_start (void)
 Start capturing stdout and stderr.
void log_io_stop (log_io_t capture, const char *prefix)
 Stop capturing and log the accumulated output.

Detailed Description

IO capture implementation.

Definition in file io.c.

Macro Definition Documentation

◆ LOG_IO_BUFFER_SIZE

#define LOG_IO_BUFFER_SIZE   8192

Definition at line 13 of file io.c.

Referenced by log_io_stop().

Function Documentation

◆ log_io_start()

log_io_t log_io_start ( void )

Start capturing stdout and stderr.

Redirects both stdout and stderr to a temporary file so their output can be captured and logged. Call log_io_stop() to end capture and log the accumulated output.

Returns
Capture handle (use with log_io_stop)
Note
If capture fails, returns a handle with saved_stdout_fd = -1
Do not use stdout/stderr directly while capturing

Definition at line 15 of file io.c.

15 {
16 log_io_t capture = {.saved_stdout_fd = -1, .saved_stderr_fd = -1, .output = NULL};
17
18 // A pipe cannot be drained until the wrapped call returns. A temporary file
19 // lets backend worker threads finish even when diagnostics exceed pipe capacity.
20 capture.output = platform_tmpfile();
21 if (!capture.output)
22 return capture;
23
24 fflush(stdout);
25 fflush(stderr);
26 capture.saved_stdout_fd = platform_dup(STDOUT_FILENO);
27 capture.saved_stderr_fd = platform_dup(STDERR_FILENO);
28 if (capture.saved_stdout_fd < 0 || capture.saved_stderr_fd < 0)
29 goto fail;
30
31 int output_fd = platform_fileno(capture.output);
32 if (platform_dup2(output_fd, STDOUT_FILENO) != ASCIICHAT_OK)
33 goto fail;
34 if (platform_dup2(output_fd, STDERR_FILENO) != ASCIICHAT_OK) {
35 // Restore stdout if only the first redirection succeeded.
36 platform_dup2(capture.saved_stdout_fd, STDOUT_FILENO);
37 goto fail;
38 }
39 return capture;
40
41fail:
42 if (capture.saved_stdout_fd >= 0)
43 platform_close(capture.saved_stdout_fd);
44 if (capture.saved_stderr_fd >= 0)
45 platform_close(capture.saved_stderr_fd);
46 fclose(capture.output);
47 return (log_io_t){.saved_stdout_fd = -1, .saved_stderr_fd = -1, .output = NULL};
48}
@ ASCIICHAT_OK
Definition error_codes.h:46
asciichat_error_t platform_dup2(int source_fd, int target_fd)
int platform_fileno(FILE *stream)
int platform_dup(int fd)
Saved descriptors and temporary file used during stdout/stderr capture.
Definition io.h:37
FILE * output
Temporary capture file, deleted on close.
Definition io.h:40
int saved_stdout_fd
Saved stdout file descriptor.
Definition io.h:38
int saved_stderr_fd
Saved stderr file descriptor.
Definition io.h:39

References ASCIICHAT_OK, log_io_t::output, platform_dup(), platform_dup2(), platform_fileno(), log_io_t::saved_stderr_fd, and log_io_t::saved_stdout_fd.

◆ log_io_stop()

void log_io_stop ( log_io_t capture,
const char * prefix )

Stop capturing and log the accumulated output.

Restores stdout and stderr to their original state, reads all captured output from the temporary file, and logs it at DEBUG level with the given prefix.

Parameters
captureCapture handle returned by log_io_start()
prefixPrefix to use in log messages (e.g., "ffmpeg", "portaudio")
Note
Safe to call with invalid handle (saved_stdout_fd = -1)
After calling this, the capture handle is invalidated

Definition at line 50 of file io.c.

50 {
51 if (capture.saved_stdout_fd < 0 || capture.saved_stderr_fd < 0) {
52 return; // Capture was not started successfully
53 }
54
55 // Flush before restoring so buffered backend output is captured.
56 fflush(stdout);
57 fflush(stderr);
58
59 // Restore stdout and stderr
60 platform_dup2(capture.saved_stdout_fd, STDOUT_FILENO);
61 platform_dup2(capture.saved_stderr_fd, STDERR_FILENO);
62 platform_close(capture.saved_stdout_fd);
63 platform_close(capture.saved_stderr_fd);
64
65 // Read and log the captured output
66 if (capture.output) {
67 char buffer[LOG_IO_BUFFER_SIZE];
68 size_t bytes_read;
69
70 // Replay only after restoring the descriptors, so logger output cannot feed
71 // back into this capture. fseek synchronizes the stream with descriptor writes.
72 if (fseek(capture.output, 0, SEEK_SET) != 0) {
73 fclose(capture.output);
74 return;
75 }
76 while ((bytes_read = fread(buffer, 1, sizeof(buffer) - 1, capture.output)) > 0) {
77 buffer[bytes_read] = '\0';
78
79 // Split by lines and log each line
80 char *line_start = buffer;
81 char *line_end;
82
83 while ((line_end = strchr(line_start, '\n')) != NULL) {
84 // Null-terminate the line (overwriting the newline)
85 *line_end = '\0';
86
87 // Log the line if it's not empty
88 if (line_start[0] != '\0') {
89 if (prefix) {
90 log_debug("[%s] %s", prefix, line_start);
91 } else {
92 log_debug("%s", line_start);
93 }
94 }
95
96 // Move to the next line
97 line_start = line_end + 1;
98 }
99
100 // Log any remaining partial line
101 if (line_start[0] != '\0') {
102 if (prefix) {
103 log_debug("[%s] %s", prefix, line_start);
104 } else {
105 log_debug("%s", line_start);
106 }
107 }
108 }
109
110 fclose(capture.output);
111 }
112}
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:595
#define LOG_IO_BUFFER_SIZE
Definition io.c:13

References log_debug, LOG_IO_BUFFER_SIZE, log_io_t::output, platform_dup2(), log_io_t::saved_stderr_fd, and log_io_t::saved_stdout_fd.