OS: executing binaries is now supported.

parent 511058ba
...@@ -70,7 +70,7 @@ cd { ...@@ -70,7 +70,7 @@ cd {
return CD; return CD;
} }
/* Identifiers: source */ /* Identifiers: source, cat, echo */
{id} { {id} {
column_no += yyleng; column_no += yyleng;
yylval.str = strdup(yytext); yylval.str = strdup(yytext);
...@@ -97,7 +97,7 @@ cd { ...@@ -97,7 +97,7 @@ cd {
} }
/* Argument (filenames and such): hello.txt, .config */ /* Argument (filenames and such): hello.txt, .config */
{id}[^\t\n ]+ { {id}[^\t\n |]+ {
column_no += yyleng; column_no += yyleng;
yylval.str = strdup(yytext); yylval.str = strdup(yytext);
return ARGUMENT; return ARGUMENT;
......
...@@ -61,6 +61,7 @@ void shell_help(); ...@@ -61,6 +61,7 @@ void shell_help();
void shell_pipe(); void shell_pipe();
void shell_repeat(int prev); void shell_repeat(int prev);
void shell_exec(shell_args *args); void shell_exec(shell_args *args);
void shell_exec_process(shell_args *args);
/* /*
* Doubly linked list containing all executed commands. * Doubly linked list containing all executed commands.
......
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
#include <unistd.h> #include <unistd.h>
#include <stdarg.h> #include <stdarg.h>
#include <math.h> #include <math.h>
#include <signal.h>
#include <sys/wait.h>
#include "input.yacc.h" #include "input.yacc.h"
#include "input.lex.h" #include "input.lex.h"
...@@ -133,18 +135,135 @@ shell_args *shell_pop_args() { ...@@ -133,18 +135,135 @@ shell_args *shell_pop_args() {
void shell_exec(shell_args *args) { void shell_exec(shell_args *args) {
assert(args && args->arg); assert(args && args->arg);
shell_debug("exec %s", args->arg); shell_debug("exec `%s'", args->arg);
// TODO: handle arguments. // TODO: handle arguments.
shell_args *cur = args->next; shell_args *cur = args->next;
while(cur) { while(cur) {
assert(cur->arg); shell_debug("argument: `%s'", cur->arg);
shell_debug("argument: %s", cur->arg);
cur = cur->next; cur = cur->next;
} }
// Append to command history. // Fork the process and handle forking error.
shell_history_append(FN_EXEC, args); 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() { void shell_pipe() {
...@@ -165,6 +284,12 @@ void shell_exit() { ...@@ -165,6 +284,12 @@ void shell_exit() {
void shell_help() { void shell_help() {
// TODO: implement help text. // 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. // Append to command history.
shell_history_append(FN_HELP, NULL); shell_history_append(FN_HELP, NULL);
...@@ -173,7 +298,7 @@ void shell_help() { ...@@ -173,7 +298,7 @@ void shell_help() {
/* /*
* Change the current working directory of the shell. This function handles * Change the current working directory of the shell. This function handles
* absolute and relative paths and will return 0 when the directory is * absolute and relative paths and will return 0 when the directory is
* successfully changed, or otherwise 1 in case of an error (e.g. path does not * successfully changed, or otherwise 1 in case of an error (e.g. path does not
* exist). * exist).
*/ */
void shell_change_dir(const char *path) { void shell_change_dir(const char *path) {
...@@ -249,10 +374,13 @@ shell_cmd *shell_history_get(int prev) { ...@@ -249,10 +374,13 @@ shell_cmd *shell_history_get(int prev) {
assert(cur); assert(cur);
assert(cur->cmd); assert(cur->cmd);
assert(cur->cmd->args); 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; cur = cur->prev;
} }
if( cur )
shell_debug("found [%d]: %s\n", prev, cur->cmd->args->arg);
return cur ? cur->cmd : NULL ; return cur ? cur->cmd : NULL ;
} }
...@@ -302,7 +430,56 @@ void shell_repeat(int prev) { ...@@ -302,7 +430,56 @@ void shell_repeat(int prev) {
shell_history_add(cmd); 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) { 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. // Detect if terminal is attached.
struct stat stats; struct stat stats;
fstat(0, &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