Du befindest dich hier: FSI Informatik » Prüfungsfragen und Altklausuren » Prüfungen im Bachelor-Studium (1. - 5. Semester) » Aufgabe 2: Timebox   (Übersicht)

Restliche Lösungen siehe StuvePad.

Aufgabe 2: Timebox

#include <arpa/inet.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
 
#include "sem.h"
 
#define BUFFERSIZE 16
#define MAX_LINE_LEN 1024
 
static const char BASEDIR[] = "timebox-dir";
static const char CONFIG[] = "config";
static const unsigned int DEFAULT_THREADS = 2;
static const int LISTEN_PORT = 2016;
static const int DEAD_PILL = (int) 0xdeaddead;
 
static void die(const char msg[]) {
    fprintf(stderr, "Error: %s\n", msg);
    exit(EXIT_FAILURE);
}
 
// Funktionsdeklarationen, globale Variablen usw.
const char *cmd_fail_str = "Error: unknown cmd\n";
static int bb[BUFFERSIZE];
static SEM *bb_sem_data, *bb_sem_free, *bb_out_mutex;
static volatile int bb_in_idx, bb_out_idx;
 
static volatile int reread_config = 0;
static volatile int cur_threads;
 
static void usr_handler(int);
static void parse_config(void);
static void *thread_handler(void *);
static void handleCommand(FILE*, FILE*);
static void bbPut(int);
static int bbGet(void);
 
// Test with
// pkill timebox -SIGUSR1
// netcat localhost 2016
 
int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) {
    bb_sem_data = semCreate(0);
    bb_sem_free = semCreate(BUFFERSIZE);
    bb_out_mutex = semCreate(1);
 
    if (!bb_sem_data || !bb_sem_free || !bb_out_mutex) {
        die("Sem creation");
    }
    if (chdir(BASEDIR) == -1) die("chdir");
 
    sigset_t emptyset;
    sigemptyset(&emptyset);
    struct sigaction sig_pipe= {
        .sa_handler = SIG_IGN,
        .sa_mask = emptyset
    };
    if (sigaction(SIGPIPE, &sig_pipe, NULL) == -1) die("sigpipe");
 
    struct sigaction sig_usr = {
        .sa_handler = usr_handler,
        .sa_mask = emptyset,
        .sa_flags = SA_RESTART
    };
    if (sigaction(SIGUSR1, &sig_usr, NULL) == -1) die("sigusr");
 
    sigset_t usrblock;
    sigemptyset(&usrblock);
    sigaddset(&usrblock, SIGUSR1);
    if (pthread_sigmask(SIG_BLOCK, &usrblock, NULL) != 0) die("sigmask");
    parse_config();
    if (pthread_sigmask(SIG_UNBLOCK, &usrblock, NULL) != 0) die("sigmask");
 
    // Socket erstellen und auf Verbindungsannahme vorbereiten
    int sock = socket(AF_INET6, SOCK_STREAM, 0);
    if (sock == -1) die("socket");
 
    // Nur zum Testen!
    const int reuseaddr_flag = 1;
	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_flag, sizeof(reuseaddr_flag)) == -1) {
		perror("Failed setting SO_REUSE_ADDR");
		exit(EXIT_FAILURE);
	}
 
    struct sockaddr_in6 name= {
        .sin6_family = AF_INET6,
        .sin6_port = htons((uint16_t) LISTEN_PORT),
        .sin6_addr = IN6ADDR_ANY_INIT
    };
 
    if (bind(sock, (const struct sockaddr *) &name, sizeof(name)) == -1) die("bind");
    if (listen(sock, SOMAXCONN) == -1) die ("listen");
 
    while (1) {
        // Verbindungen annehmen und bearbeiten
        int client = accept(sock, NULL, NULL);
        if (client == -1) {
            perror("accept failed, retrying...");
            continue;
        }
 
        if (reread_config) {
            if (pthread_sigmask(SIG_BLOCK, &usrblock, NULL) != 0) die("sigmask");
            parse_config();
            reread_config = 0;
            if (pthread_sigmask(SIG_UNBLOCK, &usrblock, NULL) != 0) die("sigmask");
        }
 
        bbPut(client);
    }
}
 
static void usr_handler(int signum __attribute__((unused))) {
    reread_config = 1;
}
 
static void parse_config(void) {
    FILE *f = fopen(CONFIG, "r");
 
    int new_count = (int) DEFAULT_THREADS;
    if (f != NULL) {
        int c;
        if (fscanf(f, "%d", &c) == 1 && c >= 1) {
            new_count = c;
        }
        fclose(f);
    }
    for (int i = cur_threads; i < new_count; i++) {
        pthread_t t;
        errno = pthread_create(&t, NULL, thread_handler, NULL);
        if (errno != 0) die("thread creation");
        if (pthread_detach(t) != 0) die("detach");
    }
 
    for (int i = new_count; i < cur_threads; i++) {
        bbPut(DEAD_PILL);
    }
 
    cur_threads = new_count;
 
    // Nur zum Testen
    printf("New amount of threads: %d\n", cur_threads);
}
 
static void *thread_handler(void *a __attribute__((unused))) {
    while (1) {
        int client = bbGet();
        if (client == DEAD_PILL) break;
        int client2 = dup(client);
        if (client2 == -1) {
            perror("dup");
            close(client);
            continue;
        }
 
        FILE *rx = fdopen(client, "r");
        if (!rx) {
            perror("fdopen");
            close(client); close(client2);
            continue;
        }
 
        FILE *tx = fdopen(client2, "w");
        if (!tx) {
            perror("fdopen");
            fclose(rx); close(client2);
            continue;
        }
 
        handleCommand(rx, tx);
 
        fclose(rx);
        fclose(tx);
    }
 
    return NULL;
}
 
static void handleCommand(FILE *rx, FILE *tx) {
    char buf[MAX_LINE_LEN + 1];
    if (fgets(buf, MAX_LINE_LEN + 1, rx) == NULL) {
        fprintf(tx, cmd_fail_str);
        return;
    }
 
    char *saveptr;
    char *cmd = strtok_r(buf, " \n", &saveptr);
    if (!cmd || strcmp(cmd, "TIME") != 0) {
        fprintf(tx, cmd_fail_str);
        return;
    }
 
    char *relpath = strtok_r(NULL, " \n", &saveptr);
    if (!relpath) {
        fprintf(tx, cmd_fail_str);
        return;
    }
 
    struct stat s;
    if (lstat(relpath, &s) == -1) {
        fprintf(tx, "file not found or permission error\n");
        return;
    }
 
    fprintf(tx, "%ld\n", s.st_mtime);
}
 
static void bbPut(int v) { // only one writer
    P(bb_sem_free);
    bb[bb_in_idx] = v;
    bb_in_idx = (bb_in_idx + 1) % BUFFERSIZE;
 
    V(bb_sem_data);
}
 
static int bbGet(void) {
    P(bb_sem_data);
    int idx;
    P(bb_out_mutex);
        idx = bb_out_idx;
        int v = bb[idx];
        bb_out_idx = (bb_out_idx + 1) % BUFFERSIZE;
    V(bb_out_mutex);
 
    V(bb_sem_free);
    return v;
}