OS: executing binaries is now supported.

parent 511058ba
......@@ -70,7 +70,7 @@ cd {
return CD;
}
/* Identifiers: source */
/* Identifiers: source, cat, echo */
{id} {
column_no += yyleng;
yylval.str = strdup(yytext);
......@@ -97,7 +97,7 @@ cd {
}
/* Argument (filenames and such): hello.txt, .config */
{id}[^\t\n ]+ {
{id}[^\t\n |]+ {
column_no += yyleng;
yylval.str = strdup(yytext);
return ARGUMENT;
......
......@@ -61,6 +61,7 @@ void shell_help();
void shell_pipe();
void shell_repeat(int prev);
void shell_exec(shell_args *args);
void shell_exec_process(shell_args *args);
/*
* Doubly linked list containing all executed commands.
......
......@@ -12,6 +12,8 @@
#include <unistd.h>
#include <stdarg.h>
#include <math.h>
#include <signal.h>
#include <sys/wait.h>
#include "input.yacc.h"
#include "input.lex.h"
......@@ -133,18 +135,135 @@ shell_args *shell_pop_args() {
void shell_exec(shell_args *args) {
assert(args && args->arg);
shell_debug("exec %s", args->arg);
shell_debug("exec `%s'", args->arg);
// TODO: handle arguments.
shell_args *cur = args->next;
while(cur) {
assert(cur->arg);
shell_debug("argument: %s", cur->arg);
shell_debug("argument: `%s'", cur->arg);
cur = cur->next;
}
// Append to command history.
// Fork the process and handle forking error.
pid_t pid = fork();
switch( pid ) {
case -1:
shell_error("Error when forking");
exit(EXIT_FAILURE);
case 0:
shell_debug("Child process #%d is ready.", getpid());
shell_exec_process(args);
break;
default:
shell_debug("Parent launched child process #%d.", pid);
shell_history_append(FN_EXEC, args);
}
}
/*
* Helper function for shell_exec(), which executes the process using execv()
* and handles all occurred errors caused by execv().
*/
void shell_exec_process(shell_args *args) {
assert(args && args->arg);
char *bin = malloc((5 + strlen(args->arg) + 1) * sizeof(char));
bin = strcpy(strcpy(bin, "/bin/")+5, args->arg)-5;
// Count the arguments.
int argc = 0;
for( shell_args *arg = args; arg; arg = arg->next, argc++);
// Convert argument structure to list of strings. And add a NULL pointer as
// a marker for the end of the list. The first string should point to the
// binary being executed.
char **argv = malloc((argc+1) * sizeof(char *));
argv[0] = bin;
shell_args *cur = args + sizeof(shell_args *);
for( int a = 0; a < argc && cur; cur = args->next, a++)
argv[a] = strdup(cur->arg);
argv[argc] = NULL;
// Function execvp() searches in PATH-environment, which is more flexible
// (user can set its own preferences), but the assignment states execv()
// should be used, because only binaries in /bin should be loaded.
shell_debug("execv(%s, [...]) called.\n", bin);
execv(bin, argv);
// If the execv() call returns, an error occurred and errno contains the
// error number corresponding with the error occurred.
switch( errno ) {
case E2BIG:
shell_error("The total number of bytes in the environment (envp)"
" and argument list (argv) is too large.");
break;
case EACCES:
shell_error("Search permission is denied on a component of the"
" path prefix of filename or the name of a script "
" interpreter. The file or a script interpreter is not a"
" regular file. Execute permission is denied for the file"
" or a script or ELF interpreter. The file system is"
" mounted noexec.");
break;
case EFAULT:
shell_error("Filename points outside accessible address space.");
break;
case EINVAL:
shell_error("An ELF executable had more than one PT_INTERP segment"
" (i.e., tried to name more than one interpreter).");
break;
case EIO:
shell_error("An I/O error occurred.");
break;
case EISDIR:
shell_error("An ELF interpreter was a directory.");
break;
case ELIBBAD:
shell_error("An ELF interpreter was not in a recognized format.");
break;
case ELOOP:
shell_error("Too many symbolic links were encountered in resolving"
" filename or the name of a script or ELF interpreter.");
break;
case EMFILE:
shell_error("The process has the maximum number of files open.");
break;
case ENAMETOOLONG:
shell_error("Filename is too long.");
break;
case ENFILE:
shell_error("The system limit on the total number of open files"
" has been reached.");
break;
case ENOENT:
shell_error("The file filename or a script or ELF interpreter does"
" not exist, or a shared library needed for file or"
" interpreter cannot be found.");
break;
case ENOEXEC:
shell_error("An executable is not in a recognized format, is for"
" the wrong architecture, or has some other format error"
" that means it cannot be executed.");
break;
case ENOMEM:
shell_error("Insufficient kernel memory was available.");
break;
case ENOTDIR:
shell_error("A component of the path prefix of filename or a"
" script or ELF interpreter is not a directory.");
break;
case EPERM:
shell_error("The file system is mounted nosuid, the user is"
" not the superuser, and the file has the set-user-ID or"
" set-group-ID bit set. The process is being traced, the"
" user is not the superuser and the file has the "
" set-user-ID or set-group-ID bit set.");
break;
case ETXTBSY:
shell_error("Executable was open for writing by one or more"
" processes.");
break;
}
}
void shell_pipe() {
......@@ -165,6 +284,12 @@ void shell_exit() {
void shell_help() {
// TODO: implement help text.
puts("Builtin commands / macros:");
puts(" cd PATH - Change current working directory to PATH.");
puts(" exit - Exit this shell (also CTRL + D).");
puts(" help - This help text.");
puts(" !! - Repeat previous shell command.");
puts(" !N - Repeat Nth previous shell command.");
// Append to command history.
shell_history_append(FN_HELP, NULL);
......@@ -249,10 +374,13 @@ shell_cmd *shell_history_get(int prev) {
assert(cur);
assert(cur->cmd);
assert(cur->cmd->args);
printf("skipping [%d]: %s\n", prev, cur->cmd->args->arg);
shell_debug("skipping [%d]: %s\n", prev, cur->cmd->args->arg);
cur = cur->prev;
}
if( cur )
shell_debug("found [%d]: %s\n", prev, cur->cmd->args->arg);
return cur ? cur->cmd : NULL ;
}
......@@ -302,7 +430,56 @@ void shell_repeat(int prev) {
shell_history_add(cmd);
}
/*
* Process number of the parent shell.
*/
pid_t parent_pid;
/*
* Signal handler for SIGINT, SIGTERM and SIGCHLD.
*/
void signal_handler(int signum) {
char *sig = "TERM";
pid_t pid;
// TODO: implement signal handling
switch( signum ) {
case SIGINT:
sig = "INT";
case SIGTERM:
if( (pid = getpid()) == parent_pid )
printf("Parent process received SIG%s and will exit.\n", sig);
else
printf("Process #%d received SIG%s and will exit.\n", pid, sig);
exit(1);
case SIGCHLD:
puts("Parent process received SIGCHLD.");
pid = wait(NULL);
printf("Child with id %d exited.\n", pid);
break;
default:
puts("This handler does not understand this type of signal.");
}
}
/*
* Main function, initiates the program.
*/
int main(void) {
struct sigaction action = {.sa_handler = signal_handler};
parent_pid = getpid();
// Bind signal handlers
if( sigaction(SIGINT, &action, NULL)
|| sigaction(SIGTERM, &action, NULL)
|| sigaction(SIGCHLD, &action, NULL) )
{
perror("An error occured while binding the signal handlers.\n");
exit(1);
}
// Detect if terminal is attached.
struct stat stats;
fstat(0, &stats);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment