Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
script3r
GitHub Repository: script3r/os161
Path: blob/master/user/bin/sh/sh.c
734 views
1
/*
2
* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
3
* The President and Fellows of Harvard College.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
* 3. Neither the name of the University nor the names of its contributors
14
* may be used to endorse or promote products derived from this software
15
* without specific prior written permission.
16
*
17
* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
18
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
21
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
* SUCH DAMAGE.
28
*/
29
30
/*
31
* sh - shell
32
*
33
* Usage:
34
* sh
35
* sh -c command
36
*/
37
38
#include <sys/types.h>
39
#include <sys/wait.h>
40
#include <assert.h>
41
#include <unistd.h>
42
#include <stdlib.h>
43
#include <stdio.h>
44
#include <string.h>
45
#include <limits.h>
46
#include <errno.h>
47
#include <err.h>
48
49
#ifdef HOST
50
#include "hostcompat.h"
51
#endif
52
53
#ifndef NARG_MAX
54
/* no NARG_MAX on most unixes */
55
#define NARG_MAX 1024
56
#endif
57
58
/* avoid making this unreasonably large; causes problems under dumbvm */
59
#if ARG_MAX > 4096
60
#define CMDLINE_MAX 4096
61
#else
62
#define CMDLINE_MAX ARG_MAX
63
#endif
64
65
/* set to nonzero if __time syscall seems to work */
66
static int timing = 0;
67
68
/* array of backgrounded jobs (allows "foregrounding") */
69
#define MAXBG 128
70
static pid_t bgpids[MAXBG];
71
72
/*
73
* can_bg
74
* just checks for an open slot.
75
*/
76
static
77
int
78
can_bg(void)
79
{
80
int i;
81
82
for (i = 0; i < MAXBG; i++) {
83
if (bgpids[i] == 0) {
84
return 1;
85
}
86
}
87
88
return 0;
89
}
90
91
/*
92
* remember_bg
93
* sticks the pid in an open slot in the background array. note the assert --
94
* better check can_bg before calling this.
95
*/
96
static
97
void
98
remember_bg(pid_t pid)
99
{
100
int i;
101
for (i = 0; i < MAXBG; i++) {
102
if (bgpids[i] == 0) {
103
bgpids[i] = pid;
104
return;
105
}
106
}
107
assert(0);
108
}
109
110
/*
111
* printstatus
112
* print results from wait
113
*/
114
static
115
void
116
printstatus(int status)
117
{
118
if (WIFEXITED(status)) {
119
printf("Exit %d", WEXITSTATUS(status));
120
}
121
else if (WIFSIGNALED(status) && WCOREDUMP(status)) {
122
printf("Signal %d (core dumped)", WTERMSIG(status));
123
}
124
else if (WIFSIGNALED(status)) {
125
printf("Signal %d", WTERMSIG(status));
126
}
127
else if (WIFSTOPPED(status)) {
128
printf("Stopped on signal %d", WSTOPSIG(status));
129
}
130
else {
131
printf("Invalid status code %d", status);
132
}
133
}
134
135
/*
136
* dowait
137
* just does a waitpid.
138
*/
139
static
140
void
141
dowait(pid_t pid)
142
{
143
int status;
144
if (waitpid(pid, &status, 0)<0) {
145
warn("pid %d", pid);
146
}
147
else {
148
printf("pid %d: ", pid);
149
printstatus(status);
150
printf("\n");
151
}
152
}
153
154
#ifdef WNOHANG
155
/*
156
* dowaitpoll
157
* like dowait, but uses WNOHANG. returns true if we got something.
158
*/
159
static
160
int
161
dowaitpoll(pid_t pid)
162
{
163
int status;
164
pid_t result;
165
result = waitpid(pid, &status, WNOHANG);
166
if (result<0) {
167
warn("pid %d", pid);
168
}
169
else if (result!=0) {
170
printf("pid %d: ", pid);
171
printstatus(status);
172
printf("\n");
173
return 1;
174
}
175
return 0;
176
}
177
178
/*
179
* waitpoll
180
* poll all background jobs for having exited.
181
*/
182
static
183
void
184
waitpoll(void)
185
{
186
int i;
187
for (i=0; i < MAXBG; i++) {
188
if (bgpids[i] != 0) {
189
if (dowaitpoll(bgpids[i])) {
190
bgpids[i] = 0;
191
}
192
}
193
}
194
}
195
#endif /* WNOHANG */
196
197
/*
198
* wait
199
* allows the user to "foreground" a process by waiting on it. without ps to
200
* know the pids, this is a little tough to use with an arg, but without an
201
* arg it will wait for all the background jobs.
202
*/
203
static
204
int
205
cmd_wait(int ac, char *av[])
206
{
207
int i;
208
pid_t pid;
209
210
if (ac == 2) {
211
pid = atoi(av[1]);
212
dowait(pid);
213
for (i = 0; i < MAXBG; i++) {
214
if (bgpids[i]==pid) {
215
bgpids[i] = 0;
216
}
217
}
218
return 0;
219
}
220
else if (ac == 1) {
221
for (i=0; i < MAXBG; i++) {
222
if (bgpids[i] != 0) {
223
dowait(bgpids[i]);
224
bgpids[i] = 0;
225
}
226
}
227
return 0;
228
}
229
printf("Usage: wait [pid]\n");
230
return 1;
231
}
232
233
/*
234
* chdir
235
* just an interface to the system call. no concept of home directory, so
236
* require the directory.
237
*/
238
static
239
int
240
cmd_chdir(int ac, char *av[])
241
{
242
if (ac == 2) {
243
if (chdir(av[1])) {
244
warn("chdir");
245
return 1;
246
}
247
return 0;
248
}
249
printf("Usage: chdir dir\n");
250
return 1;
251
}
252
253
/*
254
* exit
255
* pretty simple. allow the user to choose the exit code if they want,
256
* otherwise default to 0 (success).
257
*/
258
static
259
int
260
cmd_exit(int ac, char *av[])
261
{
262
int code;
263
264
if (ac == 1) {
265
code = 0;
266
}
267
else if (ac == 2) {
268
code = atoi(av[1]);
269
}
270
else {
271
printf("Usage: exit [code]\n");
272
return 1;
273
}
274
275
exit(code);
276
277
return 0; /* quell the compiler warning */
278
}
279
280
/*
281
* a struct of the builtins associates the builtin name with the function that
282
* executes it. they must all take an argc and argv.
283
*/
284
static struct {
285
const char *name;
286
int (*func)(int, char **);
287
} builtins[] = {
288
{ "cd", cmd_chdir },
289
{ "chdir", cmd_chdir },
290
{ "exit", cmd_exit },
291
{ "wait", cmd_wait },
292
{ NULL, NULL }
293
};
294
295
/*
296
* docommand
297
* tokenizes the command line using strtok. if there aren't any commands,
298
* simply returns. checks to see if it's a builtin, running it if it is.
299
* otherwise, it's a standard command. check for the '&', try to background
300
* the job if possible, otherwise just run it and wait on it.
301
*/
302
static
303
int
304
docommand(char *buf)
305
{
306
char *args[NARG_MAX + 1];
307
int nargs, i;
308
char *s;
309
pid_t pid;
310
int status;
311
int bg=0;
312
time_t startsecs, endsecs;
313
unsigned long startnsecs, endnsecs;
314
315
nargs = 0;
316
for (s = strtok(buf, " \t\r\n"); s; s = strtok(NULL, " \t\r\n")) {
317
if (nargs >= NARG_MAX) {
318
printf("%s: Too many arguments "
319
"(exceeds system limit)\n",
320
args[0]);
321
return 1;
322
}
323
args[nargs++] = s;
324
}
325
args[nargs] = NULL;
326
327
if (nargs==0) {
328
/* empty line */
329
return 0;
330
}
331
332
for (i=0; builtins[i].name; i++) {
333
if (!strcmp(builtins[i].name, args[0])) {
334
return builtins[i].func(nargs, args);
335
}
336
}
337
338
/* Not a builtin; run it */
339
340
if (nargs > 0 && !strcmp(args[nargs-1], "&")) {
341
/* background */
342
if (!can_bg()) {
343
printf("%s: Too many background jobs; wait for "
344
"some to finish before starting more\n",
345
args[0]);
346
return -1;
347
}
348
nargs--;
349
args[nargs] = NULL;
350
bg = 1;
351
}
352
353
if (timing) {
354
__time(&startsecs, &startnsecs);
355
}
356
357
pid = fork();
358
switch (pid) {
359
case -1:
360
/* error */
361
warn("fork");
362
return _MKWAIT_EXIT(255);
363
case 0:
364
/* child */
365
execv(args[0], args);
366
warn("%s", args[0]);
367
/*
368
* Use _exit() instead of exit() in the child
369
* process to avoid calling atexit() functions,
370
* which would cause hostcompat (if present) to
371
* reset the tty state and mess up our input
372
* handling.
373
*/
374
_exit(1);
375
default:
376
break;
377
}
378
379
/* parent */
380
if (bg) {
381
/* background this command */
382
remember_bg(pid);
383
printf("[%d] %s ... &\n", pid, args[0]);
384
return 0;
385
}
386
387
if (waitpid(pid, &status, 0) < 0) {
388
warn("waitpid");
389
status = -1;
390
}
391
392
if (timing) {
393
__time(&endsecs, &endnsecs);
394
if (endnsecs < startnsecs) {
395
endnsecs += 1000000000;
396
endsecs--;
397
}
398
endnsecs -= startnsecs;
399
endsecs -= startsecs;
400
warnx("subprocess time: %lu.%09lu seconds",
401
(unsigned long) endsecs, (unsigned long) endnsecs);
402
}
403
404
return status;
405
}
406
407
/*
408
* getcmd
409
* pulls valid characters off the console, filling the buffer.
410
* backspace deletes a character, simply by moving the position back.
411
* a newline or carriage return breaks the loop, which terminates
412
* the string and returns.
413
*
414
* if there's an invalid character or a backspace when there's nothing
415
* in the buffer, putchars an alert (bell).
416
*/
417
static
418
void
419
getcmd(char *buf, size_t len)
420
{
421
size_t pos = 0;
422
int done=0, ch;
423
424
/*
425
* In the absence of a <ctype.h>, assume input is 7-bit ASCII.
426
*/
427
428
while (!done) {
429
ch = getchar();
430
if ((ch == '\b' || ch == 127) && pos > 0) {
431
putchar('\b');
432
putchar(' ');
433
putchar('\b');
434
pos--;
435
}
436
else if (ch == '\r' || ch == '\n') {
437
putchar('\r');
438
putchar('\n');
439
done = 1;
440
}
441
else if (ch >= 32 && ch < 127 && pos < len-1) {
442
buf[pos++] = ch;
443
putchar(ch);
444
}
445
else {
446
/* alert (bell) character */
447
putchar('\a');
448
}
449
}
450
buf[pos] = 0;
451
}
452
453
/*
454
* interactive
455
* runs the interactive shell. basically, just infinitely loops, grabbing
456
* commands and running them (and printing the exit status if it's not
457
* success.)
458
*/
459
static
460
void
461
interactive(void)
462
{
463
char buf[CMDLINE_MAX];
464
int status;
465
466
while (1) {
467
printf("OS/161$ ");
468
getcmd(buf, sizeof(buf));
469
status = docommand(buf);
470
if (status) {
471
printstatus(status);
472
printf("\n");
473
}
474
#ifdef WNOHANG
475
waitpoll();
476
#endif
477
}
478
}
479
480
static
481
void
482
check_timing(void)
483
{
484
time_t secs;
485
unsigned long nsecs;
486
if (__time(&secs, &nsecs) != -1) {
487
timing = 1;
488
warnx("Timing enabled.");
489
}
490
}
491
492
/*
493
* main
494
* if there are no arguments, run interactively, otherwise, run a program
495
* from within the shell, but immediately exit.
496
*/
497
int
498
main(int argc, char *argv[])
499
{
500
#ifdef HOST
501
hostcompat_init(argc, argv);
502
#endif
503
check_timing();
504
505
/*
506
* Allow argc to be 0 in case we're running on a broken kernel,
507
* or one that doesn't set argv when starting the first shell.
508
*/
509
if (argc == 0 || argc == 1) {
510
interactive();
511
}
512
else if (argc == 3 && !strcmp(argv[1], "-c")) {
513
return docommand(argv[2]);
514
}
515
else {
516
errx(1, "Usage: sh [-c command]");
517
}
518
return 0;
519
}
520
521