#include #include #include #include #include #include #include int shellcmd(char *); char *strsignal(int); void prstat(int); int main(void) { int status; char command[BUFSIZ]; /* * Forever... */ for (;;) { /* * Prompt for a command. */ printf("Enter a command: "); /* * Read a command. If NULL is returned, the * user typed CTRL-D, so exit. */ if (fgets(command, sizeof(command), stdin) == NULL) { putchar('\n'); exit(0); } /* * Strip off the trailing newline character * left by fgets. */ command[strlen(command)-1] = '\0'; /* * Execute the command and print the termination * status. */ status = shellcmd(command); prstat(status); putchar('\n'); } } /* * shellcmd - start a child process, and pass command to the shell. */ int shellcmd(char *command) { int status; pid_t p, pid; extern int errno; sigset_t mask, savemask; struct sigaction ignore, saveint, savequit; /* * Set up a sigaction structure to ignore signals. */ sigemptyset(&ignore.sa_mask); ignore.sa_handler = SIG_IGN; ignore.sa_flags = 0; /* * Ignore keyboard signals; save old dispositions. */ sigaction(SIGINT, &ignore, &saveint); sigaction(SIGQUIT, &ignore, &savequit); /* * Block SIGCHLD. */ sigemptyset(&mask); sigaddset(&mask, SIGCHLD); sigprocmask(SIG_BLOCK, &mask, &savemask); /* * Start a child process. */ if ((pid = fork()) < 0) status = -1; /* * This code executes in the child process. */ if (pid == 0) { /* * Restore signals to their original dispositions, * and restore the signal mask. */ sigaction(SIGINT, &saveint, (struct sigaction *) 0); sigaction(SIGQUIT, &savequit, (struct sigaction *) 0); sigprocmask(SIG_SETMASK, &savemask, (sigset_t *) 0); /* * Execute a shell with the command as argument. */ execl("/bin/sh", "sh", "-c", command, 0); _exit(127); } /* * Wait for the child process to finish. */ while (waitpid(pid, &status, 0) < 0) { /* * EINTR (interrupted system call) is okay; otherwise, * we got some error that we need to report back. */ if (errno != EINTR) { status = -1; break; } } /* * Restore signals to their original dispositions, * and restore the signal mask. */ sigaction(SIGINT, &saveint, (struct sigaction *) 0); sigaction(SIGQUIT, &savequit, (struct sigaction *) 0); sigprocmask(SIG_SETMASK, &savemask, (sigset_t *) 0); /* * Return the child process' termination status. */ return(status); } /* * prstat - decode the termination status. */ void prstat(int status) { if (WIFEXITED(status)) { printf("Process terminated normally, exit status = %d.\n", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { printf("Process terminated abnormally, signal = %d (%s)", WTERMSIG(status), strsignal(WTERMSIG(status))); if (WCOREDUMP(status)) printf(" -- core file generated.\n"); else printf(".\n"); } else if (WIFSTOPPED(status)) { printf("Process stopped, signal = %d (%s).\n", WSTOPSIG(status), strsignal(WSTOPSIG(status))); } } char * strsignal(int sig) { char *siglist[NSIG] = { "Unknown Signal", "Hangup", "Interrupt", "Quit", "Illegal Instruction", "Trace/Breakpoint Trap", "Abort", "Emulation Trap", "Arithmetic Exception", "Killed", "Bus Error", "Segmentation Fault", "Bad System Call", "Broken Pipe", "Alarm Clock", "Terminated", "User Signal 1", "User Signal 2", "Child Status Changed", "Power-Fail/Restart", "Window Size Change", "Urgent Socket Condition", "Pollable Event", "Stopped (signal)", "Stopped (user)", "Continued", "Stopped (tty input)", "Stopped (tty output)", "Virtual Timer Expired", "Profiling Timer Expired", "Cpu Limit Exceeded", "File Size Limit Exceeded" }; return(sig < NSIG ? siglist[sig] : siglist[0]); }