Line data Source code
1 : #include "executor.h"
2 :
3 2 : void sigchld_handler(int signo)
4 : {
5 : int status;
6 : pid_t pid;
7 :
8 4 : while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
9 : {
10 2 : if (WIFEXITED(status))
11 1 : print_generic(STDOUT_FILENO, "[%d] Process exited with status %d.\n", pid, WEXITSTATUS(status));
12 1 : else if (WIFSIGNALED(status))
13 1 : print_generic(STDOUT_FILENO, "[%d] Process killed by signal %d.\n", pid, WTERMSIG(status));
14 : }
15 2 : }
16 :
17 70 : int execute_single_command(char **args)
18 : {
19 70 : if (args == NULL || args[0] == NULL)
20 8 : return EXIT_FAILURE;
21 :
22 62 : pid_t pid = fork();
23 124 : if (pid < 0)
24 : {
25 0 : print_error("[ERROR] fork() failed");
26 0 : return EXIT_FAILURE;
27 : }
28 :
29 124 : if (pid == 0)
30 : {
31 62 : int child_status = custom_exec(args[0], args);
32 3 : if (child_status == -1)
33 : {
34 3 : print_error("[ERROR] %s", args[0]);
35 3 : exit(EXIT_FAILURE);
36 : }
37 : }
38 : else
39 : {
40 62 : if (!is_custom_command_main_process(args[0]))
41 : {
42 : int status;
43 38 : waitpid(pid, &status, 0);
44 38 : return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE;
45 : }
46 : else
47 : {
48 : // Wait for fork to close; ignoring result
49 : int status;
50 24 : waitpid(pid, &status, 0);
51 :
52 24 : return execute_custom_command_main_process(args[0], args) ? EXIT_SUCCESS : EXIT_FAILURE;
53 : }
54 : }
55 0 : return EXIT_SUCCESS;
56 : }
57 :
58 7 : int execute_pipe_command(command_node_t *node)
59 : {
60 7 : if (!node || node->op_type != OP_PIPE)
61 0 : return EXIT_FAILURE;
62 :
63 : // Count commands
64 7 : int cmd_count = 1;
65 7 : command_node_t *current = node;
66 11 : while (current->left && current->left->op_type == OP_PIPE)
67 : {
68 4 : cmd_count++;
69 4 : current = current->left;
70 : }
71 7 : cmd_count++; // Add the last command
72 :
73 : // Collect all commands in order
74 7 : command_node_t **commands = malloc(cmd_count * sizeof(command_node_t *));
75 7 : if (!commands)
76 : {
77 0 : errno = ENOMEM;
78 0 : print_error("[ERROR] Failed to allocate memory for commands array");
79 0 : return EXIT_FAILURE;
80 : }
81 :
82 : // Fetch commands FROM RIGHT TO LEFT
83 7 : int index = cmd_count - 1;
84 7 : current = node;
85 7 : commands[index--] = current->right; // Rightmost command
86 11 : while (current->left && current->left->op_type == OP_PIPE)
87 : {
88 4 : commands[index--] = current->left->right;
89 4 : current = current->left;
90 : }
91 7 : commands[0] = current->left; // Leftmost command
92 :
93 : int pipefd[2];
94 7 : int prev_pipe_read = STDIN_FILENO;
95 7 : pid_t *pids = malloc(cmd_count * sizeof(pid_t));
96 :
97 7 : if (!pids)
98 : {
99 0 : free_if_needed(commands);
100 0 : errno = ENOMEM;
101 0 : print_error("[ERROR] Failed to allocate memory for PIDs array");
102 0 : return EXIT_FAILURE;
103 : }
104 :
105 25 : for (int i = 0; i < cmd_count; i++)
106 : {
107 18 : if (i < cmd_count - 1)
108 : {
109 11 : if (pipe(pipefd) == -1)
110 : {
111 0 : print_error("[ERROR] Failed to create pipe");
112 0 : free_if_needed(commands);
113 0 : free_if_needed(pids);
114 0 : return EXIT_FAILURE;
115 : }
116 : }
117 :
118 18 : pids[i] = fork();
119 36 : if (pids[i] < 0)
120 : {
121 0 : print_error("[ERROR] Fork failed");
122 0 : free_if_needed(commands);
123 0 : free_if_needed(pids);
124 0 : return EXIT_FAILURE;
125 : }
126 :
127 36 : if (pids[i] == 0)
128 : {
129 18 : if (i > 0)
130 : {
131 11 : if (dup2(prev_pipe_read, STDIN_FILENO) == -1)
132 : {
133 0 : print_error("[ERROR] Failed to duplicate input fd");
134 0 : exit(EXIT_FAILURE);
135 : }
136 : }
137 :
138 18 : if (i < cmd_count - 1)
139 : {
140 11 : if (dup2(pipefd[1], STDOUT_FILENO) == -1)
141 : {
142 0 : print_error("[ERROR] Failed to duplicate output fd");
143 0 : exit(EXIT_FAILURE);
144 : }
145 11 : safe_close(pipefd[0]);
146 11 : safe_close(pipefd[1]);
147 : }
148 :
149 18 : if (custom_exec(commands[i]->args[0], commands[i]->args) == -1)
150 : {
151 0 : errno = ENOENT;
152 0 : print_error("[ERROR] %s", commands[i]->args[0]);
153 0 : exit(EXIT_FAILURE);
154 : }
155 0 : exit(EXIT_SUCCESS);
156 : }
157 :
158 18 : if (i > 0)
159 11 : safe_close(prev_pipe_read);
160 :
161 18 : if (i < cmd_count - 1)
162 : {
163 11 : safe_close(pipefd[1]);
164 11 : prev_pipe_read = pipefd[0];
165 : }
166 : }
167 :
168 : int status;
169 7 : int last_status = EXIT_SUCCESS;
170 25 : for (int i = 0; i < cmd_count; i++)
171 : {
172 18 : waitpid(pids[i], &status, 0);
173 18 : if (i == cmd_count - 1)
174 7 : last_status = WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE;
175 : }
176 :
177 7 : free_if_needed(commands);
178 7 : free_if_needed(pids);
179 :
180 7 : return last_status;
181 : }
182 :
183 2 : int execute_background_command(command_node_t *node)
184 : {
185 2 : pid_t pid = fork();
186 3 : if (pid < 0)
187 : {
188 0 : print_error("[ERROR] fork() failed");
189 0 : return EXIT_FAILURE;
190 : }
191 :
192 3 : if (pid == 0)
193 : {
194 1 : int child_status = custom_exec(node->args[0], node->args);
195 0 : if (child_status == -1)
196 : {
197 0 : print_error("[ERROR] %s", node->args[0]);
198 0 : exit(EXIT_FAILURE);
199 : }
200 : }
201 :
202 2 : print_generic(STDOUT_FILENO, "[%d] %s running in background.\n", pid, node->args[0]);
203 2 : signal(SIGCHLD, sigchld_handler);
204 2 : return EXIT_SUCCESS;
205 : }
206 :
207 8 : int execute_redirection_command(command_node_t *left, command_node_t *right, operator_t redirect_type)
208 : {
209 8 : if (left == NULL || right == NULL)
210 1 : return EXIT_FAILURE;
211 :
212 7 : int input_fd = -1;
213 7 : int output_fd = -1;
214 :
215 7 : switch (redirect_type)
216 : {
217 2 : case OP_REDIR_OUT:
218 2 : output_fd = safe_open(right->args[0], O_WRONLY | O_CREAT | O_TRUNC, 0644);
219 2 : break;
220 1 : case OP_APPEND:
221 1 : output_fd = safe_open(right->args[0], O_WRONLY | O_CREAT | O_APPEND, 0644);
222 1 : break;
223 3 : case OP_REDIR_IN:
224 : case OP_HEREDOC:
225 3 : input_fd = safe_open(right->args[0], O_RDONLY, 0);
226 3 : break;
227 1 : default:
228 1 : return EXIT_FAILURE;
229 : }
230 :
231 6 : if ((input_fd < 0 && (redirect_type == OP_REDIR_IN || redirect_type == OP_HEREDOC)) ||
232 3 : (output_fd < 0 && (redirect_type == OP_REDIR_OUT || redirect_type == OP_APPEND)))
233 : {
234 0 : print_error("[ERROR] Failed to open redirection file");
235 0 : return EXIT_FAILURE;
236 : }
237 :
238 6 : pid_t pid = fork();
239 12 : if (pid < 0)
240 : {
241 0 : print_error("[ERROR] fork() failed");
242 0 : if (input_fd >= 0)
243 0 : safe_close(input_fd);
244 0 : if (output_fd >= 0)
245 0 : safe_close(output_fd);
246 0 : return EXIT_FAILURE;
247 : }
248 :
249 12 : if (pid == 0)
250 : {
251 6 : if (input_fd >= 0)
252 : {
253 3 : if (dup2(input_fd, STDIN_FILENO) < 0)
254 : {
255 0 : print_error("[ERROR] Input redirection failed");
256 0 : exit(EXIT_FAILURE);
257 : }
258 3 : safe_close(input_fd);
259 : }
260 :
261 6 : if (output_fd >= 0)
262 : {
263 3 : if (dup2(output_fd, STDOUT_FILENO) < 0)
264 : {
265 0 : print_error("[ERROR] Output redirection failed");
266 0 : exit(EXIT_FAILURE);
267 : }
268 3 : safe_close(output_fd);
269 : }
270 :
271 6 : exit(execute_command_tree(left));
272 : }
273 :
274 6 : if (input_fd >= 0)
275 3 : safe_close(input_fd);
276 6 : if (output_fd >= 0)
277 3 : safe_close(output_fd);
278 :
279 : int status;
280 6 : waitpid(pid, &status, 0);
281 :
282 6 : return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE;
283 : }
284 :
285 83 : int custom_exec(char *command, char **args)
286 : {
287 83 : if (command == NULL)
288 1 : return -1;
289 :
290 : // Custom commands
291 82 : if (is_custom_command(command))
292 : {
293 47 : execute_custom_command(command, args);
294 : }
295 :
296 : // Only reach here for non-custom commands
297 3 : return execvp(command, args);
298 : }
299 :
300 121 : int execute_command_tree(command_node_t *node)
301 : {
302 121 : if (node == NULL)
303 1 : return EXIT_SUCCESS;
304 :
305 : int status;
306 :
307 120 : switch (node->op_type)
308 : {
309 7 : case OP_PIPE:
310 7 : status = execute_pipe_command(node);
311 7 : break;
312 23 : case OP_SEQ:
313 23 : if (node->left != NULL)
314 23 : status = execute_command_tree(node->left);
315 23 : if (node->right != NULL)
316 23 : status = execute_command_tree(node->right);
317 23 : break;
318 7 : case OP_AND:
319 7 : status = execute_command_tree(node->left);
320 7 : if (status == EXIT_SUCCESS)
321 7 : status = execute_command_tree(node->right);
322 7 : break;
323 4 : case OP_OR:
324 4 : status = execute_command_tree(node->left);
325 4 : if (status != EXIT_SUCCESS)
326 1 : status = execute_command_tree(node->right);
327 4 : break;
328 2 : case OP_BG:
329 2 : status = execute_background_command(node->left);
330 2 : break;
331 6 : case OP_REDIR_OUT:
332 : case OP_REDIR_IN:
333 : case OP_APPEND:
334 : case OP_HEREDOC:
335 6 : status = execute_redirection_command(node->left, node->right, node->op_type);
336 6 : break;
337 70 : case OP_NONE:
338 70 : status = execute_single_command(node->args);
339 70 : break;
340 1 : default:
341 1 : status = EXIT_FAILURE;
342 1 : break;
343 : }
344 :
345 120 : return status;
346 : }
347 :
348 4 : int execute_command(char *input, command_tree_t *command_tree)
349 : {
350 4 : add_history_entry(input);
351 4 : command_tree = parse_command(input);
352 4 : if (command_tree == NULL)
353 : {
354 1 : print_error("[ERROR] Failed to parse command");
355 1 : return EXIT_FAILURE;
356 : }
357 : // TODO: add the command tree to the history
358 : // Check if command_tree is a built-in command
359 3 : print_command_tree(command_tree->root, 0);
360 3 : int status = execute_command_tree(command_tree->root);
361 3 : free_command_node(command_tree->root);
362 3 : free_if_needed(command_tree);
363 3 : command_tree = NULL;
364 3 : return status;
365 : }
|