Institutionen för datavetenskap Umeå Universitet

Laboration 2, msh


Introduktion

Det som gör Unix så kraftfullt är att man kan skriva in komplicerade kommandon i kommandotolken. Allt som användare av Unix-system skriver in vid prompten tolkas av ett kommandoskal (shell) så det är ett väldigt centralt program för ett Unix-system.

Idag finns en uppsjö av olika kommandoskal att tillgå. De vanligaste är bash och tcsh. Dessa är väldigt avancerade med en hel del mer eller mindre nyttiga funktioner som tab-completition, command-history och wildcard-expansion. Det kommandoskalet gör är att det skriver ut en prompt, varpå det läser in ett kommando från användaren och sedan kör det angivna programmet på så sätt att stdin och stdout är ihopkopplade på ett korrekt sätt.

Syftet med den här laborationen är att ni ska lära er om pipor, omdirigering av standard-input och -output, samt att få förståelse för hur ett shell arbetar.

Laborationen ska lösas enskilt.

Din uppgift

Ni ska implementera ett eget shell med minimal funktion. Det ska klara av exekvering av program samt dirigering av stdin och stdout för dessa program. Programet ska startas med följande kommando:

./msh [script-files...]

Till er hjälp får ni en funktion som parsar en kommandorad och lägger informationen i ett mer lättillgängligt format. Denna parser finns i filerna parser.h och parser.c.

När programmet startats ska det läsa in en rad från användaren (eller från en skript-fil, men mer om det senare). Sedan ska programmet expandera miljövariabelnamn så man får en ny kommandorad, parsa denna rad med hjälp av parsern som ni får givet. Därefter ska kommandona exekveras på så sätt att deras stdin och stdout är ihopkopplade såsom angivits. Dessutom ska en infil kunna skickas på stdin till det första programmet och stdout från det sista programmet ska kunna skickas till en fil. När shellet startat upp alla kommandon ska det ligga och vänta på att de avslutas.

En bar (|) används för att koppla ihop kommandon. Betrakta följande enkla exempel: cat < infil | sort > utfil I det här fallet ska "infil" skickas på stdin till cat, och stdout från cat ska skickas på stdin till sort och stdout från sort ska skickas till filen "utfil". Programmet ska fortsätta läsa rader och bearbeta dessa ända tills att EOF påträffas (användaren tryckte ctrl-D). Förutom denna grundläggande funktion att starta upp kommandon och koppla pipor mellan dom så ska erat shell klara av några ytterligare saker.

Interna kommandon

Ni ska implementera tre stycken interna kommandon. Ni kan anta att varje kommandorad som läses in antingen är ett enda internt kommando, eller så är det en vanlig kommandorad som ska behandlas som vanligt.
  • cd [dir] Byter current working directory till "dir". Om ingen katalog anges brukar de flesta shell gå till $(home), men det är ospecifierat i den här laborationen.
  • set [var=value] Sätter värdet på miljövariabeln "var" till värdet "value".
  • exit Avslutar shellet. Bekvämlighetskommando som man kan använda för att avsluta shellet istället för ctrl-D.
Dessa kommandon kan inte implementeras som externa program. Tänk gärna lite på varför det är så och nämn resultatet i rapporten.

Signalhantering

Erat shell ska fånga signalen SIGINT. När denna signal fångas ska shellet avsluta samtliga processer som det har startat, genom att skicka SIGINT. Därefter ska shellet fortsätta med att läsa in rader som vanligt. Det lättaste sättet att skicka SIGINT till shellet är att trycka ctrl-C.

Shellskript

Programmet ska klara av att köra shellskript. Operativsystemet sköter som tur är om det mesta. Det enda man behöver göra är att kolla om något kommandoradsarguments givits till shellet och isåfall läsa kommandorader från dessa filer (istället för från stdin). Dessutom måste shellet klara av kommentarer. Det vill säga rader som börjar med ett "#" ska ignoreras. Tomma rader ska också ignoreras. För att skriva ett msh-skript, gör en fil som börjar med en rad på följande form "#! [sökväg till msh]". På efterföljande rader kan kommandorader som msh klarar skrivas in. När shellskript körs ska ingen prompt skrivas ut.

rc-fil

När shellet startas ska det utföra de kommandon som finns i filen .mshrc i hemkatalogen. Existerar inte denna fil så ska shellet bara gå vidare utan felutskrifter. Denna fil kan lämpligen användas för att sätta prompten till ett lämpligt värde.

Struktur

För att strukturera upp programmet krävs det att ni implementerar vissa funktioner. Dessa finns deklarerade i execute.h. Funktionernas uppförande beskrivs mer ingående nedan.
  • int prompt(); Den här funktionen ska helt enkelt bara skriva ut prompten på stdout. Det den ska skriva ut är värdet av miljö-variabeln prompt. Är denna inte satt ska prompten "msh%" användas.
  • int expand(const char*rawline, char *eline); Syftet med den här funktionen är att expandera vissa specialtecken som man kan skicka till shellet. Det som krävs är att den expanderar miljövariabler. Dessa markeras med ett $ sedan följer namnet på miljö-variablen direkt omslutet av parenteser. Exempel: $(user) Funktionen tar strängen rawline och expanderar den och lägger resultatet i eline. Tänk på att eline har begränsad storlek.
  • int dupPipe(int pip[2], int end, int destfd); Duplicerar en ände av en pipa till en fildeskriptor, och stänger båda pipändar. Se execute.h för mer information om argument och returvärde för den här funktionen.
  • int redirect(char *filename, int flags, int destfd); Dirigerar om standard-input eller -output till en fil. Inga filer ska skrivas över! Om filen redan finns ska en felkod returneras. Se execute.h för mer information om argument och returvärde för den här funktionen.
Ni får inte ändra i de givna filerna ( execute.h, parser.h och parser.c).

Ni bör göra en funktion som kontrollerar output från parsern, så att inte programmet hänger sig om man matar in något otillåtet som parsern inte upptäcker. (tips: skriv in yes | cat < fil | less).

Kompilering

Programet ska gå att kompilera på Solaris. Ni måste använda en Makefile.

Filer

Alla filer som behövs för att kompilera och köra er lösning ska finnas i ~/edu/5DV004/lab2/ och vara läsbara. Programmet ska vara skrivet i ansi-c. Källkoden ska vara välkommenterad och lätt att förstå. Tänk på att det kan vara lämpligt att dela upp källkoden i flera filer för att göra den mer överskådlig. Filerna ska döpas till: msh.c, msh.h, execute.c, execute.h, parser.c, parser.h, sighant.c, sighant.h samt Makefile.

Rapport

Ni ska skriva en välskriven laborationsrapport som beskriver er lösning. Se följande länk för några riktlinjer för rapporten. Ha även med ett funktionsanropsdiagram i systembeskrivningen. http://www.cs.umu.se/information/rapportguide.html

Inlämningsdatum

Senast kl. 13.00 måndagen den 10 maj 2010 ska rapporten vara inlämnad i kursens labblåda på plan 4 i MIT-huset.

Tips och trix

  • Gör gärna en funktion som heter shell som tar en fildeskriptor som argument från vilket programmet läser in kommandoraden. Denna fildeskriptor är förstås stdin om msh anropas utan argument. Men om argument ges ska innehållet i filerna som angets köras istället. Dessutom blir det betydligt enklare att läsa in rc-filen om man har en shellfunktion.
  • I prompten finns ingen radbrytning på slutet, så ni måste flusha stdout för att vara säkra på att prompten skrivs ut när ni vill.
  • Försök inte att lägga in msh som ett så kallat login-shell. Det kommer inte att fungera och är inte så uppskattat av systemadministratörerna.
  • Undvik att köra shellet på peppar och salt innan ni har fått piporna att fungera ordentligt. En bra grundregel är att bara köra msh på den dator man sitter, samt de dedikerade labbdatorerna itchy och scratchy. Om man har tid över finns det massvis med saker man kan göra för att få ett trevligare shell.
  • Det finns många andra trevliga saker man kan expandera i funktionen expand, såsom tilde (~) och asterisk (*).
  • Istället för att bara skriva ut prompten kan man tolka tecknen och skriva ut annan information. Som i tcsh så kan man sätta prompten till "%m:%~#" för att skriva ut en fin prompt som innehåller namnet på den dator man kör på samt aktuell katalog.

Enkelt testprogram

Ett enkelt testprogram hittas här: testmish. Ladda hem det, sätt rättigheterna så att det är körbart och starta. Testprogramet testar bara en del av de saker som laborationen kräver.

Exempel

Interaktiv testkörning.
klyka:~/edu/5DV004/lab2> ./msh
msh% cat < fil
Vilen fin fil
C
B
A
msh% cat fil | sort > fil2
msh% cat fil2
A
B
C
Vilen fin fil
msh% cat < fil2
A
B
C
Vilen fin fil
msh% cd ..
msh% ls
lab1 lab2 lab3 lab4 lab5
msh% cd lab4
msh% echo Nu kör $(user) msh...
Nu kör samme msh...
msh% set ENVTEST=Hejhej
msh% echo $(ENVTEST)
Hejhej
msh% yes | cat | cat
y
y
...
y
^Cmsh% exit
Såhär kan det se ut när man kör ett shell-skript.
klyka:~/edu/5DV004/lab2> cat script.msh
#!./msh
pwd
ypcat passwd |grep samme

klyka:~/edu/5DV004/lab2> ls -l script.msh -rwxr--r-- 1 samme tdb 38 Nov 9 16:46 script.msh
klyka:~/edu/5DV004/lab2> ./script.msh /Home/staff/samme/edu/5DV004/lab2 samme:x:16033:1101:Samuel Karlsson:/Home/staff/samme:/usr/local/bin/tcsh klyka:~/edu/5DV004/lab2>

Vanliga frågor

Här är en samling vanliga frågor och svar.

http://www.cs.umu.se/kurser/5DV004/VT10/labbar/lab2/index.html
Ansvarig för sidan: Mikael Rännar
Senast ändrad 2010-04-15