-
Notifications
You must be signed in to change notification settings - Fork 1
/
runner.c
253 lines (205 loc) · 6.5 KB
/
runner.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "parser.h"
#include "jobs.h"
#include "redir.h"
// Returns command as an args array for execv
char** _get_command_as_args_array(struct Command* command) {
char** commandAsString = (char**) malloc(sizeof(char*) * (command->numArgs + 1));
for (int i = 0; i < command->numArgs; i++) {
commandAsString[i] = command->args[i]->tokenValue;
}
// Null terminate the args array
commandAsString[command->numArgs] = NULL;
return commandAsString;
}
// Runs command with pipe returning the cpid and sets piperead
int _run_with_pipe(struct Command* command, int infd, int* pipeRead, int pgid) {
// Get command as args array
char** commandAsString = _get_command_as_args_array(command);
// Create pipe
int pfd[2];
pipe(pfd);
// Fork and exec
int cpid = fork();
if (cpid == 0) {
// Put child into process group specified
setpgid(0, pgid);
// Check if redirectors exist and deal with them
// < redirector
if (command->infd != STDIN_FILENO) {
dup2(command->infd, STDIN_FILENO);
} else {
dup2(infd, STDIN_FILENO);
}
// > redirector
if (command->outfd != STDOUT_FILENO) {
dup2(command->outfd, STDOUT_FILENO);
} else {
dup2(pfd[1], STDOUT_FILENO);
}
// 2> redirector
if (command->errfd != STDERR_FILENO) {
dup2(command->errfd, STDERR_FILENO);
}
// Close read end of pipe, this child only writes
close(pfd[0]);
execvp(commandAsString[0], commandAsString);
exit(1);
}
// Put child into process group specified
setpgid(cpid, pgid);
// Give terminal control to child process
if (!command->backgrounded) {
tcsetpgrp(STDIN_FILENO, cpid);
}
// Free command as args list
free(commandAsString);
// Close write pipe
close(pfd[1]);
// Close file descriptors
closeDescriptors(command);
// Set the pipe read for use for the next exec
*pipeRead = pfd[0];
// Return child pid
return cpid;
}
// Run input with pipelines
void run(struct ParsedInput* input) {
// Stores last read descriptor for pipe
int lastPipeRead;
// Run first command in pipeline
int cpidFirst = _run_with_pipe(input->commands[0], STDIN_FILENO, &lastPipeRead, 0);
setpgid(cpidFirst, cpidFirst);
gpidRunning = cpidFirst;
// Run commands in middle of pipeline
for (int i = 1; i < input->numCommands - 1; i++) {
_run_with_pipe(input->commands[i], lastPipeRead, &lastPipeRead, cpidFirst);
}
// Run last command in pipeline
struct Command* lastCommand = input->commands[input->numCommands - 1];
char** lastCommandAsString = _get_command_as_args_array(lastCommand);
int cpidLast = fork();
if (cpidLast == 0) {
// Put child into process group of first command
setpgid(0, cpidFirst);
// Pipe output of last command into stdin
dup2(lastPipeRead, STDIN_FILENO);
// Check for output redirector
if (lastCommand->outfd != STDOUT_FILENO) {
dup2(lastCommand->outfd, STDOUT_FILENO);
}
// Check for error redirector
if (lastCommand->errfd != STDERR_FILENO) {
dup2(lastCommand->errfd, STDERR_FILENO);
}
// Run command
execvp(lastCommandAsString[0], lastCommandAsString);
exit(1);
}
// Put child into process group of first command
setpgid(cpidLast, cpidFirst);
// Close the last pipe and all file descriptors
close(lastPipeRead);
closeDescriptors(lastCommand);
// Give terminal access to process group if it is not backgrounded
if (!lastCommand->backgrounded) {
tcsetpgrp(STDIN_FILENO, cpidFirst);
}
// Copy original input for printing back in job
char* originalInput = input->originalInput;
char* inputCopy = (char*) malloc(sizeof(char) * (strlen(originalInput) + 1));
strcpy(inputCopy, originalInput);
// Wait if command not backgrounded
if (!lastCommand->backgrounded) {
// Wait for entire pg to finish
int status;
while(waitpid(-cpidFirst, &status, WUNTRACED) > 0);
// Get terminal back to shell
tcsetpgrp(STDIN_FILENO, getpgrp());
// Check if pg was stopped
if (WIFSTOPPED(status)) {
// Add job if process was stopped
int jobId = add_job(cpidFirst, inputCopy);
set_job_status(jobId, JOB_STOPPED);
printf("\n");
} else {
free(inputCopy);
}
} else {
// If running in background, add job immediately
add_job(cpidFirst, inputCopy);
}
// Free the args array
free(lastCommandAsString);
}
// Run input with no pipeline
void run_single(struct ParsedInput* input) {
// Get first command
struct Command* command = input->commands[0];
// Get command as args array for execv
char** commandAsString = _get_command_as_args_array(command);
// Fork and exec
int cpid = fork();
if (cpid == 0) {
// Put child in its own process group
setpgid(0, 0);
// Give terminal access to child if it is not backgrounded
if (!command->backgrounded) {
tcsetpgrp(STDIN_FILENO, getpgrp());
}
// Check for redirectors
// < redirector
if (command->infd != STDIN_FILENO) {
dup2(command->infd, STDIN_FILENO);
}
// > redirector
if (command->outfd != STDOUT_FILENO) {
dup2(command->outfd, STDOUT_FILENO);
}
// 2> redirector
if (command->errfd != STDERR_FILENO) {
dup2(command->errfd, STDERR_FILENO);
}
execvp(commandAsString[0], commandAsString);
exit(1);
}
// Put child in its own process group
setpgid(cpid, 0);
// Give terminal access to child if it is not backgrounded
if (!command->backgrounded) {
tcsetpgrp(STDIN_FILENO, cpid);
}
// Close file descriptors
closeDescriptors(command);
// Copy input for job table
char* originalInput = input->originalInput;
char* inputCopy = (char*) malloc(sizeof(char) * (strlen(originalInput) + 1));
strcpy(inputCopy, originalInput);
// Set the running process so we know how to redirect signals
gpidRunning = cpid;
// If command is in foreground, wait for it to finish
if (!command->backgrounded) {
int status;
waitpid(-cpid, &status, WUNTRACED);
// Get terminal back to shell
tcsetpgrp(STDIN_FILENO, getpgrp());
// If child was stopped, add it as a job
if (WIFSTOPPED(status)) {
int jobId = add_job(cpid, inputCopy);
set_job_status(jobId, JOB_STOPPED);
printf("\n");
} else {
free(inputCopy);
}
} else {
// If command was run in background, add it as job immediately
add_job(cpid, inputCopy);
}
// Free args array
free(commandAsString);
}