DCC605: Sistemas Operacionais
2013/2

TP0.A: Implementação de um shell básico

Introdução

Neste trabalho você se familiarizará com a interface de chamadas de sistema do Linux implementando algumas funcionalidades num shell simples.

Baixe o esqueleto do shell e o estude. O esqueleto do shell contém duas partes: um processador de linhas de comando e código para execução dos comandos. O processador de linhas só reconhece comandos simples como:

ls > y
cat < y | sort | uniq | wc > y1
cat y1
rm y1
ls | sort | uniq | wc
rm y

Se você não entende o que esses comandos fazem, estude o manual de um shell do linux (por exemplo, do bash) bem como o manual de cada um dos comandos acima (ls, cat, rm, sort, uniq, wc). Copie e cole esses comandos num arquivo, por exemplo, teste.sh.

Você pode compilar o esqueleto do shell rodando

$ gcc sh.c
Esse comando irá produzir um arquivo a.out que você pode rodar:
$ ./a.out
Para sair do shell simplificado aperte ctrl+d (fim de arquivo). Teste o shell executando os comandos no arquivo teste.sh:
$ ./a.out < teste.sh
Essa execução irá falhar pois você ainda não implementou várias funcionalidades do shell. É isso que você fará nesse trabalho.

Executando comandos simples

Implemente comandos simples, como:

$ ls
O processador de linhas já constrói uma estrutura execcmd para você, a única coisa que você precisa fazer é escrever o código do case ' ' (espaço) na função runcmd. Depois de escrever o código, teste execução de programas simples como
$ ls
$ cat sh.c
Se ainda não conhecê-la, dê uma olhada no manual da função exec (digite man 3 exec). Nota: não use a função system para implementar as funções do seu shell.

Redirecionamento de entrada/saída

Implemente comandos com redirecionamento de entrada e saída para que você possa rodar:

echo "6.828 is cool" > x.txt
cat < x.txt
O processador de linhas já reconhece ">" e "<" e constrói uma estrutura redircmd para você. Seu trabalho é apenas preencher o código na função runcmd para esses casos. Teste sua implementação com os comandos acima e outros comandos similares. Talvez você queira dar uma olhada no manual das funções open e close. Se você não conhece o esquema de entrada e saída padrão de programas, dê uma olhada no artigo da Wikipedia aqui.

Implemente pipes

Implemente pipes para que você consiga rodar comandos tipo

$ ls | sort | uniq | wc
O processador de linhas já reconhece '|' e constrói uma estrutura pipecmd pra você. A única coisa que você precisa fazer é completar o código para o case '|' na função runcmd. Teste sua implementação para o comando acima. Se precisar, leia a documentação das funções pipe, fork e close.

Finalmente, você agora deve conseguir rodar o seguinte comando com sucesso:

$ a.out < t.sh

Clarificações

Não esqueça de responder à pergunta na tarefa 1 presente no esqueleto do shell.

Não use a função system na sua implemencação. Use fork e exec.

Extras

Cada um dos extras abaixo valem um ponto extra:

Correção

Seu shell será utilizado para executar um script (como em $ ./a.out < teste.sh) e a saída será conferida automaticamente. Por causa disso, seu shell deve imprimir somente a saída dos programas em casos que não ocorrem erro.

Seu código será inspecionado manualmente. Para facilitar o trabalho do professor, seu código será extraído do código do shell usando um script que retira o texto entre os marcadores no texto (MART START e MARK END). Por causa disso, entregue o código em um arquivo único e não retire os marcadores de sua solução final. Verifique se todo o seu código está sendo extraído rodando o script no seu código.