-
Notifications
You must be signed in to change notification settings - Fork 0
/
command_handler.c
346 lines (307 loc) · 12.8 KB
/
command_handler.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
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
#include "command_handler.h"
#include "job.h"
#include "global.h"
#include "signals.h"
/* global variable keeps the track of where next token to be extracted */
static int parser_index;
static IOContext io_context;
/* shell delimiters are pipes, redirects and ampersand */
const char shell_delimiters[] = {'|', '>', '<', '&'};
/* reads the command from standard input */
int read_command(char *command_token, int max_command_length) {
int i = 0, ch;
while((ch = getchar()) != '\n' && ch != EOF && i < max_command_length) {
command_token[i++] = ch;
}
command_token[i] = '\0';
/* comments are by default ignored */
if(i > 0 && command_token[0] == '#') {
command_token[0] = '\0';
return 0;
}
return i == 0 && ch == EOF ? -1 : i;
}
/* checks if the given character is shell delimiter */
bool check_shell_delimiter(char ch) {
int n = sizeof(shell_delimiters) / sizeof(shell_delimiters[0]);
for(int i = 0; i < n; i++) {
if(shell_delimiters[i] == ch) {
return true;
}
}
return false;
}
/* returns the state of given shell delimiter */
int get_shell_delimiter_state(char shell_delimiter) {
if(shell_delimiter == '|') {
return PIPE;
}
if(shell_delimiter == '<') {
return INPUT_REDIRECT;
}
if(shell_delimiter == '>') {
return OUTPUT_REDIRECT;
}
if(shell_delimiter == '&') {
return NON_BLOCKING_COMMAND;
}
return INVALID;
}
/* tokenizer : returns the next token */
int parser(char *token, char *command) {
int n = strlen(command);
/* skip the spaces */
while(command[parser_index] == ' ' && parser_index < n) {
parser_index++;
}
/* parse given commannd */
/* CASE 1 : end of the command */
if(parser_index == n) {
return END;
}
/* CASE 2 : check for shell command delimiters */
if(check_shell_delimiter(command[parser_index])) {
int state = get_shell_delimiter_state(command[parser_index]);
parser_index++;
return state;
}
int token_index = 0;
/* CASE 3 : extract the shell command */
while(parser_index < n && !check_shell_delimiter(command[parser_index])) {
token[token_index++] = command[parser_index++];
}
token[token_index] = '\0';
return COMMAND;
}
/* FSM for handling the command */
void command_handler(char *command_token) {
char token[128]; /* stores string token */
char file_name[128]; /* stores the file name */
int pfd[2], fd; /* stores file descripters */
IronShellCommand shell_command; /* stores shell command */
parser_index = 0;
int current_state = START, next_state; /* stores state of FSM */
int num_process = 0;
/* initialize the current foreground job */
init_job(&(iscb.fg_job), command_token);
/* set the appropriate signal handlers */
set_signals();
/* save the context of the file descripters */
save_io_context();
/* parse until the end of shell command */
while(current_state != END) {
/* get next token */
next_state = parser(token, command_token);
/* switch state accordingly and take actions */
switch(next_state) {
case COMMAND:
switch(current_state) {
/* seeing the first command after starting */
case START:
/* extract the command and store the command */
strcpy(shell_command.command, token);
command_argument_parser(&shell_command);
break;
/* seeing command after pipe before */
case PIPE:
/* extract the command and store the command */
strcpy(shell_command.command, token);
command_argument_parser(&shell_command);
/* close the write end of the pipe */
close(pfd[1]);
/* duplicate read fd with pipe read end */
dup2(pfd[0], STDIN_FILENO);
break;
/* seeing command after input redirect */
case INPUT_REDIRECT:
/* interrpret current token as file not command */
strcpy(file_name, strtok(token, " "));
fd = open(file_name, O_RDONLY);
if(fd == -1) {
PRINT_ERR("cannot open file !");
}
dup2(fd, STDIN_FILENO);
break;
/* seeing command after output redirect */
case OUTPUT_REDIRECT:
/* interrpret current token as file not command */
strcpy(file_name, strtok(token, " "));
fd = open(file_name, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if(fd == -1) {
PRINT_ERR("cannot open file !");
}
dup2(fd, STDOUT_FILENO);
break;
/* seeing command after & symbol */
case NON_BLOCKING_COMMAND:
/* execute the previous command and extract then new command */
execute_shell_command(shell_command);
num_process++;
/* extract the command and store the command */
strcpy(shell_command.command, token);
command_argument_parser(&shell_command);
break;
}
break;
case PIPE:
switch(current_state) {
/* seeing a pipe after previous command */
case COMMAND:
/* create new pipe and close the read end of pipe
* and execute the shell command
*/
execute_shell_command_with_pipe(shell_command, pfd);
num_process++;
break;
/* malicious user : need learn how write shell commands */
case INPUT_REDIRECT:
PRINT_ERR("syntax error near unexpected token `newline'");
break;
/* malicious user : need learn how write shell commands */
case OUTPUT_REDIRECT:
PRINT_ERR("syntax error near unexpected token `newline'");
break;
/* malicious user : need learn how write shell commands */
case START:
PRINT_ERR("syntax error near unexpected token `|'");
break;
}
break;
case INPUT_REDIRECT:
switch(current_state) {
case PIPE:
PRINT_ERR("syntax error near unexpected token `|'");
break;
case START:
PRINT_ERR("syntax error near unexpected token `|'");
break;
case OUTPUT_REDIRECT:
PRINT_ERR("syntax error near unexpected token `|'");
}
break;
case OUTPUT_REDIRECT:
switch(current_state) {
case PIPE:
PRINT_ERR("syntax error near unexpected token `newline'");
break;
case START:
PRINT_ERR("syntax error near unexpected token `newline'");
break;
case INPUT_REDIRECT:
PRINT_ERR("syntax error near unexpected token `newline'");
break;
}
break;
case NON_BLOCKING_COMMAND:
switch(current_state) {
case START:
PRINT_ERR("syntax error near unexpected token `|'");
break;
case PIPE:
PRINT_ERR("syntax error near unexpected token `newline'");
break;
case INPUT_REDIRECT:
PRINT_ERR("syntax error near unexpected token `newline'");
break;
}
break;
case END:
switch(current_state) {
/* empty string command */
case START:
break;
/* malicious user : need learn how write shell commands */
case PIPE:
PRINT_ERR("syntax error near unexpected token `|'");
break;
/* malicious user : need learn how write shell commands */
case INPUT_REDIRECT:
PRINT_ERR("syntax error near unexpected token `newline'");
break;
/* malicious user : need learn how write shell commands */
case OUTPUT_REDIRECT:
PRINT_ERR("syntax error near unexpected token `newline'");
break;
/* execute command such that it's non blocking for shell terminal */
case NON_BLOCKING_COMMAND:
execute_shell_command(shell_command);
/* add this command to the currently running jobs in the background */
add_job(&(iscb.jobs), iscb.fg_job);
/* initialize the foreground job as empty */
init_job(&(iscb.fg_job), "");
return;
/* execute command such that it's blocking for shell terminal */
case COMMAND:
execute_shell_command(shell_command);
num_process++;
break;
}
break;
default:
break;
}
current_state = next_state;
}
/* wait for all the concurrently running process to get over */
for(int i = 0; i < num_process; i++) {
/* wait pid waits for any process to changes its state */
waitpid(-1, NULL, WUNTRACED);
}
/* restore the context of input output file descripters */
restore_io_context();
/* reset the original signals handlers */
reset_signals();
/* destroy the resource for foreground process which was running */
destroy_job(&(iscb.fg_job));
return;
}
/* function executes the simple shell command concurrently */
void execute_shell_command(IronShellCommand shell_command) {
pid_t pid = fork();
/* child process will execute command */
if(pid == 0) {
/* reset the signal handlers before executing the child process
* thus it executes with the default signal handlers given by kernel
*/
reset_signals();
execvp(shell_command.arguments[0], shell_command.arguments);
PRINT_ERR("execvp failed");
}
/* parent process will be remain unblocked and update the process control list */
else {
add_sub_job(&(iscb.fg_job), shell_command.arguments[0], pid);
return;
}
}
/* function executes the shell command connecting output to pipe */
void execute_shell_command_with_pipe(IronShellCommand shell_command, int pfd[]) {
pipe(pfd);
pid_t pid = fork();
if(pid == 0) {
/* close the read end and duplicate the STDOUT with write end */
close(pfd[0]);
dup2(pfd[1], STDOUT_FILENO);
/* reset the signal handlers before executing the child process
* thus it executes with the default signal handlers given by kernel
*/
reset_signals();
execvp(shell_command.arguments[0], shell_command.arguments);
PRINT_ERR("execvp failed");
} else {
/* this makes commands in pipe to execute concurrently and
* update the process control list
*/
add_sub_job(&(iscb.fg_job), shell_command.arguments[0], pid);
return;
}
}
/* saves the current context of the standard file descripters */
void save_io_context() {
io_context.stdin_fileno = dup(STDIN_FILENO);
io_context.stdout_fileno = dup(STDOUT_FILENO);
}
/* restores the context of the standard IO file descripter */
void restore_io_context() {
dup2(io_context.stdin_fileno, STDIN_FILENO);
dup2(io_context.stdout_fileno, STDOUT_FILENO);
}