/*
* runtree
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX_PATH 2048
/* Deklaration von Funktionen, die bei der Programmierung von
* main benötigt werden und die im Rahmen dieser
* Aufgabe (zusätzlich zu main) zu programmieren sind
*/
/* traversiert rekursiv das jeweilige Verzeichnis */
void traverse_dir( char* dir_name, char* command );
/* startet einen Sohn-Prozess */
void run_command( char* command, char* file_name );
/* Signalhandler für SIGCHLD */
void sigchld_handler( int sig );
/* Deklaration von Funktionen, die bei der Programmierung
* benötigt werden und die im Rahmen dieser Aufgabe bereits
* vorgegeben sind.
*/
/* Installiert den SIGCHLD-Signalhandler */
void install_signalhandler();
/* Blockiert das Signal SIGCHLD */
void block_sigchld();
/* Deblockiert das Signal SIGCHLD */
void unblock_sigchld();
/* Wartet auf einen einzelnen Sohn-Prozess */
int wait_for_child();
/*
* Globale Variablen
*/
/* maximale Anzahl der Sohn-Prozesse */
const int max_children=3;
/* aktuelle Anzahl von Sohn-Prozessen */
int number_of_children=0;
/*
* Funktion main
*/
int main( int argc, char** argv ) {
/* Überprüfen der korrekten Anzahl von Argumenten */
if( argc != 3 ) {
fprintf( stderr, "usage: %s ", argv[0] );
exit( EXIT_FAILURE );
}
/*
* Funktionsaufruf zum Installieren des Signal-Handler
*/
install_signalhandler();
/*
* Funktionsaufruf zum Traversieren der Verzeichnisse
*/
traverse_dir( argv[1], argv[2] );
/* warten auf noch laufende Sohn-Prozesse */
block_sigchld();
while( (number_of_children > 0) && (wait_for_child() > 0) ) ;
return 0;
}
/*
* Die Funktion traverse_dir öffnet das jeweilige Verzeichnis
* und iteriert anschließend über die Verzeichniseinträge.
* Ist der Verzeichniseintrag selber wieder ein Verzeichnis
* so ruft sich die Funktion rekursiv auf. Bei Verzeichnis-
* einträgen von regulären Dateien wird die Funktion
* run_command aufgerufen. */
void traverse_dir( char* dir_name, char* command ) {
struct dirent* entry;
struct stat fstatus;
DIR* dir;
char name_buf[MAX_PATH];
/* Verzeichnis öffnen */
if( (dir = opendir( dir_name )) == NULL ) {
fprintf( stderr, "Fehler beim Öffnen des Verzeichnisses %s: %s\n",
dir_name, strerror(errno) );
return;
}
/* Die Funktion soll hier beginnen über alle Verzeichnis-
* einträge zu iterieren. Verzeichniseinträge, welche mit
* einem Punkt beginnen, sollen übersprungen werden.
*/
while( (entry = readdir(dir)) != NULL ) {
if( entry->d_name[0] == '.' ) continue;
/* Aus jedem Verzeichniseintrag wird zusammen mit
* dem aktuellen Verzeichnisnamen ein kompletter
* Path-Name gebildet. Path-Namen, welche länger
* als MaX_PATH sind, werden übersprungen. */
if( (strlen(dir_name) + strlen(entry->d_name) + 2) > MAX_PATH ) continue;
strcpy( name_buf, dir_name );
if( dir_name[strlen(dir_name)-1] != '/' ) strcat( name_buf, "/" );
strcat( name_buf, entry->d_name );
/* Mit dem kompletten Path-Namen ist der Dateistatus
* abzufragen. Handelt es sich bei dem Verzeichniseintrag
* um ein Verzeichnis, so wird die Funktion traverse_dir
* rekursiv aufgerufen, bei einer regulären Datei wird
* run_command aufgerufen. Symbolische Links werden
* nicht verfolgt */
if( lstat( name_buf, &fstatus ) == -1 ) {
fprintf( stderr, "Fehler beim Holen des Dateistatus von %s: %s\n",
name_buf, strerror(errno) );
continue;
}
if( S_ISDIR(fstatus.st_mode) ) traverse_dir( name_buf, command );
else if( S_ISREG(fstatus.st_mode) ) run_command( command, name_buf );
} /* Hier endet die Iteration */
closedir(dir);
}
/*
* Die Funktion run_command erzeugt einen neuen Sohn-Prozess
* und startet in diesem das Kommando mit dem Dateinamen als
* erstem Argument.
*/
void run_command( char* command, char* file_name ) {
pid_t pid;
char* arguments[3];
/*
* Ausfüllen des Argumenten-Vektors
*/
arguments[0] = command;
arguments[1] = file_name;
arguments[2] = NULL;
/* SIGCHLD-Signal blockieren, damit Prozess-Zähler
* konsistent bleibt */
block_sigchld();
/* Ist die maximale Anzahl von Sohn-Prozessen erreicht
* dann soll an dieser Stelle auf das Ende eines Sohn-
* Prozesses gewartet werden */
if( (number_of_children >= max_children) && !(wait_for_child > 0) )
return;
/* An dieser Stelle soll nun ein neuer Sohn-Prozess
* erzeugt werden. Dieser führt das Kommando aus.
* Der Vater-Prozess erhöht anschliessend seinen
* Kind-Prozesszähler. */
if( (pid = fork()) > 0 ) number_of_children++;
else if( pid == 0 ) {
execvp( command, arguments );
fprintf( stderr, "Kommando %s ausführen fehlgeschlagen: %s\n", command, strerror(errno) );
} else perror( "Sohn-Prozess erzeugen fehlgeschlagen" );
/* Vor dem Verlassen der Funktion muss das Signal SIGCHLD
* wieder deblockiert werden */
unblock_sigchld();
}
/*
* Signalhandler für SIGCHLD installieren
*/
void install_signalhandler() {
struct sigaction action;
memset( &action, 0, sizeof(action) );
sigemptyset( &action.sa_mask );
action.sa_flags = 0;
action.sa_handler = sigchld_handler;
if( sigaction( SIGCHLD, &action, NULL ) == -1 ) {
perror( "Fehler beim Installieren des Signalhandlers" );
exit( EXIT_FAILURE );
}
}
/*
* Signalhandler für SIGCHLD.
* In diesem Signalhandler sollen alle zu diesem Zeitpunkt
* beendeten Sohn-Prozesse aufgeräumt werden.
*/
void sigchld_handler( int sig ) {
pid_t ret;
int errno_bak = errno;
while( (ret = waitpid( -1, NULL, WNOHANG )) != -1 ) {
if( ret == 0 ) break;
number_of_children--;
}
errno = errno_bak;
}
/*
* Signal SIGCHLD blockieren.
*/
void block_sigchld() {
sigset_t set;
sigemptyset( &set );
sigaddset( &set, SIGCHLD );
sigprocmask( SIG_BLOCK, &set, NULL );
}
/*
* Signal SIGCHLD deblockieren.
*/
void unblock_sigchld() {
sigset_t set;
sigemptyset( &set );
sigaddset( &set, SIGCHLD );
sigprocmask( SIG_UNBLOCK, &set, NULL );
}
/*
* Die Funktion wait_for_child wartet auf das Beenden eines
* einzelnen Sohn-Prozesses. (Diese Funktion ist nicht für
* den signalhandler geeignet!)
*/
int wait_for_child() {
pid_t pid;
while( pid = wait(NULL) ) {
if( pid < 0 ) {
if( errno == EINTR ) continue;
perror("");
return -1;
}
number_of_children--;
return pid;
}
return 0;
}