-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.c
More file actions
269 lines (207 loc) · 7.07 KB
/
Copy pathmain.c
File metadata and controls
269 lines (207 loc) · 7.07 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
/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*\
*) ASSIGNMENT 3: SMALLSH (PORTFOLIO ASSIGNMENT) * * * * (*
(* Provide a prompt for running commands * DONE * *)
*) Handle blank lines and comments * DONE * (*
(* Provide expansion for the variable $$ * DONE * *)
*) exit, cd, and status built into the shell * DONE * (*
(* Execute other commands with new processes * DONE * *)
*) Support input and output redirection * FIX * (*
(* Support running commands in background * DONE * *)
*) custom handlers for SIGINT and SIGTSTP * 0/1 * (*
(* > > > > STATUS: 7 / 8 < < < ^ * * * * *)
\*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <stdbool.h>
#define MAX_LENGTH 512
#define MAX_ARGS 2048
// Exit status of most recently executed foreground process
int status = 0;
// Indicates whether background processes are disabled
bool foregroundOnlyMode = false;
int numBGProcesses = 0; // Tracks number of background processes
int BGProcesses[MAX_ARGS]; // Array storing PIDs of background processes
void getArgs(int *argc, char **argv); // OBJ 1, 2
void shell(int argc, char **argv); // OBJ 4, 5
void handleRedirection(char **argv); // OBJ 6
void checkBGProcesses(); // OBJ 7
char *replacePID(const char *string, const char *pid_str); // OBJ 3
int main() {
// Clear terminal
printf("\033[H\033[J$ smallsh\n");
while(1) {
// Total line input
char *argList[MAX_ARGS];
// Amount of arguments in line
int argCount = 0;
getArgs(&argCount, argList);
shell(argCount, argList);
for (int i = 0; i < argCount; i++) {
free(argList[i]);
}
}
return 0;
}
void getArgs(int *argc, char **argv) {
char input[MAX_LENGTH];
char *processArg;
pid_t pid = getpid();
char pid_str[16];
snprintf(pid_str, 16, "%d", pid);
do {
printf(": ");
fgets(input, MAX_LENGTH, stdin);
strtok(input, "\n");
} while(input[0] == '#' || strlen(input) == 1);
// separate each process by a blank line
processArg = strtok(input, " ");
while (processArg != NULL) {
// Determining if "$$" is present and replace with pid
if (strstr(processArg, "$$") != NULL) {
argv[*argc] = replacePID(processArg, pid_str);
} else {
argv[*argc] = strdup(processArg);
}
// Incrememt argument count to rerun while loop
(*argc)++;
processArg = strtok(NULL, " ");
}
// Set the last argument + 1 to NULL
argv[*argc] = NULL;
}
void shell(int argc, char **argv) {
// Check for background command
bool background = false;
if (argc > 0 && strcmp(argv[argc - 1], "&") == 0) {
// Set background bool to true and delete trailing "&"
background = true;
argv[argc - 1] = NULL;
argc--;
}
// "exit" special command check
if (strcmp(argv[0], "exit") == 0) exit(0);
// "cd" special command check
if (strcmp(argv[0], "cd") == 0) {
if (argc == 1)
chdir(getenv("HOME"));
else if (chdir(argv[1]) != 0) {
printf("cd: %s: No such file or directory\n", argv[1]);
}
return;
}
// "status" special command check
if (strcmp(argv[0], "status") == 0) {
if (WIFEXITED(status))
printf("exit status %d\n", WEXITSTATUS(status));
else if (WIFSIGNALED(status))
printf("terminated by signal %d\n", WTERMSIG(status));
return;
}
// Create new process by forking "process" to hold PID of the child process
// Returns 0 in the child process on success, -1 in the parent on failure
pid_t process = fork();
if (process == -1) {
perror("fork failed");
} else if (process == 0) { // Ran by child process
handleRedirection(argv);
// Runs command using argv as the list of arguments
execvp(argv[0], argv);
exit(1);
} else { // Ran by parent process
if (background && !foregroundOnlyMode) {
printf("background pid is %d\n", process);
BGProcesses[numBGProcesses++] = process;
} else {
// Allow child process to complete and store it's status
waitpid(process, &status, 0);
if (WIFSIGNALED(status)) {
printf("terminated by signal %d\n", WTERMSIG(status));
}
}
}
checkBGProcesses();
}
// FIX
void handleRedirection(char **argv) {
// For every argument passed in line
for (int i = 0; argv[i] != NULL; i++) {
// if current iterated argument is <
if (strcmp(argv[i], "<") == 0 && argv[i + 1] != NULL) {
// Defining the file descriptor
int FD = open(argv[i + 1], O_RDONLY);
// ensuring open() succeeded
if (FD == -1) exit(1);
// Redirect stdin to the opened file descriptor
// This allows the command to read input from the specified file
dup2(FD, STDIN_FILENO);
// closing file descriptor
close(FD);
// Ignoring < now that operation is processed
argv[i] = NULL;
}
// if current iterated argument is >
if (strcmp(argv[i], ">") == 0 && argv[i + 1] != NULL) {
// Defining the file descriptor
int FD = open(argv[i + 1], O_WRONLY | O_CREAT | O_TRUNC, 0644);
// ensuring open() succeeded
if (FD == -1) exit(1);
// Redirect stdout to the opened file descriptor
// This allows the command to write output to the specified file
dup2(FD, STDOUT_FILENO);
// closing file descriptor
close(FD);
// Ignoring > now that operation is processed
argv[i] = NULL;
}
}
}
void checkBGProcesses() {
// For every background process
for (int i = 0; i < numBGProcesses; i++) {
int BGStatus;
// check status of background process without blocking
// WNOHANG makes waitpid return 0 instead of waiting
pid_t result = waitpid(BGProcesses[i], &BGStatus, WNOHANG);
// waitpid returns a positive value upon finishing
if (result > 0)
// Check if the process exited normally
if (WIFEXITED(BGStatus)) {
// Print the exit status of the completed BG process
printf("background pid %d is done: exit value %d\n",
result, WEXITSTATUS(BGStatus));
// Check if process was terminated by a signal
} else if (WIFSIGNALED(BGStatus)) {
// Print signal number that terminated the BG process
printf("background pid %d is done: terminated by signal %d\n",
result, WTERMSIG(BGStatus));
}
// Remove completed process from list of BG processes
BGProcesses[i] = BGProcesses[numBGProcesses--];
// Recheck index in for loop now that numBGProcesses is decremented
i--;
}
}
char *replacePID(const char *string, const char *pid_str) {
char newString[MAX_LENGTH] = "";
const char *startPosition = string;
char *dollarPosition;
// Search for the first occurrence of "$$" starting from startPosition
// dollarPosition holds the pointer to the first occurence of $$
while ((dollarPosition = strstr(startPosition, "$$")) != NULL) {
// Copying text before "$$"
strncat(newString, startPosition, dollarPosition - startPosition);
// Append the process ID with the new string
strcat(newString, pid_str);
// Move the startPosition past the "$$"
startPosition = dollarPosition + 2;
}
// Append the rest of the string after the last "$$"
strcat(newString, startPosition);
// Copy new string onto old argument
return strdup(newString);
}