/* 15May24: Eine Implementierung einer execlp(2)-artigen Funktion. * Die Absicht ist hier zu zeigen, wie execlp(2) auf execl(2) * reduziert werden kann. */ #include <unistd.h> #include <stdlib.h> #include <string.h> #include <stdio.h> /* Wir brauchen hier die Schnittstelle, damit wir beim übersetzen * immer sicher sein können, dass wir in diesem Modul uns auch an * unsere eigenen Versprechen halten. */ #include "exec1.h" /* Die Idee ist es einen Spezialfall von execlp(2) anzubieten wenn man * nur ein Argument hat, OHNE selbst execlp(2) zu benutzen. (Zur * Erinnerung: execl(2) führt ein Programm aus welches man mit einem * Pfad angeben hat. execlp(2) sucht nach dem Programm im Systempfad * (Liste von Verzeichnissen mit Programmen) nach). * * Beachte: diese Funktion ist nicht `static', denn es soll auch aus * dem anderen Modul aufrufbar sein. */ void exec1(char *cmd, char *arg) { /* Unix hat das Konzept von "Umgebungsvariablen". Diese werden * von Kind-Prozessen vererbt, und sollen dazu diesen * Systeminformation weiterzugeben. Hier interessiert uns die * Variable PATH, wo Verziechnisse aufgezählt werden, wo man * Anwendungen finden kann. */ char *PATH = getenv("PATH"); /* ~~> /usr/bin:/bin:/usr/local/bin */ if (PATH == NULL) { return; } /* Der PATH ist mit Doppelpunkten (:) getrennt. Um diese * nacheinander abzurufen, wollen wir die Bestandteile * nacheinander ausprobieren: Dazu benutzen wir die Funktion * `strtok', welche den String in seine Bestandteile Trennt. Bei * jedem Aufruf bekommen wir ein "Tücken" (Einheit). */ char *dir = strtok(PATH, ":"); while (dir != NULL) { /* Wenn wir Verzeichnis haben (und den Programm-Namen aus * `cmd'), können wir mit sprintf(3) das kombinieren zu einem Pfad:*/ char path[strlen(dir) + 1 + strlen(cmd) + 1]; sprintf(path, "%s/%s", dir, cmd); /* Diesen absoluten Pfad übergeben wir nun an execl(2), was * den Tatsächlichen Systemaufruf absetzt, und versucht das * neue Programm zu starten. */ execl(path, cmd, arg, NULL); /* Sollte der Aufruf gerade nicht geklappt haben, gab es die * Datei nicht. Daher probieren wir den nächsten Token aus, * indem wir strtok mit NULL aufrufen. Damit wird es von * dem Punkt an im String arbeiten, wo es das letzte mal * aufgehört hat, und den nächsten Token generieren. Die * Trennzeichen (übergeben als String, was als Menge von * Zeichen zu interpretieren ist) kann zwischen Aufrufen * variiert werden. */ dir = strtok(NULL, ":"); } } /* Bonus: Wir können seit C99 auch execl auf execv reduzieren, indem * man Makro-Definitionen benutzt: */ #define execl(cmd, ...) execv(cmd, (char*[]) { __VA_ARGS__ }) /* Man kann daher sagen, dass execv(2) die Grundlegendste Funktion * ist: execl(2) und execvp(2) bauen auf dieser auf, genau wie oben * gesehen wurde, wie execlp(2) auf execl(2) aufbaut: * * execlp exec1 * \ / * \ / * execl execvp * \ / * \ / * execv */