Line data Source code
1 : /**
2 : * @file function_capture.c
3 : * @brief Functionality for redirecting stdout and stderr streams to buffers
4 : */
5 :
6 : #include "testitall.h"
7 :
8 : /**
9 : * @brief Executes a function and captures both stdout and stderr output
10 : *
11 : * @param func Function pointer to the function to be executed
12 : * @param stdout_buffer memory structure to store captured stdout output
13 : * @param stderr_buffer memory structure to store captured stderr output
14 : * @return Return Status of execution (SUCCESS/FAILURE)
15 : *
16 : * @details This function:
17 : * 1. Creates temporary files to capture stdout and stderr
18 : * 2. Redirects stdout and stderr to these files
19 : * 3. Executes the provided function
20 : * 4. Captures both outputs into the provided memory structures
21 : * 5. Restores original stdout and stderr
22 : * 6. Cleans up resources
23 : */
24 105 : Return function_capture(
25 : void (*func)(void),
26 : memory *stdout_buffer,
27 : memory *stderr_buffer)
28 : {
29 105 : Return status = SUCCESS;
30 :
31 : /* Save original file descriptors */
32 105 : int stdout_fd = dup(STDOUT_FILENO);
33 105 : int stderr_fd = dup(STDERR_FILENO);
34 :
35 105 : if(stdout_fd == -1 || stderr_fd == -1)
36 : {
37 0 : slog(ERROR,"Failed to save original file descriptors\n");
38 0 : return FAILURE;
39 : }
40 :
41 : /* Create temporary files for redirection */
42 105 : FILE *stdout_tmp = tmpfile();
43 105 : FILE *stderr_tmp = tmpfile();
44 :
45 105 : if(stdout_tmp == NULL || stderr_tmp == NULL)
46 : {
47 0 : slog(ERROR,"Failed to create temporary files for redirection\n");
48 0 : status = FAILURE;
49 : }
50 :
51 : /* Disable buffering for temporary files */
52 105 : if(SUCCESS == status)
53 : {
54 210 : if(setvbuf(stdout_tmp,NULL,_IONBF,0) != 0 ||
55 105 : setvbuf(stderr_tmp,NULL,_IONBF,0) != 0)
56 : {
57 0 : slog(ERROR,"Failed to disable buffering\n");
58 0 : status = FAILURE;
59 : }
60 : }
61 :
62 : /* Disable buffering for stdout and stderr */
63 105 : if(SUCCESS == status)
64 : {
65 210 : if(setvbuf(stdout,NULL,_IONBF,0) != 0 ||
66 105 : setvbuf(stderr,NULL,_IONBF,0) != 0)
67 : {
68 0 : slog(ERROR,"Failed to disable buffering\n");
69 0 : status = FAILURE;
70 : }
71 : }
72 :
73 : /* Redirect streams */
74 105 : if(SUCCESS == status)
75 : {
76 210 : if(dup2(fileno(stdout_tmp),STDOUT_FILENO) == -1 ||
77 105 : dup2(fileno(stderr_tmp),STDERR_FILENO) == -1)
78 : {
79 0 : slog(ERROR,"Failed to redirect streams\n");
80 0 : status = FAILURE;
81 : }
82 : }
83 :
84 : /* Execute target function */
85 105 : if(SUCCESS == status)
86 : {
87 105 : func();
88 105 : fflush(stdout);
89 105 : fflush(stderr);
90 : }
91 :
92 : /* Restore original streams */
93 105 : if(SUCCESS == status)
94 : {
95 210 : if(dup2(stdout_fd,STDOUT_FILENO) == -1 ||
96 105 : dup2(stderr_fd,STDERR_FILENO) == -1)
97 : {
98 0 : slog(ERROR,"Failed to restore original streams\n");
99 0 : status = FAILURE;
100 : }
101 : }
102 :
103 : /* Read data from temporary files */
104 105 : if(SUCCESS == status)
105 : {
106 : size_t stdout_size,stderr_size;
107 :
108 : /* Get buffer sizes */
109 105 : fseek(stdout_tmp,0,SEEK_END);
110 105 : fseek(stderr_tmp,0,SEEK_END);
111 105 : stdout_size = (size_t)ftell(stdout_tmp);
112 105 : stderr_size = (size_t)ftell(stderr_tmp);
113 :
114 : /* Allocate memory for buffers */
115 105 : char *stdout_mem = NULL;
116 105 : char *stderr_mem = NULL;
117 :
118 105 : if(SUCCESS == status)
119 : {
120 105 : if(stdout_size > 0)
121 : {
122 94 : status = resize(stdout_buffer,stdout_size + 1);
123 :
124 94 : if(SUCCESS == status)
125 : {
126 94 : stdout_mem = data(char,stdout_buffer);
127 :
128 94 : if(stdout_mem == NULL)
129 : {
130 0 : status = FAILURE;
131 : }
132 : }
133 : }
134 : }
135 :
136 105 : if(SUCCESS == status)
137 : {
138 105 : if(stderr_size > 0)
139 : {
140 2 : status = resize(stderr_buffer,stderr_size + 1);
141 :
142 2 : if(SUCCESS == status)
143 : {
144 2 : stderr_mem = data(char,stderr_buffer);
145 :
146 2 : if(stderr_mem == NULL)
147 : {
148 0 : status = FAILURE;
149 : }
150 : }
151 : }
152 : }
153 :
154 : /* Read data */
155 105 : if(SUCCESS == status)
156 : {
157 105 : fseek(stdout_tmp,0,SEEK_SET);
158 105 : fseek(stderr_tmp,0,SEEK_SET);
159 :
160 105 : size_t read_stdout = 0;
161 :
162 105 : if(stdout_size > 0)
163 : {
164 94 : read_stdout = fread(stdout_mem,1,stdout_size,stdout_tmp);
165 : }
166 :
167 105 : size_t read_stderr = 0;
168 :
169 105 : if(stderr_size > 0)
170 : {
171 2 : read_stderr = fread(stderr_mem,1,stderr_size,stderr_tmp);
172 : }
173 :
174 105 : if(read_stdout != stdout_size || read_stderr != stderr_size)
175 : {
176 0 : status = FAILURE;
177 : }
178 :
179 105 : if(SUCCESS == status)
180 : {
181 105 : if(read_stdout > 0 && stdout_mem != NULL)
182 : {
183 94 : stdout_mem[stdout_size] = '\0';
184 : }
185 :
186 105 : if(read_stderr > 0 && stderr_mem != NULL)
187 : {
188 2 : stderr_mem[stderr_size] = '\0';
189 : }
190 : }
191 : }
192 : }
193 :
194 105 : if(stdout_tmp != NULL)
195 : {
196 105 : fclose(stdout_tmp);
197 : }
198 :
199 105 : if(stderr_tmp != NULL)
200 : {
201 105 : fclose(stderr_tmp);
202 : }
203 :
204 105 : close(stdout_fd);
205 105 : close(stderr_fd);
206 :
207 105 : return(status);
208 : }
|