-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnosh.c
261 lines (217 loc) · 8.39 KB
/
nosh.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
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <limits.h>
#include "plist.h"
#define MAX_INPUT_SIZE 1337
#define DELIMITER_CHARS " "
const size_t MAX_DIRECTORY_LENGTH_CONST = 100;
static int checkBackgroundProcess(pid_t pid, const char *cmd) {
int exitstatus = 0;
pid_t status = waitpid(pid, &exitstatus, WNOHANG );
if(status == -1) {
perror("waitpid");
}else if(status != 0) {
if(WIFEXITED(exitstatus)) { //if process specified by pid stopped, report exitstatus and remove process from list of background tasks
printf("Exitstatus [%s] = %d\n", cmd, WEXITSTATUS(exitstatus));
char *buf = ""; //buffer that needs to be given to removeElement
if(removeElement(pid, buf, 0) == -1){
fprintf(stderr, "element with pid %d does not exist in list", pid);
exit(EXIT_FAILURE);
}
}
}
return 0; //traverse through whole list (return value of 0 indicates going to next element in list)
}
static int printElements(pid_t pid, const char *cmd) {
printf("%i [%s]\n", pid, cmd);
return 0;
}
static void changeDirectory(char **args, int number_of_elements){
if(number_of_elements == 3) {
char directory_to_change_to[PATH_MAX];
char cwd[PATH_MAX];
if(getcwd(cwd, PATH_MAX) == NULL) {
perror("getcwd");
exit(EXIT_FAILURE);
}
if(strcmp(args[1], "..") == 0){
char cwd_for_strtok[PATH_MAX];
strcpy(cwd_for_strtok, cwd);
//strip cwd of prefix
char *delim = "/";
char *result;
char *token;
result = token = strtok(cwd_for_strtok, delim);
while((token = strtok(NULL, delim))) {
result = token;
}
strcpy(directory_to_change_to, cwd);
directory_to_change_to[strlen(cwd)-strlen(result)-1] = '\0'; //removes last directory from current working directory string
}else if(args[1][0] != '/') {
//functionality to cd using relative paths
strcat(directory_to_change_to, "/");
strcat(directory_to_change_to, args[1]);
}else{
strcpy(directory_to_change_to, args[1]);
}
chdir(directory_to_change_to);
}else{
printf("usage: cd <directory>");
}
}
static void printcwd(void){
char current_directory[PATH_MAX];
if(getcwd(current_directory, PATH_MAX) == NULL) {
//error handling
perror("getcwd");
}else {
printf("%s: ", current_directory);
}
}
static int readstdin(char *line){
/*reads line of MAX_INPUT_SIZE length from stdin and saves content to *line parameter. If input on stdin is overlength, flushes stdin and returns -1, returns 0 on success*/
if(fgets(line, MAX_INPUT_SIZE+1, stdin) == NULL){
/*fgets error handling*/
if(ferror(stdin)) { //real error -> terminate with EXIT_FAILURE
perror("fgets");
exit(EXIT_FAILURE);
}else{ //end of file -> "normal" termination
exit(EXIT_SUCCESS);
}
}
/*handling of overlength input:*/
//fgets reads a maximum of MAX_INPUT_SIZE chars so if MAX_INPUT_SIZE chars are read and the last char is not a newline it means that the input must be longer than MAX_INPUT_SIZE chars (excluding the newline)
if(line[strlen(line)-1] != '\n') {
printf("Your input was too long (> %d chars, including newline)\n", MAX_INPUT_SIZE);
//flushing stdin if input was longer than MAX_INPUT_SIZE chars
int character;
while((character = fgetc(stdin)) != '\n') {
//error handling for fgetc
if(character == EOF) {
//if there is an error, terminate.
if(ferror(stdin)) {
perror("fgetc");
exit(EXIT_FAILURE);
}
break;
}
}
//when stdin is flushed, continue reading the next line
return -1;
}else if(strlen(line) < 2){ //check if line was empty (only newline was sent)
return -1;
}
return 0;
}
int purgeList(pid_t pid, const char* cmd) {
char *buf = "";
removeElement(pid, buf, 0);
return 0;
}
int main(int argc, char const *argv[]) {
char *args[MAX_INPUT_SIZE]; //array to hold arguments as *char (Strings) (there cant be more arguments than input characters so MAX_INPUT_SIZE is the upper bound for number of arguments)
char line[MAX_INPUT_SIZE+1 * sizeof(char)]; //reserving memory for the contents of stdin that will be written to *line
int (*checkBackgroundProcessPtr) (pid_t, const char *) = &checkBackgroundProcess;
int isBackgroundProcess = 0; //flag to indicate if '&' symbol was typed after command
while(!feof(stdin)) {
/*collecting zombies of background tasks:*/
walkList(checkBackgroundProcessPtr);
/*printing current working directory:*/
printcwd();
/*reading arguments:*/
if(readstdin(line) == -1){ //reading MAX_INPUT_SIZE chars from stdin and saving them to *line
continue; //if input was overlength, skip to new command line
}
/*parsing input:*/
char *current_argument; //buffer in which each parsed argument will reside until it´s added to args array
if(line[strlen(line)-2] == '&') {
isBackgroundProcess = 1;
line[strlen(line)-2] = '\0';
} else {
line[strlen(line)-1] = '\0'; //removes "\n"
}
current_argument = strtok(line, DELIMITER_CHARS);
if(!current_argument) {
//line was empty
continue;
}
args[0] = current_argument; //adding command name to first position in arguments array
int number_of_args = 1;
do{
current_argument = strtok(NULL, DELIMITER_CHARS);
args[number_of_args] = current_argument;
number_of_args++;
}while (current_argument);
/*own commands*/
if(strcmp(args[0], "jobs") == 0) {
if(number_of_args > 2) {
printf("usage: jobs\n");
}else{
walkList(printElements);
}
continue;
}else if(strcmp(args[0], "cd") == 0) {
changeDirectory(args, number_of_args);
continue;
}
//make command string out of args array (without '&')
int i = 0;
char cmd[MAX_INPUT_SIZE]; //gets free´d in plist.c when removing element associated with cmd
cmd[0] = '\0'; //delete buffer
while(args[i]){
strcat(cmd, args[i]);
strcat(cmd, " ");
i++;
}
cmd[strlen(cmd)-1] = '\0'; //remove last space
/*starting new process:*/
pid_t pid = fork();
if(pid == -1) {
//forking failed
perror("fork");
exit(EXIT_FAILURE);
}else if (pid == 0) {
//in child proccess -> executing command
execvp(args[0], args);
//exec only returns on error
perror("exec");
exit(EXIT_FAILURE);
}else {
//in parent proccess
//differentiate between fore- and background tasks
if(isBackgroundProcess) {
printf("background process initiated\n");
//background task
insertElement(pid, cmd); //add background task to list of background tasks
}else{
//foreground task -> suspend thread until child terminates
int exitstatus = 0;
waitpid(pid, &exitstatus, 0);
if(WIFEXITED(exitstatus)) {
printf("Exitstatus [%s] = %d\n", cmd, WEXITSTATUS(exitstatus));
}
}
}
isBackgroundProcess = 0;
}
int (*purgeListPtr) (pid_t, const char *) = &purgeList;
walkList(purgeListPtr); //remove all still running background processes from list when exiting from shell
return 0;
}
/*
int start_new_process() {
pid_t pid = fork();
if(pid == -1) {
//forking failed
}else if (pid == 0) {
//in child proccess
}else {
//in parent proccess
}
}
*/