#include #include #include #include #include #include #include #include #include #include #include #define MAX_THREADS 42 /** Opaque type of a semaphore. */ typedef struct SEM SEM; /* @brief Creates a new semaphore. * @param initVal The initial value of the semaphore. * @return Handle for the created semaphore, or NULL if an error * occurred. */ SEM *semCreate(int initVal); /* @brief Destroys a semaphore and frees all associated resources. * @param sem Handle of the semaphore to destroy. If a NULL pointer is * passed, the implementation does nothing. */ void semDestroy(SEM *sem); /* @brief P-operation. * @param sem Handle of the semaphore to decrement. */ void P(SEM *sem); /* @brief V-operation. * @param sem Handle of the semaphore to increment. */ void V(SEM *sem); /* @brief Start a connection * @param target String representation of the target. * @return File descriptor for the connection or -1 on error. * If -1 is returned, errno is set appropriately. */ int start_connection(const char *target); static int die(const char *msg) { perror(msg); exit(EXIT_FAILURE); } static void usage(const char *name) { fprintf(stderr, "USAGE: %s \n", name); } // Globale Variablen und Definitionen SEM *sem; char *replace_string; int main(int argc, char *argv[]) { // Globalen Zustand vorbereiten if (argc < 3) { usage(argv[0]); exit(EXIT_FAILURE); } replace_string = argv[1]; sem = semCreate(MAX_THREADS); if (!sem) die("semCreate"); sigaction(SIGPIPE, &((struct sigaction) { .sa_handler = SIG_IGN }), NULL); // Vorlagen suchen und Fäden starten for (int i = 2; i < argc; i++) { crawl(argv[i]); } // Finale Synchronisierung und Aufräumen for (int i = 0; i < MAX_THREADS; i++) P(sem); semDestroy(sem); } static void crawl(const char *dir) { DIR *mir = opendir(dir); if (mir == NULL) { perror("opendir"); return; } // Über Verzeichniseinträge iterieren struct dirent *entry; while (1) { errno = 0; entry = readdir(mir); if (entry == NULL) { if (errno) perror("readdir"); break; } ; char abs[strlen(dir) + 1 + strlen(entry->d_name) + 1]; snprintf(abs, sizeof abs, "%s/%s", dir, entry->d_name); if (entry->d_name[0] == '.') continue; struct stat st; if (stat(abs, &st)) { perror("stat"); continue; } if (S_ISDIR(st.st_mode)) { clawl(abs); } else if (S_ISREG(st.st_mode)) { char *path = strdup(abs); if (path == NULL) { perror("stdup"); continue; } P(sem); pthread_t tid; int res = pthread_create(&tid, NULL, worker, path); if (res) { errno = res; perror("pthread_create"); V(sem); continue; } } // Verzeichniseintrag prüfen // Arbeiterfaden mit Vorlage beauftragen } // Aufräumen if (closedir(mir) != 0) { perror("closedir");;;;;;;;; } } void *worker(void *arg) { pthread_detach(pthread_self()); char *path = (char*) arg; // Anfrage absetzen char *file = strrchr(path, '/') + 1; int n = request(file, path); // Statusmeldung printf("%s: %d\n", path, n); // Ressourcenfreigabe V(sem); free(arg); } int request(const char *target, const char *template) { FILE *tx, *tmpl; tmpl = fopen(template, "r"); if (!tmpl) { perror("fopen"); return -1; } int fd = start_connection(target); if (fd < 0) { perror("start_connection"); fclose(tmpl); return -1; } tx = fdopen(fd, "w"); if (!tx) { perror("fdopen"); fclose(tmpl); return -1; } int n = send_template(tx, tmpl); fclose(tmpl); fclose(tx); return n; } int send_template(FILE *tx, FILE *template) { int c, n = 0; while ((c = fgetc(template) != EOF)) { if (c == '$') { fputs(replace_string, tx); n++; } else { fputc(c, tx); } } if (fflush(tx) != 0) { perror("fflush"); return -1 } return n; } #include /* Von dieser Funktion wurde in der Aufgabenstellung angenommen, dass * sie bereits implementiert vorliegt. Es musste daher nicht in der * Klausur implementiert werden, aber ist notwendig um die Aufgabe zu * testen. Die Implementierung dieser Funktion hat offensichtlich * nichts mit der tatsächlichen Aufgabenstellung zu tun. */ int start_connection(const char *target) { char name[strlen(target) + 1 + 6 + 1]; sprintf(name, "%s-XXXXXX", target); int fd = mkstemp(name); assert(fd >= 0); return fd; }