OS: Finish shell assignment.

parent 99356bfe
......@@ -39,21 +39,18 @@ void yyerror(const char *str) {
%%
Start: Commands
Start: Commands { shell_exec(shell_pop_args()); }
;
Commands: Command
| Commands EOL Command
| Commands '|' { shell_pipe(); } Command
| Commands EOL { shell_exec(shell_pop_args()); } Command
| Commands '|' { shell_pipe(); shell_exec(shell_pop_args()); } Command
;
Command: /* Empty */
| Builtin
| IDENTIFIER { shell_push_args($1); }
{ shell_exec(shell_pop_args()); }
| IDENTIFIER { shell_push_args($1); }
Arguments { shell_exec(shell_pop_args()); }
;
| IDENTIFIER { shell_push_args($1); } Arguments;
Builtin: EXIT { shell_exit(); }
| HELP { shell_help(); }
......
......@@ -137,16 +137,27 @@ void shell_append_args(const char *value) {
}
shell_args *shell_pop_args() {
if( !last_arg )
return NULL;
assert(cmd_args && last_arg != NULL);
shell_args *args = cmd_args;
cmd_args = last_arg = NULL;
return args;
}
int command_has_pipe = 0;
void shell_pipe() {
shell_debug("start pipe");
command_has_pipe = 1;
}
/*
* Execute the binary and its parsed arguments.
*/
void shell_exec(shell_args *args) {
if( !args )
return;
assert(args && args->arg);
shell_debug("exec `%s'", args->arg);
......@@ -157,6 +168,17 @@ void shell_exec(shell_args *args) {
cur = cur->next;
}
int pipes[2];
int has_pipe = 0;
if( command_has_pipe ) {
pipe(pipes);
has_pipe = 1;
}
// Disable pipe flag
command_has_pipe = 0;
// Fork the process and handle forking error.
pid_t pid = fork();
switch( pid ) {
......@@ -165,10 +187,26 @@ void shell_exec(shell_args *args) {
exit(EXIT_FAILURE);
case 0:
shell_debug("Child process #%d is ready.", getpid());
if( has_pipe ) {
if (dup2(pipes[1], 1) != 1)
perror("Pipe redirection output error");
close(pipes[0]);
close(pipes[1]);
}
shell_exec_process(args);
break;
default:
shell_debug("Parent launched child process #%d.", pid);
if( has_pipe ) {
if (dup2(pipes[0], 0) != 0)
perror("Pipe redirection input error");
close(pipes[0]);
close(pipes[1]);
waitpid(pid, NULL, 0);
}
shell_history_append(FN_EXEC, args);
}
}
......@@ -280,10 +318,6 @@ void shell_exec_process(shell_args *args) {
}
}
void shell_pipe() {
shell_debug("start pipe");
}
/*
* Gracefully exit the shell.
*/
......@@ -374,6 +408,9 @@ void shell_entry() {
fflush(stdout);
}
/*
* Doubly linked list containing the command history of this shell.
*/
shell_history_log *shell_history = NULL;
int shell_history_count = 0;
......@@ -429,6 +466,9 @@ void shell_history_append(shell_func type, shell_args *args) {
shell_history_add(cmd);
}
/*
* Implementation of the repeat builtin shell command.
*/
void shell_repeat(int prev) {
shell_debug("repeat %d", prev);
......@@ -441,6 +481,8 @@ void shell_repeat(int prev) {
return;
}
// TODO: execute `cmd'
shell_history_add(cmd);
}
......
\documentclass[10pt,a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage{enumerate}
\usepackage{listings}
\usepackage{url}
\usepackage{float}
\usepackage[toc,page]{appendix}
\usepackage[dutch]{babel}
\usepackage{listings}
\title{Operating systems opdracht extra: Shell}
\author{Sander van Veen \& Tadde\"us Kroes \\
6167969 \& 6054129 \\
\url{sandervv@gmail.com} \& \url{taddeuskroes@hotmail.com} }
\begin{document}
\maketitle
\tableofcontents
\pagebreak
\section{Parser van de command shell}
Voor het bouwen van deze command shell hebben we Flex en Bison gebruikt. Dit
zijn tools voor het maken van lexer resp. een parser. Bison gebruikt een
\texttt{.y}-bestand waar context vrije grammatica (in BNF-vorm) in staat en dat
maakt het mogelijk om de parser van de main code te scheiden. Het gebruik van
deze tools neemt een deel van het schrijven van de parser uit handen. Het is
minder foutgevoeling dan de parser volledig in C schrijven, mede doordat een
Bison-bestand overzichtelijker en gemakkelijker te onderhouden is. Flex
gebruikt het bestand \texttt{input.l} om een lexer te bouwen. Als de
\texttt{main()} van de shell wordt uitgevoerd, dan roepen we \texttt{yyparse()}
aan. Dat start de parser van de shell input (en in feite dus ook de lexer).
\section{Werkende deel}
Door de projectweek is het niet meer gelukt om de complete shell af te krijgen.
Op dit moment werkt het parsen van de invoer, het veranderen van working
directory (\texttt{cd PATH}) en het sluiten van een shell (\texttt{exit}).
Daarnaast kunnen commando's worden uitgevoerd (\texttt{pwd}), maar er is een
fout in de signal handler, waardoor de shell termineert na het uitvoeren van het
commando. Er wordt een SIGCHLD naar het ``parent process'' van het geforkte
process gestuurd en dan termineert de shell doordat de lexer niet meer van stdin
kan lezen.
De parser zet de commando's om in een singly linked list van strings. Dus het
commando \texttt{cat input.l} wordt opgesplitst in twee strings en opgeslagen
als een list. Deze list wordt na het uitvoeren van het commmando aan
\texttt{shell\_history} toegevoegd. Dit maakt het mogelijk om via \texttt{!!}
en \texttt{!N} (met $N$ een geheel getal, groter dan nul) een commando te
herhalen. Met de huidige parser en lexer is het ook erg gemakkelijk om een
\texttt{.} of \texttt{source} builtin te implementeren. Flex werkt kan meerdere
bestanden openen en elke keer dat een bestand wordt geopend de stack met input
bestanden eentje ophogen. Door deze input stack is recursie mogelijk. Een
voorbeeld van een \texttt{input.l} bestand met \texttt{source} zou dan zijn:
\begin{verbatim}
/* "incl" state is used for picking up the name of an include file */
%x incl
%{
#define MAX_INCLUDE_DEPTH 10
YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
int include_stack_ptr = 0;
%}
%%
\.|source BEGIN(incl);
[a-z]+ ECHO;
[^a-z\n]*\n? ECHO;
<incl>[ \t]* /* eat the whitespace */
<incl>[^ \t\n]+ { /* got the include file name */
if( include_stack_ptr >= MAX_INCLUDE_DEPTH ) {
fprintf( stderr, "Includes nested too deeply" );
exit( 1 );
}
include_stack[include_stack_ptr++] =
YY_CURRENT_BUFFER;
yyin = fopen( yytext, "r" );
if ( ! yyin )
error( ... );
yy_switch_to_buffer(
yy_create_buffer(yyin, YY_BUF_SIZE ));
BEGIN(INITIAL);
}
<<EOF>> {
if( --include_stack_ptr ) {
yy_delete_buffer(YY_CURRENT_BUFFER);
yy_switch_to_buffer(include_stack[include_stack_ptr]);
}
else
yyterminate();
}
\end{verbatim}
Het detecteren van de pipe (om aan te geven dat twee commando's aan elkaar
moeten wordne gekoppeld) werkt, maar doordat de shell termineert door de lexer,
blijft er weinig nut over van de pipe.
\section{Conclusie} % {{{
\label{sec:Conclusie}
Na 900 regels code is het niet gelukt om de gehele shell af te maken. De shell
is wel gestructureerd opgezet waardoor het gemakkelijk is om door anderen te
gebruiken als basis voor een eigen shell. Naast het bouwen van de shell hebben
we onze C vaardigheden verbeterd door een eigen printf te maken
(\texttt{shell\_debug}). Dit is een variadic function, wat wil zeggen dat het
een variable aantal argumenten gebruikt. Wij hebben het aantal arguments
afgeleid uit het aantal \texttt{\%} van het eerste argument.
% }}}
\end{document}
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