Aufgabe 5 crawl

Disclaimer: Dieser Thread wurde aus dem alten Forum importiert. Daher werden eventuell nicht alle Formatierungen richtig angezeigt. Der ursprüngliche Thread beginnt im zweiten Post dieses Threads.

Aufgabe 5 crawl
Wie soll man denn bei der Implementierung vom argumentParser eine Kopie von args[] angelegen, so dass am Ende der Speicher dafür auch wieder freigegeben werden kann? Denn auf den Stack kann man das nicht legen, weil die Größe vorher nicht bekannt ist bzw. vom Rückgabewert von der sysconf Funktion abhängt. Somit bleibt einem nur malloc und weil in der argumentParser.h keine freeMemory Funktion deklariert wird, kann man den Speicher nie mehr freigeben… :frowning: hab ich was übersehen?


Auf dem Stack (der Funktion) kannst du es sowieso nicht ablegen, da dieser beim Verlassen der Funktion ungültig wird. Das hat nichts mit [m]sysconf(3)[/m] zu tun.

Du musst [m]argv[/m] nicht kopieren. Es reicht wenn du dir Pointer in das Array merkst und diese dann in den anderen Funktionen nutzt. Der Aufrufer darf [m]argv[/m] nicht verändern. Damit brauchst du für die [m]argumentParser.c[/m] kein [m]malloc(3)[/m].


Ich hab irgendwie den Sinn der Funktion initArgumentParser noch nicht ganz verstanden. Ist diese nur da um zu schauen, ob ich einen gültigen Kommandonamen habe und die Optionen /Argumente gültig sind und ansonsten mir einfach irgendwelche Pointer wie schon von rudis beschrieben anlege? Oder soll hier noch etwas anderes getan werden?


Was genau du in [m]initArgumentParser()[/m] ausführst, ist dir überlassen. Allerdings müssen Fehlerfälle gleich beim init mit [m]-1[/m] quitiert werden. Du könntest dort z.B. den Beginn von Argumenten und Optionen bestimmen.

Wichtig: Der Parser soll komplett unabhängig von der crawl sein. Also darf im Parser nur das Format der Optionen geprüft werden, aber nicht ob z.B. ein [m]-maxdepth[/m] übergeben wurde.


Ich bekomm doch aber nur ein Array von Argumenten übergeben, also stehen die Optionen doch da eh nicht mit in der Eingabe :frowning: und argc is ja auch nur die Anzahl der Argumente und nicht der Optionen und der Komandoname ist auch nicht dabei…oder versteh ich das in der Angabe falsch?
Weil wenn da wirklich die Argumente und Optionen und der Kommandoname drinnen stehen, und argc einfach die Länge von argv ist, dann weiß ich ja trotzdem nicht wo die Argumente zu Ende sind und die Optionen anfangen :frowning:
Und da ich ja wenn eines der Eingabeparameter ungültiig ist -1 zurückgeben soll, heißt das ich soll hierbei auch überprüfen ob diese Argumente und Optionen wirklich zum Komandonamen existieren?

Und noch eine Frage allgemein zur crawl: wieso darf ich diese nicht auf den Servern laufen lassen, die unten auf dem Blatt angegeben sind und welchen kann ich denn dann verwenden?


Ja, das verstehst du falsch!

Du hast doch in der clash selbst eine Shell geschrieben, die einen Befehl zerlegt und [m]argv[/m] an ein neues Programm übergibt.

Folgender Befehl:

$ ls -l -a foo

Wird von d(ein)er Shell wie folgt zerlegt:

argv[0] = "ls";
argv[1] = "-l";
argv[2] = "-a";
argv[3] = "foo";
argv[4] = NULL;

Das wird dann [m]execve(2)[/m] übergeben, dass es als [m]argv[/m] (Argument der [m]main()[/m]) an das Programm übergibt.

Da gibt es keinen Unterschied zwischen Optionen und Argumenten. Das ist eine (willkürliche) Entscheidung die dein Programm vornimmt. Die meisten Programme verwenden [m]-x[/m], [m]–option=wert[/m] oder [m]-option wert[/m] als Optionen, aber auch [m]iflag=flags[/m] ist für manche Programme eine gültige Option.

Richtig. Das ist genau das, was deine [m]initArgumentParser()[/m] herausfinden muss. Sie muss also die Strings in [m]argv[/m] durchgehen und schauen wo die Argumente enden und wo die Optionen beginnen. Falls Optionen und Argumente (= Nicht-Optionen) „gemischt“ werden, dann ist das ein Fehler.

Denk mal drüber nach - wie schon gesagt muss der Parser vollständig unabhängig vom Programm sein, dass ihn verwendet.

1 Like

Wenn mein Modul auch das Durchmischen von Optionen und Argumenten erlaubt, ist es ja nicht falsch, oder? Oder soll ich das explizit verhindern?

Und darf ich in meiner Umsetzung argv kopieren und die Pointer dazu rausgeben? Dann muss eben der Aufrufer dafür sorgen, dass diese wieder an free übergeben werden.


Eigentlich schon, denn das Format der Argumente/Optionen ist in der Angabe und .h vorgegeben.

Wenn du es ohne eine weitere Funktion schaffst, dass am Ende aller Speicher freigegeben wird, klar. Aber damit kannst du schonmal nicht ganz [m]argv[/m] kopieren, da es ja keine Schnittstelle gibt um zu sagen, dass man keine Argumente mehr haben will.


Ok, danke.

Und in der argumentParserh.h steht noch folgendes:

Wobei „getProgramName“ nicht in der Schnittstelle definiert ist.
Soll ich „getProgramName“ oder „getCommand“ umbenennen?


Ich hoffe, dass ich in der Angabe nichts überlesen habe, aber ich hätte mal eine Frage bezüglich [m]opendir[/m] und [m]readdir[/m].
Wenn diese Funktionen fehlschlagen, sollen wir dann das ganze Programm beenden - oder einfach den Fehlerfall an der speziellen Stelle/Datei/Ordner ignorieren und für den Rest fortfahren?
Aktuell habe ich es so implementiert, dass für [m]errno == EACCES[/m] (keine Zugriffsrechte) der jeweilige Fall ignoriert und einfach mit dem Rest fortgesetzt wird.
Für alle anderen [m]errno[/m]-Werte gibt es einen Programmabbruch.
Ist das ok so oder soll ich noch weitere [m]errno[/m]-Werte übernehmen, die einfach ignoriert werden?

Angenommen meine derzeitige Behandlung dieser Fälle wäre korrekt …
… klappt das zwar soweit ganz gut, allerdings bin ich da beim [m]readdir[/m] noch nicht so 100%ig zufrieden.
Sofern das [m]readdir[/m] nämlich fehlschlägt und beispielsweise [m]errno[/m] auf [m]EACCES[/m] gesetzt wurde, weiß ich ja quasi, dass der aktuelle Eintrag im Verzeichnis aufgrund der Rechte für mich nicht lesbar ist.
Ich hab dazu nichts weiter per Google oder in der manpage gefunden, daher nahm ich an, dass ein weiterer Aufruf von [m]readdir[/m] einfach an der nächsten Stelle im Verzeichnis fortsetzt und ich damit quasi dann das nicht lesbare Element einfach ignoriert habe.
Allerdings verhält sich [m]readdir[/m] so, dass jeder darauffolgende Aufruf immer wieder diesen Fehler zurückliefert, als ob er bei dem nicht lesbaren Element “hängenbleibt”.
Wenn jetzt beispielsweise ein Element im Verzeichnis nicht lesbar ist und darauf noch weitere möglicherweise lesbare Elemente folgen, kann ich - sofern ich das nicht lesbare ignoriere - diese ja aber auch nicht mehr lesen.
Gibt es dafür dann eine Lösung?


Verwende die Deklaration aus der .h-Datei, also [m]getCommand()[/m].


[m]opendir(3)[/m] kann mit mehr als nur [m]EACCES[/m] fehlschlagen und auch dann sollte sich das Programm nicht beenden. Du solltest immer [m]perror()[/m] verwenden und dann ohne Abbruch weitermachen.

Einzige Ausnahmen sind [m]EMFILE[/m] und [m]ENFILE[/m] bei [m]opendir(3)[/m], da musst du laut Angabe abbrechen.

Hast du das mal ausprobiert?

[m]readdir(3)[/m] schlägt normalerweise gar nicht fehl. Wenn eine Datei nicht lesbar ist, dann hat das keinen Einfluss auf [m]readdir(3)[/m], das liest nur die Einträge aus dem Verzeichnis aus (da wär [m]opendir(3)[/m] also schon fehlgeschlagen). Ein [m]opendir(3)[/m]/[m]readdir(3)[/m] auf folgendes Verzeichnis funktioniert ohne Probleme:

touch a b c
chmod 0000 a b c

Das liegt daran, dass die Informationen zu einer Datei im Verzeichnis gespeichert werden, und nicht in der Datei selbst (wie auch). Deswegen kannst du eine solche Datei (keine Rechte) auch löschen, denn das geschieht durch das Verzeichnis.

Falls [m]readdir(3)[/m] doch fehlschlägt, so ist das endgültig und das mit [m]opendir(3)[/m] geöffnete Verzeichnis muss übersprungen werden ([m]closedir(3)[/m] nicht vergessen).


Ok. Bei [m]EMFILE[/m] und [m]ENFILE[/m] abbrechen.
Bei [m]ENOMEM[/m] denke ich mal logischerweise auch.
Die restlichen werden von mir jetzt ignoriert. :slight_smile:

@ Ausprobiert:
Ja, habe ich.
Wenn ich zuhause auf meinem Ubuntu ein „./crawl /“ draufschmeiße, bleibt er beim Verzeichnis „/proc/1626/map_files/“ am [m]readdir(3)[/m] mit „Permission denied“ hängen, was das auch immer für eine Datei/für ein Ordner ist.
Nachdem ich mir mal mit „cd /proc/1626/map_files/“ und „ls“ dieses Verzeichnis angesehen hab, musste ich feststellen, dass ls auch gescheitert ist mit „ls: reading directory .: Permission denied“.
Ich kenne mich zu wenig in der Materie aus um daraus jetzt etwas rauslesen zu können und es wundert auch, dass das [m]readdir(3)[/m] deswegen errno auf [m]EACCES[/m] setzt, obwohl das garnicht als Möglichkeit in der manpage steht. Und nein, ich habe nicht vergessen, von einem vorherigen übersprungenen Fehler die [m]errno[/m]-Variable zurück auf 0 zu setzen. :stuck_out_tongue:
Daher habe ich es aktuell so implementiert, dass mein Programm einen Fehler durch [m]readdir(3)[/m] einfach ignoriert und weiterläuft.

Aso, ich geb natürlich beim Überspringen trotzdem den Fehler auf stderr aus (z.B. mit [m]perror(3)[/m]). :slight_smile:

EDIT: Wenn ich die Zeilen meiner [m]stderr[/m] Ausgabe zähle, wo „readdir failed“ vorkommt, bin ich bei 61 Fällen für mein System ab „/“.
Und zwar 61x wegen Permissions. Und zwar 61x bei einem Verzeichnis der Form „/proc/*/map_files/“. :confused:


Die crawl sollte deswegen nicht auf den Sun-Ray-Servern gestartet werden, weil du dir auf diesen Maschinen die Performance mit einer Menge anderer Nutzer teilst und die u.U. merklich ausbremsen würdest. Auf den Client-Maschinen sind normalerweise nicht so viele Leute gleichzeitig angemeldet, so dass das verschmerzbar ist. Auf faui01*, faui07* und faui09* sollst du die crawl deswegen nicht benutzen, weil deren Root-Dateisystem nicht auf einer an der Rechner angeschlossenen Festplatte liegt, sondern über das Netzwerk kommt, was I/O deutlich langsamer macht und bei der Verwendung der crawl unnötige Netzwerklast erzeugen würde. Diese Rechner (meines Wissens aus dem Windows-CIP und dem CIP1-Nebenraum) haben glaube ich aber sowieso keine funktionierende Linux-Installation mehr und sind jetzt immer unter Windows gebootet, wordurch sich der Hinweis erübrigt.

Statt den genannten kannst du jeden beliebigen anderen Rechner verwenden, eine Liste potentieller Kandidaten bekommst du im CIP mit
[m]dig axfr uni-erlangen.de @ns1.rrze.uni-erlangen.de | grep -E ‚^faui0[^.]+.informatik.uni-erlangen.de‘ | awk ‚{print $1}‘ | sort | uniq[/m] (Vorsicht, da sind auch einige false positives dabei). Ansonsten darfst du dich gerne mal auf der CIP-Homepage umsehen, da findest du die Infos auch.


Ich habe noch eine Frage zu den Pointern.

Ich habe es jetzt auch so gelöst, dass keine Kopie von argv in argumentParser.c angelegt wird. Aber die Pointer, die ich anlege, muss ich ja doch irgendwo speichern. Hier habe ich mir ein Array angelegt, das ich dynamisch vergrößere. Aber wie soll das jemals wieder an free übergeben werden können?


Interessant. Sowas habe ich noch nicht gesehen.

Je nachdem was du mit ignorierst meinst, ist das gefährlich.

Du musst auf jeden Fall die while-Schleife verlassen sobald [m]readdir(3)[/m] [m]NULL[/m] zurückliefert! Einfach ein [m]perror(3)[/m] und mit dem nächsten Verzeichnis weitermachen. Sonst kannst du bei anderen Fehlern sehr leicht eine Endlosschleife bauen. Falls dann ein paar potentielle Einträge übersprungen werden dann macht das nichts.


Die Lösung ist einfach: Speichere dir nur eine feste Anzahl von Pointern.

Dann musst du zwar zur Laufzeit der anderen Funktionen etwas mehr tun, aber ohne „free“ geht das nicht besser.


Interessant. Sowas habe ich noch nicht gesehen.[/quote]
Ist aber durchaus erklärbar: [m]/proc[/m] ist unter Linux ein spezielles Dateisystem – die Dateien darin existieren gar nicht wirklich irgendwo auf der Festplatte, sondern repräsentieren bestimmte Datenstrukturen innerhalb des Kernels. Wenn du [m]ls /proc/1626/map_files[/m] ausführst interpretiert der Kernel den (von [m]ls[/m] ausgeführten) Aufruf von [m]readdir(3)[/m][1] „kreativ“ anders und gibt Datenstrukturen zurück, die es für deinen Userspace-Prozess so aussehen lassen, als würden dort Dateien existieren, und zwar für jede geladene Library im Adressraum des Prozesses mit der PID 1626 eine[2]. Bei dieser Implementierung hat wohl jemand im Gegenstück zu [m]readdir(3)[/m] auf Kernel-Seite [m]return -EPERM;[/m] geschrieben, was genau zu dem beschriebenen Verhalten führt. Und tatsächlich, in [2] sehen wir im angehängten Patch in der Funktion [m]static int proc_map_files_readdir(struct file *filp, void *dirent, filldir_t filldir)[/m] den Code

ret = -EPERM; if (!ptrace_may_access(task, PTRACE_MODE_READ)) goto out;
Man könnte das u.U. als Bug betrachten.

[1] Tatsächlich ist es natürlich nicht [m]readdir(3)[/m] (wie einem die Sektionsnummer 3 an der manpage verrät), sondern der System Call [m]getdents(2)[/m].
[2] vgl. [patch 2/2] fs, proc: Introduce the /proc/<pid>/map_files/ directory v6 [LWN.net]


Joar so hab ich das mit „ignorieren“ gemeint. Also das ding wieder zumachen ([m]closedir(3)[/m]) und quasi „überspringen“. :slight_smile: