PatchworkOS
Loading...
Searching...
No Matches
pipeline.c
Go to the documentation of this file.
1#include "pipeline.h"
2#include "builtin.h"
3
4#include <errno.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <sys/argsplit.h>
9#include <sys/io.h>
10#include <sys/proc.h>
11
12static const char* lookupDirs[] = {
13 "/bin",
14 "/usr/bin",
15};
16
17uint64_t pipeline_init(pipeline_t* pipeline, const char* cmdline)
18{
19 uint64_t tokenAmount;
20 const char** tokens = argsplit(cmdline, UINT64_MAX, &tokenAmount);
21 if (tokens == NULL)
22 {
23 return ERR;
24 }
25
26 if (tokenAmount == 0)
27 {
28 pipeline->cmds = NULL;
29 pipeline->capacity = tokenAmount;
30 pipeline->amount = 0;
31 free(tokens);
32 return 0;
33 }
34
35 pipeline->cmds = malloc(sizeof(cmd_t) * tokenAmount);
36 if (pipeline->cmds == NULL)
37 {
38 free(tokens);
39 return ERR;
40 }
41
42 pipeline->capacity = tokenAmount;
43 pipeline->amount = 0;
44 pipeline->status = 0;
45 if (open2("/dev/pipe/new", pipeline->globalStdin) == ERR)
46 {
47 free(pipeline->cmds);
48 free(tokens);
49 return ERR;
50 }
51
52 for (uint64_t i = 0; i < tokenAmount; i++)
53 {
54 cmd_t* cmd = &pipeline->cmds[i];
55 cmd->argv = NULL;
56 cmd->argc = 0;
57 cmd->stdin = pipeline->globalStdin[PIPE_READ];
58 cmd->stdout = STDOUT_FILENO;
59 cmd->stderr = STDERR_FILENO;
60 cmd->shouldCloseStdin = false;
61 cmd->shouldCloseStdout = false;
62 cmd->shouldCloseStderr = false;
63 }
64
65 uint64_t currentCmd = 0;
66 uint64_t currentArg = 0;
67 const char** currentArgv = malloc(sizeof(char*) * (tokenAmount + 1));
68 if (currentArgv == NULL)
69 {
70 free(pipeline->cmds);
71 free(tokens);
72 return ERR;
73 }
74
75 for (uint64_t i = 0; i < tokenAmount; i++)
76 {
77 if (strcmp(tokens[i], "|") == 0)
78 {
79 if (currentArg == 0)
80 {
81 printf("error: empty command in pipeline\n");
82 if (pipeline->cmds[currentCmd].shouldCloseStdin)
83 {
84 close(pipeline->cmds[currentCmd].stdin);
85 pipeline->cmds[currentCmd].shouldCloseStdin = false;
86 }
87 goto token_parse_error;
88 }
89
90 currentArgv[currentArg] = NULL;
91
92 cmd_t* cmd = &pipeline->cmds[currentCmd];
93 cmd->argv = currentArgv;
94 cmd->argc = currentArg;
95
96 fd_t pipe[2];
97 if (open2("/dev/pipe/new", pipe) == ERR)
98 {
99 printf("error: unable to open pipe (%s)\n", strerror(errno));
100 goto token_parse_error;
101 }
102
103 cmd->stdout = pipe[PIPE_WRITE];
104 cmd->shouldCloseStdout = true;
105
106 currentCmd++;
107 currentArg = 0;
108 currentArgv = malloc(sizeof(char*) * (tokenAmount + 1));
109 if (currentArgv == NULL)
110 {
111 printf("error: out of memory\n");
112 goto token_parse_error;
113 }
114
115 cmd_t* nextCmd = &pipeline->cmds[currentCmd];
116 nextCmd->stdin = pipe[PIPE_READ];
117 nextCmd->shouldCloseStdin = true;
118 }
119 else if (strcmp(tokens[i], "<") == 0)
120 {
121 if (i + 1 >= tokenAmount)
122 {
123 printf("error: missing filename after <\n");
124 goto token_parse_error;
125 }
126
127 fd_t fd = open(tokens[i + 1]);
128 if (fd == ERR)
129 {
130 printf("error: unable to open %s (%s)\n", tokens[i + 1], strerror(errno));
131 goto token_parse_error;
132 }
133
134 cmd_t* cmd = &pipeline->cmds[currentCmd];
135 if (cmd->shouldCloseStdin)
136 {
137 close(cmd->stdin);
138 }
139 cmd->stdin = fd;
140 cmd->shouldCloseStdin = true;
141
142 i++; // Skip file
143 }
144 else if (strcmp(tokens[i], ">") == 0)
145 {
146 if (i + 1 >= tokenAmount)
147 {
148 printf("error: missing filename after >\n");
149 goto token_parse_error;
150 }
151
152 fd_t fd = open(tokens[i + 1]);
153 if (fd == ERR)
154 {
155 printf("error: unable to open %s (%s)\n", tokens[i + 1], strerror(errno));
156 goto token_parse_error;
157 }
158
159 cmd_t* cmd = &pipeline->cmds[currentCmd];
160 if (cmd->shouldCloseStdout)
161 {
162 close(cmd->stdout);
163 }
164 cmd->stdout = fd;
165 cmd->shouldCloseStdout = true;
166
167 i++; // Skip file
168 }
169 else if (strcmp(tokens[i], "2>") == 0)
170 {
171 if (i + 1 >= tokenAmount)
172 {
173 printf("error: missing filename after 2>\n");
174 goto token_parse_error;
175 }
176
177 fd_t fd = open(tokens[i + 1]);
178 if (fd == ERR)
179 {
180 printf("error: unable to open %s (%s)\n", tokens[i + 1], strerror(errno));
181 goto token_parse_error;
182 }
183
184 cmd_t* cmd = &pipeline->cmds[currentCmd];
185 if (cmd->shouldCloseStderr)
186 {
187 close(cmd->stderr);
188 }
189 cmd->stderr = fd;
190 cmd->shouldCloseStderr = true;
191
192 i++; // Skip file
193 }
194 else
195 {
196 currentArgv[currentArg] = strdup(tokens[i]);
197 if (currentArgv[currentArg] == NULL)
198 {
199 printf("error: out of memory\n");
200 goto token_parse_error;
201 }
202 currentArg++;
203 }
204 }
205
206 if (currentArg > 0)
207 {
208 currentArgv[currentArg] = NULL;
209 pipeline->cmds[currentCmd].argv = currentArgv;
210 pipeline->cmds[currentCmd].argc = currentArg;
211 currentCmd++;
212 }
213 else
214 {
215 free(currentArgv);
216 if (pipeline->amount > 0 && currentCmd > 0)
217 {
218 printf("error: pipeline ends with empty command\n");
219 cmd_t* emptyCmd = &pipeline->cmds[currentCmd];
220 if (emptyCmd->shouldCloseStdin)
221 {
222 close(emptyCmd->stdin);
223 emptyCmd->shouldCloseStdin = false;
224 }
225 goto token_parse_error_no_current_argv;
226 }
227 }
228
229 pipeline->amount = currentCmd;
230 free(tokens);
231 return 0;
232
233token_parse_error:
234 if (currentArgv != NULL)
235 {
236 for (uint64_t k = 0; k < currentArg; k++)
237 {
238 free((void*)currentArgv[k]);
239 }
240 }
241 free(currentArgv);
242
243token_parse_error_no_current_argv:
244 for (uint64_t j = 0; j < currentCmd; j++)
245 {
246 if (pipeline->cmds[j].shouldCloseStdin)
247 {
248 close(pipeline->cmds[j].stdin);
249 }
250 if (pipeline->cmds[j].shouldCloseStdout)
251 {
252 close(pipeline->cmds[j].stdout);
253 }
254 if (pipeline->cmds[j].shouldCloseStderr)
255 {
256 close(pipeline->cmds[j].stderr);
257 }
258
259 if (pipeline->cmds[j].argv != NULL)
260 {
261 for (uint64_t k = 0; pipeline->cmds[j].argv[k] != NULL; k++)
262 {
263 free((void*)pipeline->cmds[j].argv[k]);
264 }
265 free(pipeline->cmds[j].argv);
266 }
267 }
268 close(pipeline->globalStdin[PIPE_READ]);
269 close(pipeline->globalStdin[PIPE_WRITE]);
270 free(pipeline->cmds);
271 free(tokens);
272 return ERR;
273}
274
276{
277 for (uint64_t j = 0; j < pipeline->amount; j++)
278 {
279 if (pipeline->cmds[j].shouldCloseStdin)
280 {
281 close(pipeline->cmds[j].stdin);
282 }
283 if (pipeline->cmds[j].shouldCloseStdout)
284 {
285 close(pipeline->cmds[j].stdout);
286 }
287 if (pipeline->cmds[j].shouldCloseStderr)
288 {
289 close(pipeline->cmds[j].stderr);
290 }
291
292 if (pipeline->cmds[j].argv != NULL)
293 {
294 for (uint64_t k = 0; pipeline->cmds[j].argv[k] != NULL; k++)
295 {
296 free((void*)pipeline->cmds[j].argv[k]);
297 }
298 free(pipeline->cmds[j].argv);
299 }
300 }
301 close(pipeline->globalStdin[PIPE_READ]);
302 close(pipeline->globalStdin[PIPE_WRITE]);
303 if (pipeline->cmds != NULL)
304 {
305 free(pipeline->cmds);
306 }
307}
308
310{
311 // The price of not having fork()
312
313 fd_t originalStdin = dup(STDIN_FILENO);
314 if (originalStdin == ERR)
315 {
316 return ERR;
317 }
318 fd_t originalStdout = dup(STDOUT_FILENO);
319 if (originalStdout == ERR)
320 {
321 close(originalStdin);
322 return ERR;
323 }
324 fd_t originalStderr = dup(STDERR_FILENO);
325 if (originalStderr == ERR)
326 {
327 close(originalStdin);
328 close(originalStdout);
329 return ERR;
330 }
331
332 if (dup2(cmd->stdin, STDIN_FILENO) == ERR || dup2(cmd->stdout, STDOUT_FILENO) == ERR ||
333 dup2(cmd->stderr, STDERR_FILENO) == ERR)
334 {
335 close(originalStdin);
336 close(originalStdout);
337 close(originalStderr);
338 return ERR;
339 }
340
341 uint64_t result = builtin_execute(cmd->argc, cmd->argv);
342 if (dup2(originalStdin, STDIN_FILENO) == ERR || dup2(originalStdout, STDOUT_FILENO) == ERR ||
343 dup2(originalStderr, STDERR_FILENO) == ERR)
344 {
345 result = ERR;
346 }
347
348 close(originalStdin);
349 close(originalStdout);
350 close(originalStderr);
351 return result;
352}
353
355{
356 pid_t result = ERR;
357
358 spawn_fd_t fds[] = {
359 {.child = STDIN_FILENO, .parent = cmd->stdin},
360 {.child = STDOUT_FILENO, .parent = cmd->stdout},
361 {.child = STDERR_FILENO, .parent = cmd->stderr},
363 };
364
365 const char** argv = cmd->argv;
366 uint64_t argc = cmd->argc;
367 if (builtin_exists(argv[0]))
368 {
369 if (pipeline_execute_builtin(cmd) == ERR)
370 {
371 result = ERR;
372 }
373 else
374 {
375 result = 0;
376 }
377 }
378 else if (argv[0][0] == '.' && argv[0][1] == '/')
379 {
380 stat_t info;
381 if (stat(argv[0], &info) != ERR && info.type != INODE_DIR)
382 {
383 result = spawn(argv, fds, NULL, NULL);
384 }
385 else
386 {
387 printf("error: %s not found\n", argv[0]);
388 result = ERR;
389 }
390 }
391 else
392 {
393 bool isFound = false;
394 for (uint64_t j = 0; j < sizeof(lookupDirs) / sizeof(lookupDirs[0]); j++)
395 {
396 if (strlen(lookupDirs[j]) + strlen(argv[0]) + 1 >= MAX_PATH)
397 {
398 continue;
399 }
400
401 char path[MAX_PATH];
402 sprintf(path, "%s/%s", lookupDirs[j], argv[0]);
403
404 stat_t info;
405 if (stat(path, &info) != ERR && info.type != INODE_DIR)
406 {
407 const char* temp = argv[0];
408 argv[0] = path;
409 result = spawn(argv, fds, NULL, SPAWN_NONE);
410 argv[0] = temp;
411 isFound = true;
412 break;
413 }
414 }
415
416 if (!isFound)
417 {
418 fprintf(stderr, "shell: %s not found\n", argv[0]);
419 result = ERR;
420 }
421 }
422
423 if (cmd->shouldCloseStdin)
424 {
425 close(cmd->stdin);
426 cmd->shouldCloseStdin = false;
427 }
428 if (cmd->shouldCloseStdout)
429 {
430 close(cmd->stdout);
431 cmd->shouldCloseStdout = false;
432 }
433 if (cmd->shouldCloseStderr)
434 {
435 close(cmd->stderr);
436 cmd->shouldCloseStderr = false;
437 }
438
439 return result;
440}
441
443{
444 if (pipeline->amount == 0)
445 {
446 return 0;
447 }
448
449 pollfd_t* fds = calloc(pipeline->amount + 1, sizeof(pollfd_t));
450 if (fds == NULL)
451 {
452 return ERR;
453 }
454 fds[0].fd = STDIN_FILENO;
455 fds[0].events = POLLIN;
456 uint64_t fdCount = 1;
457
458 pid_t* pids = malloc(sizeof(pid_t) * pipeline->amount);
459 if (pids == NULL)
460 {
461 free(fds);
462 return ERR;
463 }
464
465 uint64_t result = 0;
466 pipeline->status = 0;
467
468 for (uint64_t i = 0; i < pipeline->amount; i++)
469 {
470 pid_t pid = pipeline_execute_cmd(&pipeline->cmds[i]);
471 if (pid == ERR)
472 {
473 pipeline->status = EXIT_FAILURE;
474 result = ERR;
475 goto cleanup;
476 }
477 if (pid == 0)
478 {
479 pipeline->status = 0;
480 continue;
481 }
482
483 pids[fdCount - 1] = pid;
484
485 fds[fdCount].fd = openf("/proc/%d/wait", pid);
486 if (fds[fdCount].fd == ERR)
487 {
488 printf("shell: unable to open /proc/%d/wait (%s)\n", pid, strerror(errno));
489 result = ERR;
490 goto cleanup;
491 }
492 fds[fdCount].events = POLLIN;
493 fdCount++;
494 }
495
496 if (fdCount == 1)
497 {
498 // All commands were builtins
499 goto cleanup;
500 }
501
502 while (true)
503 {
504 uint64_t ready = poll(fds, fdCount, CLOCKS_NEVER);
505 if (ready == ERR)
506 {
507 printf("shell: poll failed (%s)\n", strerror(errno));
508 result = ERR;
509 goto cleanup;
510 }
511
512 for (uint64_t i = 0; i < fdCount; i++)
513 {
514 if (fds[i].revents & POLLERR)
515 {
516 printf("shell: poll error on fd %d\n", fds[i].fd);
517 result = ERR;
518 goto cleanup;
519 }
520 }
521
522 if (fds[0].revents & POLLIN)
523 {
524 char buffer[MAX_PATH];
525 uint64_t bytesRead = read(STDIN_FILENO, buffer, sizeof(buffer));
526 if (bytesRead == ERR)
527 {
528 printf("shell: failed to read stdin (%s)\n", strerror(errno));
529 result = ERR;
530 goto cleanup;
531 }
532
533 for (uint64_t i = 0; i < bytesRead; i++)
534 {
535 if (buffer[i] != '\003') // Ctrl-C
536 {
537 continue;
538 }
539
540 printf("^C\n");
541 for (uint64_t j = 1; j < fdCount; j++)
542 {
543 fd_t note = openf("/proc/%d/note", pids[j - 1]);
544 if (note == ERR)
545 {
546 continue;
547 }
548
549 writef(note, "kill");
550 close(note);
551 }
552
553 break;
554 }
555
556 if (write(pipeline->globalStdin[PIPE_WRITE], buffer, bytesRead) == ERR)
557 {
558 printf("shell: failed to write to pipeline stdin (%s)\n", strerror(errno));
559 result = ERR;
560 goto cleanup;
561 }
562 }
563
564 bool allExited = true;
565 for (uint64_t i = 1; i < fdCount; i++)
566 {
567 if (fds[i].revents & POLLIN)
568 {
569 char buffer[64];
570 uint64_t readCount = read(fds[i].fd, buffer, sizeof(buffer) - 1);
571 if (readCount == ERR)
572 {
573 printf("shell: failed to read process %d exit status (%s)\n", pids[i - 1], strerror(errno));
574 result = ERR;
575 goto cleanup;
576 }
577 buffer[readCount] = '\0';
578
579 pipeline->status = atoi(buffer);
580
581 close(fds[i].fd);
582 memmove(&fds[i], &fds[i + 1], sizeof(pollfd_t) * (fdCount - i - 1));
583 memmove(&pids[i - 1], &pids[i], sizeof(pid_t) * (fdCount - i - 1));
584 fdCount--;
585 }
586 else
587 {
588 allExited = false;
589 }
590 }
591
592 if (allExited)
593 {
594 break;
595 }
596 }
597
598cleanup:
599 for (uint64_t i = 1; i < fdCount; i++)
600 {
601 close(fds[i].fd);
602 }
603 free(fds);
604 free(pids);
605 return result;
606}
#define MAX_PATH
Maximum length of filepaths.
Definition MAX_PATH.h:11
uint64_t builtin_execute(uint64_t argc, const char **argv)
Definition builtin.c:100
bool builtin_exists(const char *name)
Definition builtin.c:87
#define CLOCKS_NEVER
Definition clock_t.h:16
#define errno
Error number variable.
Definition errno.h:27
const char ** argsplit(const char *str, uint64_t maxLen, uint64_t *count)
Standardized argument parsing function.
Definition argsplit.c:3
fd_t dup2(fd_t oldFd, fd_t newFd)
System call for duplicating file descriptors, with a destination.
Definition dup2.c:9
uint64_t stat(const char *path, stat_t *stat)
System call for retrieving info about a file or directory.
Definition stat.c:9
uint64_t writef(fd_t fd, const char *_RESTRICT format,...)
Wrapper for writing a formatted string to a file.
Definition writef.c:9
fd_t open(const char *path)
System call for opening files.
Definition open.c:9
#define PIPE_READ
Pipe read end.
Definition io.h:55
uint64_t close(fd_t fd)
System call for closing files.
Definition close.c:9
fd_t dup(fd_t oldFd)
System call for duplicating file descriptors.
Definition dup.c:9
uint64_t poll(pollfd_t *fds, uint64_t amount, clock_t timeout)
System call for polling files.
Definition poll.c:9
fd_t openf(const char *_RESTRICT format,...)
Wrapper for opening files with a formatted path.
Definition openf.c:9
#define PIPE_WRITE
Pipe write end.
Definition io.h:63
#define STDOUT_FILENO
Definition io.h:45
uint64_t read(fd_t fd, void *buffer, uint64_t count)
System call for reading from files.
Definition read.c:9
uint64_t open2(const char *path, fd_t fd[2])
System call for opening 2 file descriptors from one file.
Definition open2.c:9
#define STDERR_FILENO
Definition io.h:46
uint64_t write(fd_t fd, const void *buffer, uint64_t count)
System call for writing to files.
Definition write.c:9
#define STDIN_FILENO
Definition io.h:44
@ INODE_DIR
Is a directory.
Definition io.h:346
@ POLLIN
File descriptor is ready to read.
Definition io.h:290
@ POLLERR
File descriptor caused an error.
Definition io.h:292
pid_t spawn(const char **argv, const spawn_fd_t *fds, const char *cwd, spawn_attr_t *attr)
System call for creating child processes.
Definition spawn.c:6
#define SPAWN_FD_END
Spawn fds termination constant.
Definition proc.h:82
#define SPAWN_NONE
Definition proc.h:75
#define NULL
Pointer error value.
Definition NULL.h:23
#define ERR
Integer error value.
Definition ERR.h:17
__UINT64_TYPE__ fd_t
A file descriptor.
Definition fd_t.h:12
__UINT64_TYPE__ pid_t
Process Identifier.
Definition pid_t.h:11
static fb_info_t info
Definition gop.c:41
EFI_PHYSICAL_ADDRESS buffer
Definition mem.c:15
static pid_t pipeline_execute_cmd(cmd_t *cmd)
Definition pipeline.c:354
uint64_t pipeline_execute(pipeline_t *pipeline)
Definition pipeline.c:442
void pipeline_deinit(pipeline_t *pipeline)
Definition pipeline.c:275
static uint64_t pipeline_execute_builtin(cmd_t *cmd)
Definition pipeline.c:309
static const char * lookupDirs[]
Definition pipeline.c:12
uint64_t pipeline_init(pipeline_t *pipeline, const char *cmdline)
Definition pipeline.c:17
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
#define UINT64_MAX
Definition stdint.h:74
_PUBLIC int printf(const char *_RESTRICT format,...)
Definition printf.c:5
FILE * stderr
Definition std_streams.c:18
_PUBLIC int fprintf(FILE *_RESTRICT stream, const char *_RESTRICT format,...)
Definition fprintf.c:5
_PUBLIC int sprintf(char *_RESTRICT s, const char *_RESTRICT format,...)
Definition sprintf.c:5
#define EXIT_FAILURE
Definition stdlib.h:47
#define atoi(nptr)
Definition stdlib.h:34
_PUBLIC void * calloc(size_t nmemb, size_t size)
Definition calloc.c:6
_PUBLIC void * malloc(size_t size)
Definition malloc.c:5
_PUBLIC void free(void *ptr)
Definition free.c:11
_PUBLIC char * strerror(int errnum)
Definition strerror.c:6
_PUBLIC void * memmove(void *_RESTRICT s1, const void *_RESTRICT s2, size_t n)
char * strdup(const char *src)
Definition strdup.c:5
_PUBLIC size_t strlen(const char *s)
Definition strlen.c:3
_PUBLIC int strcmp(const char *s1, const char *s2)
Definition strcmp.c:3
uint64_t argc
Definition pipeline.h:10
fd_t stderr
Definition pipeline.h:13
bool shouldCloseStdin
Definition pipeline.h:14
bool shouldCloseStdout
Definition pipeline.h:15
fd_t stdin
Definition pipeline.h:11
fd_t stdout
Definition pipeline.h:12
const char ** argv
Definition pipeline.h:9
bool shouldCloseStderr
Definition pipeline.h:16
uint64_t amount
Definition pipeline.h:23
int status
Definition pipeline.h:24
fd_t globalStdin[2]
Definition pipeline.h:25
cmd_t * cmds
Definition pipeline.h:21
uint64_t capacity
Definition pipeline.h:22
Poll file descriptor structure.
Definition io.h:307
poll_events_t events
The events to wait for.
Definition io.h:309
fd_t fd
The file descriptor to poll.
Definition io.h:308
Stucture used to duplicate fds in spawn().
Definition proc.h:55
fd_t child
The destination file descriptor in the child.
Definition proc.h:56
Stat type.
Definition io.h:360