2 * Copyright (c) 1993 by David I. Bell
3 * Permission is granted to use, distribute, or modify this source,
4 * provided that this copyright notice remains intact.
6 * Stand-alone shell for system maintainance for Linux.
7 * This program should NOT be built using shared libraries.
9 * 1.1.1, hacked to re-allow cmd line invocation of script file
10 * Pat Adamo, padamo@unix.asb.com
23 static const char enoent_msg
[] = "Bad command or file name";
24 static const char unkerr_msg
[] = "Unknown error!";
28 extern void do_test();
40 "cat", "filename ...",
46 "chgrp", "gid filename ...",
49 "chmod", "mode filename ...",
52 "chown", "uid filename ...",
55 "cmp", "filename1 filename2",
58 "cp", "srcname ... destname",
61 "date", "date [MMDDhhmm[YYYY]]",
64 "df", "[file-system]",
70 "exec", "filename [args]",
82 "hexdump", "[-s pos] filename",
85 "hostname", "[hostname]",
88 "kill", "[-sig] pid ...",
91 "ln", "[-s] srcname ... destname",
94 "ls", "[-lidC] filename ...",
97 "mkdir", "dirname ...",
100 "mknod", "filename type major minor",
103 "more", "filename ...",
106 "mount", "[-t type] devname dirname",
107 do_mount
, 3, MAXARGS
,
109 "mv", "srcname ... destname",
115 "printenv", "[name]",
127 "rm", "filename ...",
130 "rmdir", "dirname ...",
131 do_rmdir
, 2, MAXARGS
,
133 "setenv", "name value",
139 "source", "filename",
145 "touch", "filename ...",
146 do_touch
, 2, MAXARGS
,
151 "umount", "filename",
165 static ALIAS
*aliastable
;
166 static int aliascount
;
168 static FILE *sourcefiles
[MAXSOURCE
];
169 static int sourcecount
;
171 volatile static BOOL intcrlf
= TRUE
;
174 static void catchint();
175 static void catchquit();
176 static void catchchild();
177 static void readfile();
178 static void command();
179 static void runcmd();
180 static void showprompt();
181 static BOOL
trybuiltin();
182 static BOOL
command_in_path();
183 static ALIAS
*findalias();
185 extern char ** environ
;
190 int main(argc
, argv
, env
)
195 struct sigaction act
;
199 if ((argc
> 1) && !strcmp(argv
[1], "-c")) {
200 /* We are that fancy a shell */
202 for (dofile
= 2; dofile
< argc
; dofile
++) {
203 strncat(buf
, argv
[dofile
], sizeof(buf
));
204 if (dofile
+ 1 < argc
)
205 strncat(buf
, " ", sizeof(buf
));
211 if ((argc
> 1) && strcmp(argv
[1], "-t"))
214 printf("Shell invoked to run file: %s\n",argv
[1]);
217 printf("\nSash command shell (OpenADK edition)\n");
220 signal(SIGINT
, catchint
);
221 signal(SIGQUIT
, catchquit
);
223 memset(&act
, 0, sizeof(act
));
224 act
.sa_handler
= catchchild
;
225 act
.sa_flags
= SA_RESTART
;
226 sigaction(SIGCHLD
, &act
, NULL
);
228 if (getenv("PATH") == NULL
)
229 putenv("PATH=/bin:/usr/bin:/sbin:/usr/sbin");
231 readfile(dofile
? argv
[1] : NULL
);
237 * Read commands from the specified file.
238 * A null name pointer indicates to read from stdin.
249 if (sourcecount
>= MAXSOURCE
) {
250 fprintf(stderr
, "Too many source files\n");
256 fp
= fopen(name
, "r");
262 sourcefiles
[sourcecount
++] = fp
;
264 ttyflag
= isatty(fileno(fp
));
268 if (fp
== stdin
) //using terminal, so show prompt
271 if (intflag
&& !ttyflag
&& (fp
!= stdin
)) {
277 if (fgets(buf
, CMDLEN
- 1, fp
) == NULL
) {
278 if (ferror(fp
) && (errno
== EINTR
)) {
287 while ((cc
> 0) && isspace(buf
[cc
- 1]))
290 /* remove leading spaces and look for a '#' */
292 while (*ptr
== ' ') {
297 //taking commands from file - echo
298 printf("Command: %s\n",buf
);
299 } //end if (fp != stdin)
301 command(buf
, fp
== stdin
);
308 perror("Reading command line");
316 printf("Execution Finished, Exiting\n");
324 * Parse and execute one null-terminated command line string.
325 * This breaks the command line up into words, checks to see if the
326 * command is an alias, and expands wildcards.
329 command(cmd
, do_history
)
339 char last_exit_code
[10];
341 sprintf(last_exit_code
, "%d", exit_code
);
348 while (isblank(*cmd
))
353 static char *history
[HISTORY_SIZE
];
360 if (i
< 0 || i
>= HISTORY_SIZE
) {
361 printf("%s: Out of range\n", cmd
);
365 if (history
[i
] == NULL
) {
366 printf("%s: Null entry\n", cmd
);
369 strcpy(cmd
, history
[i
]);
370 } else if (*cmd
== 'h' && cmd
[1] == '\0') {
371 for (i
=0; i
<HISTORY_SIZE
; i
++) {
372 if (history
[i
] != NULL
)
373 printf("%2d: %s\n", i
+1, history
[i
]);
376 } else if (*cmd
!= '\0') {
377 if (history
[HISTORY_SIZE
-1] != NULL
)
378 free(history
[HISTORY_SIZE
-1]);
379 for (i
=HISTORY_SIZE
-1; i
>0; i
--)
380 history
[i
] = history
[i
-1];
381 history
[0] = strdup(cmd
);
384 if (c
= strchr(cmd
, '&')) {
390 /* Set the last exit code */
391 setenv("?", last_exit_code
, 1);
393 if ((cmd
= expandenvvar(cmd
)) == NULL
)
396 if ((*cmd
== '\0') || !makeargs(cmd
, &argc
, &argv
))
400 * Search for the command in the alias table.
401 * If it is found, then replace the command name with
402 * the alias, and append any other arguments to it.
404 alias
= findalias(argv
[0]);
407 strcpy(cmd
, alias
->value
);
411 strcat(cmd
, *++argv
);
414 if (!makeargs(cmd
, &argc
, &argv
))
419 * BASH-style variable setting
422 c
= index(argv
[0], '=');
425 setenv(argv
[0], c
, 1);
431 * Now look for the command in the builtin table, and execute
432 * the command if found.
434 if (!strcmp(argv
[0], "builtin")) {
437 if (!*argv
|| !trybuiltin(argc
, argv
))
438 fprintf(stderr
, "%s: %s\n", argv
[-1], enoent_msg
);
440 } else if (!command_in_path(argv
[0]) && trybuiltin(argc
, argv
))
444 * Not found, run the program along the PATH list.
446 runcmd(cmd
, bg
, argc
, argv
);
451 * return true if we find this command in our
455 command_in_path(char *cmd
)
457 struct stat stat_buf
;
459 if (strchr(cmd
, '/') == 0) {
461 static char path_copy
[PATHLEN
];
463 /* Search path for binary */
464 for (path
= getenv("PATH"); path
&& *path
; ) {
467 strcpy(path_copy
, path
);
468 if (p2
= strchr(path_copy
, ':')) {
472 if (strlen(path_copy
))
473 strcat(path_copy
, "/");
474 strcat(path_copy
, cmd
);
476 if (!stat(path_copy
, &stat_buf
) && (stat_buf
.st_mode
& 0111))
479 p2
= strchr(path
, ':');
485 } else if (!stat(cmd
, &stat_buf
) && (stat_buf
.st_mode
& 0111))
492 * Try to execute a built-in command.
493 * Returns TRUE if the command is a built in, whether or not the
494 * command succeeds. Returns FALSE if this is not a built-in command.
497 trybuiltin(argc
, argv
)
506 char *newargv
[MAXARGS
];
507 char *nametable
[MAXARGS
];
512 if (cmdptr
->name
[0] == 0)
515 } while (strcmp(argv
[0], cmdptr
->name
));
518 * Give a usage string if the number of arguments is too large
521 if ((argc
< cmdptr
->minargs
) || (argc
> cmdptr
->maxargs
)) {
522 fprintf(stderr
, "usage: %s %s\n",
523 cmdptr
->name
, cmdptr
->usage
);
530 * Now for each command argument, see if it is a wildcard, and if
531 * so, replace the argument with the list of matching filenames.
533 newargv
[0] = argv
[0];
537 while (++oac
< argc
) {
538 if (argv
[oac
][0] == '"' || argv
[oac
][0] == '\'') {
543 matches
= expandwildcards(argv
[oac
], MAXARGS
, nametable
);
548 if ((newargc
+ matches
) >= MAXARGS
) {
549 fprintf(stderr
, "Too many arguments\n");
554 newargv
[newargc
++] = argv
[oac
];
556 for (i
= 0; i
< matches
; i
++)
557 newargv
[newargc
++] = nametable
[i
];
560 (*cmdptr
->func
)(newargc
, newargv
);
566 * Execute the specified command.
569 runcmd(cmd
, bg
, argc
, argv
)
582 char *newargv
[MAXARGS
];
583 char *nametable
[MAXARGS
];
584 struct sigaction act
;
586 newargv
[0] = argv
[0];
589 * Now for each command argument, see if it is a wildcard, and if
590 * so, replace the argument with the list of matching filenames.
595 while (++oac
< argc
) {
596 if (argv
[oac
][0] == '"' || argv
[oac
][0] == '\'') {
601 matches
= expandwildcards(argv
[oac
], MAXARGS
, nametable
);
606 if ((newargc
+ matches
) >= MAXARGS
) {
607 fprintf(stderr
, "Too many arguments\n");
612 newargv
[newargc
++] = argv
[oac
];
614 for (i
= 0; i
< matches
; i
++)
615 newargv
[newargc
++] = nametable
[i
];
618 newargv
[newargc
] = 0;
621 signal(SIGCHLD
, SIG_DFL
);
624 * Do the fork and exec ourselves.
625 * If this fails with ENOEXEC, then run the
626 * shell anyway since it might be a shell script.
628 if (!(pid
= vfork())) {
633 * We are the child, so run the program.
634 * First close any extra file descriptors we have opened.
635 * be sure not to modify any globals after the vfork !
638 for (ci
= 0; ci
< sourcecount
; ci
++)
639 if (sourcefiles
[ci
] != stdin
)
640 close(fileno(sourcefiles
[ci
]));
642 signal(SIGINT
, SIG_DFL
);
643 signal(SIGQUIT
, SIG_DFL
);
644 signal(SIGCHLD
, SIG_DFL
);
646 execvp(newargv
[0], newargv
);
649 write(2, newargv
[0], strlen(newargv
[0]));
652 write(2, enoent_msg
, sizeof(enoent_msg
) - 1);
653 else if (strerror_r(ci
, errbuf
, sizeof(errbuf
)))
654 write(2, unkerr_msg
, sizeof(unkerr_msg
) - 1);
656 write(2, errbuf
, strlen(errbuf
));
659 _exit(ci
== ENOENT
? 127 : 126);
663 memset(&act
, 0, sizeof(act
));
664 act
.sa_handler
= catchchild
;
665 act
.sa_flags
= SA_RESTART
;
666 sigaction(SIGCHLD
, &act
, NULL
);
668 perror("vfork failed");
673 printf("[%d]\n", pid
);
683 cpid
= wait4(pid
, &status
, 0, 0);
684 if ((cpid
< 0) && (errno
== EINTR
))
689 fprintf(stderr
, "sh %d: child %d died\n", getpid(), cpid
);
694 act
.sa_handler
= catchchild
;
695 memset(&act
.sa_mask
, 0, sizeof(act
.sa_mask
));
696 act
.sa_flags
= SA_RESTART
;
697 sigaction(SIGCHLD
, &act
, NULL
);
701 if (WIFEXITED(status
)) {
702 if (WEXITSTATUS(status
) == 0)
704 exit_code
= WEXITSTATUS(status
);
722 for (cmdptr
= cmdtab
; cmdptr
->name
&& cmdptr
->name
[0]; cmdptr
++)
723 printf("%-10s %s\n", cmdptr
->name
, cmdptr
->usage
);
727 * Look up an alias name, and return a pointer to it.
728 * Returns NULL if the name does not exist.
738 for (alias
= aliastable
; count
-- > 0; alias
++) {
739 if (strcmp(name
, alias
->name
) == 0)
748 do_source(argc
, argv
)
760 printf("%d\n", getpid());
768 while (--sourcecount
>= 0) {
769 if (sourcefiles
[sourcecount
] != stdin
)
770 fclose(sourcefiles
[sourcecount
]);
774 execvp(argv
[1], &argv
[1]);
781 * Display the prompt string.
789 if ((cp
= getenv("PS1")) != NULL
) {
794 getcwd(buf
, sizeof(buf
) - 1);
804 signal(SIGINT
, catchint
);
809 write(STDOUT
, "\n", 1);
816 signal(SIGQUIT
, catchquit
);
821 write(STDOUT
, "\n", 1);
831 pid
= wait4(-1, &status
, WUNTRACED
, 0);
832 if (WIFSTOPPED(status
))
833 sprintf(buf
, "sh %d: Child %d stopped\n", getpid(), pid
);
835 sprintf(buf
, "sh %d: Child %d died\n", getpid(), pid
);
838 write(STDOUT
, "\n", 1);
840 write(STDOUT
, buf
, strlen(buf
));