-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.c
279 lines (255 loc) · 8.18 KB
/
main.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
/*
* project 1 (shell) main.c template
*
* project partner names and other information up here, please
*
*/
/* you probably won't need any other header files for this project */
#include "helpers.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <ctype.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <poll.h>
#include <signal.h>
#include <errno.h>
#include <assert.h>
int main(int argc, char **argv) {
//Establish anything we'll need constantly.
int mode = 1;//Start in sequential mode.
int usepath = 0;//does our path file exist?
int futuremode = mode;//This keeps track of mode changes until the end of the line.
int printPrompt = 0;
char *prompt = "s-term> ";//The prompt string.
printf("%s", prompt);//Print the prompt.
fflush(stdout);
//do pathstuff
char **paths = readFile("shell-config");
if(paths != NULL){
usepath = 1;
}
char **firststep = NULL;//The array of commands made by splitting the buffer along semicolons.
char ***secondstep = NULL;//The array of commands, with each command split along whitespace into its arguments.
struct node *head = NULL;//The head of the linked list of ongoing jobs.
char buffer[1024];//The buffer.
while (1) {//
struct pollfd pfd = {0, POLLIN};
if(printPrompt){
//Need to reprint the prompt.
printf("%s",prompt);
fflush(stdout);
printPrompt = 0;
}
int rv = poll(&pfd, 1, 1000);
if (rv==0){
//No change, use time to do other tasks
struct node *anode = head;
while(anode != NULL){
int pstatus = 0;
int pstate = waitpid((*anode).pid,&pstatus,WNOHANG);
if(pstate>0){
//Process has returned; print confirmation message and delete this node.
printf("Command %s, id %i was executed.\n",(*anode).command, anode->pid);
anode = (*anode).next;
listDelete(pstate, &head);
printPrompt = 1;
} else if(pstate<0){
//Error in waitpid, print error message and break from while loop.
printf("Error retrieving process status.\n");
break;
} else{
//Process has not returned.
anode = (*anode).next;
}
}
} else if (rv < 0){
//Poll went horribly wrong and we're bailing out of the flaming wreckage, screaming at the tops of our lungs.
printf("Polling error; shutting the (s)hell down.");
} else {
//Keyboard I/O
if(fgets(buffer, 1024, stdin) != NULL){
mode = futuremode;//Ensure that mode is up-to-date.
//Remove any comments after a hash.
removeComment(buffer);
//Tokenize buffer by semicolons- each will be an executable command.
firststep = tokenify(buffer,";");
secondstep = tokenify2(firststep," \t\n");
//Free firststep, as it is no longer needed. Free the sub-arrays first, then the array proper.
freeAll1(firststep);
free(firststep);
int j = 0;
int futureExit = 0;
int status = 0;
pid_t p = 1;
//Execute all commands in a loop.
while(secondstep[j] != NULL && secondstep[j][0] != NULL){
//check for the special commands mode or exit. If neither of these, fork and execv.
if(!strcasecmp(secondstep[j][0],"exit")){
int canwequit = 1;
struct node *tmp = head;
while(tmp != NULL){
if(tmp->state != 1){
canwequit = 0;
}
tmp = tmp->next;
}
if (canwequit){
futureExit = 1;//Will be checked at the end of the loop.
} else {
printf("Error: Jobs are currently running. Please wait for tasks to finish before exiting.\n");
}
}
else if(!strcasecmp(secondstep[j][0],"pause")){
setState(atoi(secondstep[j][1]), 1, head);
if(kill(atoi(secondstep[j][1]), SIGSTOP) != -1){
printf("Paused process %i\n",atoi(secondstep[j][1]));
}
else{
printf("Something went terribly wrong\n");
}
}
else if(!strcasecmp(secondstep[j][0],"resume")){
setState(atoi(secondstep[j][1]), 0, head);
if(kill(atoi(secondstep[j][1]), SIGCONT) != -1){
printf("Resumed process %i\n",atoi(secondstep[j][1]));
}
}
else if(!strcasecmp(secondstep[j][0],"jobs")){
struct node *tmp = head;
printf("\npid cmd paused\n");
while(tmp != NULL){
printf("%i %s %i\n",tmp->pid,tmp->command,tmp->state);
tmp = tmp->next;
}
}
/*else if(!strcasecmp(secondstep[j][0],"path")&& !strcasecmp(secondstep[j][1],"refresh")){
if(paths != NULL){
freeAll1(paths);
free(paths);
}
//do pathstuff
char **paths = readFile("shell-config");
if(paths == NULL){
usepath = 0;
}else{
usepath = 1;
}
}*/
else if(!strcasecmp(secondstep[j][0],"MODE")){
if(secondstep[j][1] == NULL){
if(mode == 0){
printf("\nCurrent mode is parallel\n");
}
else {
printf("\nCurrent mode is sequential\n");
}
}
else if(!strcasecmp(secondstep[j][1],"PARALLEL") || !strcasecmp(secondstep[j][1],"p")){
futuremode = 0;
}
else if(!strcasecmp(secondstep[j][1],"SEQUENTIAL") || !strcasecmp(secondstep[j][1],"s")){
futuremode = 1;
}
else {
//Bullshit users with their bullshit commands - throw an error.
printf("\nError: Your command was pretty awful.\n");
}
}
else{
//Fork and execute/wait depending on process id.
p = fork();
if (p == 0){
break;//Child processes handled outside the while loop.
}
if(mode==1){//Sequential mode.
wait(&status);
//Do something with childp; error checking, probably
} else {//Parallel mode; add this to the list
listInsert(p, secondstep[j][0], 0, &head);
}
}
j++;
}
if (p == 0){
if(usepath==1){
int k = 0;
while(paths[k] != NULL){
struct stat sr;
char tempbuffer[1024];
strcpy(tempbuffer, paths[k]);
strcat(tempbuffer, "/");
strcat(tempbuffer, secondstep[j][0]);
int rv = stat(tempbuffer, &sr);
if (rv < 0){
k++;
}
else{
secondstep[j][0]=tempbuffer;
if(execv(secondstep[j][0],secondstep[j])<0){
exit(0);
}
}
}
}
//Execv for an actual, non-hardcoded command.
printf("\n%s\n",secondstep[j][0]);
if(execv(secondstep[j][0],secondstep[j])<0){
fprintf(stderr, "Your command failed, and here's why you're a bad person: %s\n", strerror(errno));
}
exit(0);//Close out the child process corpse.
}
//check if there was an exit command earlier
if(futureExit == 1){
break;
}
//If we don't exit, free current buffer
freeAll2(secondstep);
free(secondstep);
//Make sure firststep and secondstep have an assigned value in case of early termination.
firststep = NULL;
secondstep = NULL;
printf("%s", prompt);
fflush(stdout);
}
if(feof(stdin)){
break;//End of file or Ctrl+D
}
}
}
//on a quit, flush our command array if it's not null already
if(secondstep != NULL){
freeAll2(secondstep);
free(secondstep);
}
//Free the paths array as well.
if(paths!=NULL){
freeAll1(paths);
free(paths);
}
//Check time spent in user mode and kernel mode. Right now I've got it separated by shell and processes, but we can add it together later.
int idParent = RUSAGE_SELF;
int idChild = RUSAGE_CHILDREN;
int statParent = 0;
int statChildren = 0;
struct rusage dataParent;
struct rusage dataChildren;
statParent = getrusage(idParent, &dataParent);
statChildren = getrusage(idChild, &dataChildren);
if(!statParent){//If the getrvalue operation was a success
printf("Shell time in user mode: %ld.%06ld seconds.\n", dataParent.ru_utime.tv_sec, dataParent.ru_utime.tv_usec);
printf("Shell time in kernel mode: %ld.%06ld seconds. \n", dataParent.ru_stime.tv_sec, dataParent.ru_stime.tv_usec);
}
if(!statChildren){
printf("Process time in user mode: %ld.%06ld seconds. \n", dataChildren.ru_utime.tv_sec, dataChildren.ru_utime.tv_usec);
printf("Process time in kernel mode: %ld.%06ld seconds. \n", dataChildren.ru_stime.tv_sec, dataChildren.ru_stime.tv_usec);
}
exit(0);
//Ctrl+D will drop you down here; need to make sure any cleanup also happens here too.
return 0;
}