/
pr7.3.c
444 lines (358 loc) · 8.91 KB
/
pr7.3.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
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
/* CMPSC 311, Spring 2013, Project 7
*
* Author: Jacob Jones
* Email: jaj5333@psu.edu
*
* Author: Scott Cheloha
* Email: ssc5145@psu.edu
*/
/*----------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdbool.h>
#include "pr7.h"
#include "wrapper.h"
#include "builtin.h"
#include "linked.h"
#include "exec.h"
#define MAXARGS 128
char *s_filename = "pr7.init"; // default startup file name
const char *prog = "pr7";
char *infile_name = "[no name]";
int i_flag = 0;
int e_flag = 0;
int v_flag = 0;
int d_flag = 0;
int s_flag = 0;
int exec_flag = 0;
int command_count = 0; // number of commands submitted since startup
pid_t shell_pid = 0;
pid_t fg_pid = 0;
pid_t fg_pgid = 0;
pid_t bg_pgid = 0;
struct process_list *bg_processes;
/*----------------------------------------------------------------------------*/
int main(int argc, char *argv[])
{
shell_pid = getpid();
eval_options(argc, argv);
/* install non-default signal handlers for shell */
Signal(SIGCHLD, handler_SIGCHLD, __func__, __LINE__);
Signal(SIGINT, handler_SIGINT, __func__, __LINE__);
int status = EXIT_SUCCESS;
/* Say hello */
if (v_flag)
{
verbose_greeting();
}
bg_processes = process_list_allocate();
read_startup_file((bool) !s_flag);
/* Can't continue unless interactivity or a command file are specified */
if (argc == 1)
{
usage(EXIT_FAILURE);
}
status = read_input(argc, argv);
return status;
}
/*----------------------------------------------------------------------------*/
void usage(int status)
{
if (status == EXIT_SUCCESS)
{
printf("usage: %s [-h] [-e] [-v] [-d] [-V] [-s sfile] [command file]\n", prog);
puts(" -h print help");
puts(" -i interactive mode");
puts(" -e echo commands before execution");
puts(" -v verbose mode; enable extra printing; can be repeated");
puts(" -d debug information for memory allocation");
puts(" -V enables verbose and debug modes");
puts(" -s sfile use sfile as startup file, default is pr7.init");
puts("\nNOTE: Use -i OR specify a command file for input");
}
else
{
fprintf(stderr, "%s: Try '%s -h' for usage information.\n", prog, prog);
}
exit(status);
}
/*-----------------------------------------------------------------------------*/
void verbose_greeting(void)
{
printf("-%s: Hello, %s!\n", prog, getenv("USER"));
Options();
}
/*----------------------------------------------------------------------------*/
void read_startup_file(bool quiet)
{
FILE *fp;
/* If the user specifies '-', we take our reads from stdin,
* otherwise we attempt to open s_filename for reading
*/
if (!strcmp(s_filename, "-"))
{
fp = stdin;
}
else if ( (fp = fopen(s_filename, "r")) == NULL )
{
if (!quiet)
{
fprintf(stderr, "-%s: failed to open startup file %s\n",
prog, s_filename);
}
return;
}
char cmdline[MAX_LINE]; /* command line */
while (fgets(cmdline, MAX_LINE, fp) != NULL)
{
eval_line(cmdline);
}
return;
}
/*----------------------------------------------------------------------------*/
void print_prompt(int newline)
{
if (newline)
{
puts("");
}
printf("%s %d%% ", prog, command_count);
command_count++;
return;
}
/*----------------------------------------------------------------------------*/
int read_input(int argc, char *argv[])
{
FILE *infile;
char *infile_name;
int status;
if (i_flag)
{
infile = stdin; infile_name = "[stdin]";
}
else
{
infile_name = argv[argc-1]; /* also use strdup()?...no */
if ((infile = Fopen(infile_name, "r", __func__, __LINE__)) == NULL)
{
printf("-%s: shell failed to read command file: ensure file '%s' exists\n",
prog, argv[argc-1]);
exit(EXIT_FAILURE); // brutally fail
}
}
char cmdline[MAX_LINE];
if (i_flag)
{
print_prompt(0);
}
while (fgets(cmdline, MAX_LINE, infile) != NULL)
{
/* issue prompt and read command line */
/*
if (feof(infile))
{
break;
}
*/
status = eval_line(cmdline);
if (i_flag)
{
print_prompt(0);
}
}
if (ferror(infile))
{
printf("-%s: error reading input: %s\n", prog, strerror(errno));
}
Fclose(infile, __func__, __LINE__);
return status;
}
/*----------------------------------------------------------------------------*/
// Runs a getopt() loop to harvest command-line options for pr7
void eval_options(int argc, char *argv[])
{
extern char *optarg;
extern int optopt;
int ch;
/* set flags */
while ((ch = getopt(argc, argv, ":hievdVs:")) != -1)
{
switch (ch)
{
case 'h':
usage(EXIT_SUCCESS);
break;
case 'i':
i_flag = 1;
break;
case 'e':
e_flag = 1;
break;
case 'v':
v_flag = 1;
break;
case 'd':
d_flag = 1;
break;
case 'V':
v_flag = 1; d_flag = 1;
break;
case 's':
s_flag++;
if (s_flag > 1)
{
fprintf(stderr, "-%s: error: cannot specify more than one startup file\n",
prog);
exit(1);
}
else
{
s_filename = optarg;
}
break;
case '?':
fprintf(stderr, "-%s: error: invalid option '%c'\n", prog, optopt);
usage(EXIT_FAILURE);
break;
case ':':
fprintf(stderr, "-%s: error: option '%c' needs filename argument\n",
prog, optopt);
usage(EXIT_FAILURE);
break;
default:
usage(EXIT_FAILURE);
break;
}
}
}
/*----------------------------------------------------------------------------*/
int eval_line(char *cmdline)
{
char *Argv[MAXARGS]; /* Argv for execve() */
char buf[MAX_LINE]; /* holds modified command line */
strcpy(buf, cmdline); /* buf[] will be modified by parse() */
int background = parse(buf, Argv);
int status = EXIT_SUCCESS;
/* ignore empty lines */
if (Argv[0] == NULL)
{
return status;
}
if (e_flag)
{
for (int i = 0; Argv[i] != NULL; i++)
{
printf("%s ", Argv[i]);
}
puts("");
}
if (Builtin(Argv))
{
return status;
}
status = new_child(Argv, background, status);
return status;
}
/*----------------------------------------------------------------------------*/
/* parse the command line and build the Argv array
*
* Compare to parseline() in CS:APP Fig. 8.24.
*/
int parse(char *buf, char *Argv[])
{
char *delim; /* points to first whitespace delimiter */
int argc = 0; /* number of args */
int bg; /* background job? */
buf[strlen(buf)-1] = ' '; /* Replace trailing ’\n’ with space */
while (*buf && (*buf == ' ')) /* Ignore leading spaces */
{
buf++;
}
/* build the Argv list */
while ((delim = strchr(buf, ' ')))
{
Argv[argc++] = buf; /* start Argv[i] */
*delim = '\0'; /* terminate Argv[i] */
buf = delim + 1; /* start Argv[i+1]? */
while (*buf && (*buf == ' '))
{
buf++;
}
}
Argv[argc] = NULL;
if (argc == 0) /* blank line */
{
return 0;
}
/* Should the job run in the background? */
if ((bg = (strcmp(Argv[argc-1], "&") == 0)))
{
Argv[--argc] = NULL;
}
return bg;
}
/*----------------------------------------------------------------------------*/
/* Prints an error message
* format is:
* -[program name]: [command name]: [msg1]
*/
void shell_msg(const char* function_name, const char* msg)
{
fprintf(stderr, "-%s: %s: %s\n", prog, function_name, msg);
}
/*----------------------------------------------------------------------------*/
void handler_SIGCHLD(int signum)
{
(void) signum;
int status;
/* We received a SIGCHLD signal from a background process,
* so we loop through
* the background process list and reap all zombies.
*/
for (pid_t pid = waitpid(-1, &status, WNOHANG);
pid != 0 && pid != -1;
pid = waitpid(-1, &status, WNOHANG))
{
if (pid == fg_pid)
{
fg_pid = fg_pgid = 0;
}
else
{
process_list_pop(bg_processes, pid);
// reprint the prompt
if (i_flag)
{
print_prompt(0);
fflush(stdout);
}
}
}
return;
}
/*----------------------------------------------------------------------------*/
void handler_SIGINT(int signum)
{
// if the fg process group exists, forward sigint to
// every process in the group
if (fg_pgid != 0)
{
Kill(-1 * fg_pgid, signum, 0, __func__, __LINE__);
puts("");
}
// if the fg process group does not exist,
// simply re-print the prompt
else if (fg_pgid == 0 && getpid() == shell_pid)
{
printf("\b\b ");
print_prompt(1);
fflush(stdout);
}
return;
}
/*----------------------------------------------------------------------------*/