-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathsmallsh.c
More file actions
345 lines (303 loc) · 8.42 KB
/
smallsh.c
File metadata and controls
345 lines (303 loc) · 8.42 KB
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
345
/***************************************************************
* Filename: smallsh.c
* Author: Brent Irwin
* Date: 21 May 2017
* Description: Main file for smallsh, CS 344 Program 3
***************************************************************/
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#define INPUTLENGTH 2048
int allowBackground = 1;
void getInput(char*[], int*, char[], char[], int);
void execCmd(char*[], int*, struct sigaction, int*, char[], char[]);
void catchSIGTSTP(int);
void printExitStatus(int);
/* ---------------------- Main function --------------------- */
/* main() gets the input, checks for blanks or comments, processes
* CD, EXIT, and STATUS, and then executes the command if the input
* didn't call for any of the other functions.
* ---------------------------------------------------------- */
int main() {
int pid = getpid();
int cont = 1;
int i;
int exitStatus = 0;
int background = 0;
char inputFile[256] = "";
char outputFile[256] = "";
char* input[512];
for (i=0; i<512; i++) {
input[i] = NULL;
}
// Signal Handlers
// Ignore ^C
struct sigaction sa_sigint = {0};
sa_sigint.sa_handler = SIG_IGN;
sigfillset(&sa_sigint.sa_mask);
sa_sigint.sa_flags = 0;
sigaction(SIGINT, &sa_sigint, NULL);
// Redirect ^Z to catchSIGTSTP()
struct sigaction sa_sigtstp = {0};
sa_sigtstp.sa_handler = catchSIGTSTP;
sigfillset(&sa_sigtstp.sa_mask);
sa_sigtstp.sa_flags = 0;
sigaction(SIGTSTP, &sa_sigtstp, NULL);
do {
// Get input
getInput(input, &background, inputFile, outputFile, pid);
// COMMENT OR BLANK
if (input[0][0] == '#' ||
input[0][0] == '\0') {
continue;
}
// EXIT
else if (strcmp(input[0], "exit") == 0) {
cont = 0;
}
// CD
else if (strcmp(input[0], "cd") == 0) {
// Change to the directory specified
if (input[1]) {
if (chdir(input[1]) == -1) {
printf("Directory not found.\n");
fflush(stdout);
}
} else {
// If directory is not specified, go to ~
chdir(getenv("HOME"));
}
}
// STATUS
else if (strcmp(input[0], "status") == 0) {
printExitStatus(exitStatus);
}
// Anything else
else {
execCmd(input, &exitStatus, sa_sigint, &background, inputFile, outputFile);
}
// Reset variables
for (i=0; input[i]; i++) {
input[i] = NULL;
}
background = 0;
inputFile[0] = '\0';
outputFile[0] = '\0';
} while (cont);
return 0;
}
/***************************************************************
* void getInput(char*[], int, char*, char*, int)
*
* Prompts the user and parses the input into an array of words
*
* INPUT:
* char*[512] arr The output array
* int* background Is this a background process? boolean
* char* inputName Name of an input file
* char* outputName Name of an output file
* int pid PID of smallsh
*
* OUTPUT:
* void - n/a
***************************************************************/
void getInput(char* arr[], int* background, char inputName[], char outputName[], int pid) {
char input[INPUTLENGTH];
int i, j;
// Get input
printf(": ");
fflush(stdout);
fgets(input, INPUTLENGTH, stdin);
// Remove newline
int found = 0;
for (i=0; !found && i<INPUTLENGTH; i++) {
if (input[i] == '\n') {
input[i] = '\0';
found = 1;
}
}
// If it's blank, return blank
if (!strcmp(input, "")) {
arr[0] = strdup("");
return;
}
// Translate rawInput into individual strings
const char space[2] = " ";
char *token = strtok(input, space);
for (i=0; token; i++) {
// Check for & to be a background process
if (!strcmp(token, "&")) {
*background = 1;
}
// Check for < to denote input file
else if (!strcmp(token, "<")) {
token = strtok(NULL, space);
strcpy(inputName, token);
}
// Check for > to denote output file
else if (!strcmp(token, ">")) {
token = strtok(NULL, space);
strcpy(outputName, token);
}
// Otherwise, it's part of the command!
else {
arr[i] = strdup(token);
// Replace $$ with pid
// Only occurs at end of string in testscirpt
for (j=0; arr[i][j]; j++) {
if (arr[i][j] == '$' &&
arr[i][j+1] == '$') {
arr[i][j] = '\0';
snprintf(arr[i], 256, "%s%d", arr[i], pid);
}
}
}
// Next!
token = strtok(NULL, space);
}
}
/***************************************************************
* void execCmd(char*[], int*, struct sigaction, int*, char[], char[])
*
* Executes the command parsed into arr[]
*
* INPUT:
* char* arr[] The array with command information
* int* childExitStatus The success status of the command
* struct sigaction sa The sigaction for SIGINT
* int* background Is it a background process? boolean
* char inputName[] The name of the input file
* char outputName[] The name of the output file
*
* OUTPUT:
* void - n/a
***************************************************************/
void execCmd(char* arr[], int* childExitStatus, struct sigaction sa, int* background, char inputName[], char outputName[]) {
int input, output, result;
pid_t spawnPid = -5;
// Fork that shit
// The structure of this is from Lecture 3.1, not much changed
spawnPid = fork();
switch (spawnPid) {
case -1: ;
perror("Hull Breach!\n");
exit(1);
break;
case 0: ;
// The process will now take ^C as default
sa.sa_handler = SIG_DFL;
sigaction(SIGINT, &sa, NULL);
// Handle input, code is basically straight from Lecture 3.4
if (strcmp(inputName, "")) {
// open it
input = open(inputName, O_RDONLY);
if (input == -1) {
perror("Unable to open input file\n");
exit(1);
}
// assign it
result = dup2(input, 0);
if (result == -1) {
perror("Unable to assign input file\n");
exit(2);
}
// trigger its close
fcntl(input, F_SETFD, FD_CLOEXEC);
}
// Handle output, code is basically straight from Lecture 3.4
if (strcmp(outputName, "")) {
// open it
output = open(outputName, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (output == -1) {
perror("Unable to open output file\n");
exit(1);
}
// assign it
result = dup2(output, 1);
if (result == -1) {
perror("Unable to assign output file\n");
exit(2);
}
// trigger its close
fcntl(output, F_SETFD, FD_CLOEXEC);
}
// Execute it!
if (execvp(arr[0], (char* const*)arr)) {
printf("%s: no such file or directory\n", arr[0]);
fflush(stdout);
exit(2);
}
break;
default: ;
// Execute a process in the background ONLY if allowBackground
if (*background && allowBackground) {
pid_t actualPid = waitpid(spawnPid, childExitStatus, WNOHANG);
printf("background pid is %d\n", spawnPid);
fflush(stdout);
}
// Otherwise execute it like normal
else {
pid_t actualPid = waitpid(spawnPid, childExitStatus, 0);
}
// Check for terminated background processes!
while ((spawnPid = waitpid(-1, childExitStatus, WNOHANG)) > 0) {
printf("child %d terminated\n", spawnPid);
printExitStatus(*childExitStatus);
fflush(stdout);
}
}
}
/***************************************************************
* void catchSIGTSTP(int)
*
* When SIGTSTP is called, toggle the allowBackground boolean.
*
* I didn't know how to pass my own variables into this, so I made
* allowBackground a global variable. I know that's bad form.
*
* INPUT:
* int signo Required, according to the homework. Isn't used.
*
* OUTPUT:
* void - n/a
***************************************************************/
void catchSIGTSTP(int signo) {
// If it's 1, set it to 0 and display a message reentrantly
if (allowBackground == 1) {
char* message = "Entering foreground-only mode (& is now ignored)\n";
write(1, message, 49);
fflush(stdout);
allowBackground = 0;
}
// If it's 0, set it to 1 and display a message reentrantly
else {
char* message = "Exiting foreground-only mode\n";
write (1, message, 29);
fflush(stdout);
allowBackground = 1;
}
}
/***************************************************************
* void printExitStatus(int)
*
* Calls the exit status
*
* INPUT:
* int childExitMethod Child Exit Method
*
* OUTPUT:
* void - n/a
***************************************************************/
void printExitStatus(int childExitMethod) {
if (WIFEXITED(childExitMethod)) {
// If exited by status
printf("exit value %d\n", WEXITSTATUS(childExitMethod));
} else {
// If terminated by signal
printf("terminated by signal %d\n", WTERMSIG(childExitMethod));
}
}