Code

networking

client

getaddrinfo connect

<!-- include (client.c lang=c) -->

# include "die.h"
# include <sys/types.h>
# include <sys/socket.h>
# include <netdb.h>
# include <stdio.h>
# include <unistd.h>

int main() {
    char *url = "www.fau.de";
    char *port = "80";
    struct addrinfo hints =
        {
        .ai_socktype = SOCK_STREAM,   // Nur TCP-Sockets
        .ai_family   = AF_UNSPEC,     // Beliebige Adressfamilie
        .ai_flags    = AI_ADDRCONFIG, // Nur Adresstypen für die auch ein lokales Interface existiert
        }; // C: alle anderen Elemente der Struktur werden implizit genullt
    
    struct addrinfo *head;
    int res = getaddrinfo(url, port, &hints, &head);
    if(res) die_m(gai_strerror(res));
    
    // Liste der Adressen durchtesten
    int sock;
    struct addrinfo *curr;
    for (curr = head; curr != NULL; curr = curr->ai_next) {
        sock = socket(curr->ai_family, curr->ai_socktype, curr->ai_protocol);
        if(sock == -1) {// Fehlerbehandlung
        continue;
        }
        if (connect(sock, curr->ai_addr, curr->ai_addrlen) == 0) break;
        close(sock);
    }
    freeaddrinfo(head);
    if (curr == NULL) die_m("Could not connect to server");

    FILE *rx, *tx;
    if( (rx = fdopen(sock, "r")) == NULL) die("fdopen");
    int sock_copy = dup(sock);
    if(sock_copy < 0) die("dup");
    if( (tx = fdopen(sock_copy, "w")) == NULL ) die("fdopen");

    //test it
    fprintf(tx, "GET http://www.fau.de/index.html HTTP/1.1\r\n\r\n");
    fflush(tx);
    char *lineptr = NULL;
    size_t lineSize = 0;
    ssize_t strLen = 0;
    strLen = getline(&lineptr, &lineSize, rx);
    if(strLen < 0) {
        if(ferror(rx)) die("getline");
    }
    printf("%s\n", lineptr);
    free(lineptr);
    
    if(fclose(rx)) perror("fclose");
    if(fclose(tx)) perror("fclose");
}

<!-- /include -->

server

listen accept

<!-- include (accept.c lang=c) -->

# include "die.h"
# include <netdb.h>
# include <sys/socket.h>
# include <unistd.h>
# include <signal.h>
# include <stdio.h>
# include <stdlib.h>

static void handleConnection(int clientSock, int listenSock);
static void handleRequest(FILE *rx, FILE *tx);

// Initialisiert den Server und nimmt Anfragen an
static void initServer(short unsigned int port) {
    //ignore SIGPIPE
    struct sigaction sa;
    sa.sa_handler = SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;

    if( sigaction(SIGPIPE, &sa, NULL) != 0 ) die("Could not set sigaction");

    //init accept
    int listenSock = socket(AF_INET6, SOCK_STREAM, 0);
    if ( listenSock == -1 ) die("Fehler beim Aufbau des Servers");
    struct sockaddr_in6 addr = {
        .sin6_family = AF_INET6,
        .sin6_port   = htons(port),
        .sin6_addr   = in6addr_any,
    };
    if ( bind(listenSock, (struct sockaddr *) &addr, sizeof(addr)) != 0 ) die("Fehler beim Aufbau des Servers");
    if ( listen(listenSock, SOMAXCONN) != 0 ) die("Fehler beim Aufbau des Servers");

    while ( 1 ) {
        int clientSock = accept(listenSock, NULL, NULL);
        if ( clientSock == -1 ) { perror("Warnung: Client konnte nicht akzeptiert werden."); continue; }
        handleConnection(clientSock, listenSock);
    }
}



static void handleConnection(int clientSock, int listenSock) {
    //if pthread just bbput

    //fork()
    pid_t pid;
    if( (pid = fork()) == -1 ) die("fork");
    if(pid != 0) {//parent process
        close(clientSock);//only in parent process
        return;
    }
    if( close(listenSock) ) perror("close listenSock");

    //open file streams
    FILE *rx, *tx;
    int clientSock_Copy = dup(clientSock);
    if(clientSock_Copy < 0) die("dup");

    if( (rx = fdopen(clientSock, "r")) == NULL) die("fdopen");
    if( (tx = fdopen(clientSock_Copy, "w")) == NULL) die("fdopen");

    //handleRequest()
    handleRequest(rx, tx);

    //close connection
    if( fclose(tx) ) die("fclose tx");
    if( fclose(rx) ) die("fclose rx");
    
    //exit
    exit(EXIT_SUCCESS);//child exits
}

static void handleRequest(FILE *rx, FILE *tx) {
    rx = (FILE *) rx;
    fprintf(tx, "hi\n");
}

int main() {
    initServer(2020);
}

<!-- /include -->

parallel processing

fork exec

-> fork siehe server

<!-- include (exec.c lang=c) -->

# include "die.h"
# include <unistd.h>

int main() {
    //fork

    //v gets array of args / l gets strings
    //p searches path
    //execlp
    execl("./threadpool", "./threadpool", "arg1");
    die("execl failed");
    
    //alternativ
    char *argv[3];
    argv[0] = "perl";
    argv[1] = "test.pl";
    argv[2] = (char*)0;
    execvp("perl", argv);
    die("execvp failed");
}

<!-- /include -->

signal

<!-- include (signal.c lang=c) -->

# include "die.h"
# include <signal.h>
# include <sys/types.h>
# include <sys/wait.h>
# include <unistd.h>

static void sigchldHandler(int sig);
static int pcount = 0;

int main() {
    struct sigaction handleSIGCHLD =
        {
        .sa_handler = sigchldHandler,
        .sa_flags   = SA_RESTART,
        };
    if( sigaction(SIGCHLD, &handleSIGCHLD, NULL) != 0 ) die("Could not set sigaction");

    //mask sigchld
    sigset_t newMask;
    sigset_t oldMask;
    sigemptyset(&newMask);
    sigaddset(&newMask, SIGCHLD);
    if (sigprocmask(SIG_BLOCK, &newMask, &oldMask) == -1) die("sigprocmask");

    //do critical stuff
    pid_t pid = fork();
    if(pid < 0) die("fork");
    if(pid == 0) {
        sleep(5);
        exit(EXIT_SUCCESS);
    }
    pcount++;

    //alte signal mask wiederherstellen
    if (sigprocmask(SIG_SETMASK, &oldMask, NULL) == -1) die("sigprocmask");


    //passives warten
    if (sigprocmask(SIG_BLOCK, &newMask, &oldMask) == -1) die("sigprocmask");
    while (pcount > 0) {
        sigsuspend(&oldMask);
    }
}

static void sigchldHandler(int sig) {
    // Collect zombie processes
    (void)sig; //unused param
    pid_t pid;
    int event;
    while ((pid = waitpid(-1, &event, WNOHANG)) > 0) {
        // collect all zombies (prevent zombies if signal got lost because of masking)
        pcount--;
    }
}

<!-- /include -->

pthread

<!-- include (threadpool.c lang=c) -->

# include "die.h"
# include <errno.h>
# include <pthread.h>
# include "jbuffer.h"

# define POISON 123456
# define BUFSIZE 32
# define THREADS 8

static BNDBUF *jbuf;

static void *workerMain(void *param){
    for(int val = bbGet(jbuf); val != POISON; val = bbGet(jbuf)) {
        //handle val
        printf("%d\n", val);
    }
    return param;
}

int main() {
    // init jbuf
    jbuf = bbCreate(BUFSIZE);
    if (jbuf == NULL) die_m("failed to create jbuffer");

    // create threads
    pthread_t tids[THREADS];
    for (int i = 0; i < THREADS; i++) {
        //pthread_t thread;
        if ( (errno = pthread_create(&tids[i], NULL, workerMain, NULL)) != 0 ) die("pthread_create");
        //if ( (errno = pthread_detach(thread)) != 0 ) die("pthread_detach");
    }

    //put work (z.B. clientSock) in the queue
    bbPut(jbuf, 123);

    //kill them all
    for (int i = 0; i < THREADS; i++) {
        bbPut(jbuf, POISON);
    }
    for (int i = 0; i < THREADS; i++) {
        errno = pthread_join(tids[i], NULL);//void **retval
        if (errno != 0) perror("pthread_join()");
    }
}

<!-- /include -->

jbuffer

<!-- include (jbuffer.c lang=c) -->

# include <stdatomic.h>
# include "jbuffer.h"
# include "sem.h"

struct BNDBUF
{
    int *buff;
    int size;
    int insertpos; // next insert pos
    SEM *freeSpaceSem; // count of free slots
    int extractpos; // next extract pos
    SEM *usedSpaceSem; // count of used slots
};

BNDBUF *bbCreate(size_t size) {
    if(size > __INT_MAX__) return NULL;//to big

    BNDBUF *bb = calloc(1, sizeof(BNDBUF));//initialize everithing with NULL
    if(bb == NULL) return NULL;//malloc failed

    bb->buff = malloc(size * sizeof(int));
    if(bb->buff == NULL) {//malloc failed
        bbDestroy(bb);
        return NULL;
    }
    bb->size = (int) size;

    //init SEMs
    bb->freeSpaceSem = semCreate((int) size);
    if(bb->freeSpaceSem == NULL) {//semCreate failed
        bbDestroy(bb);
        return NULL;
    }

    bb->usedSpaceSem = semCreate(0);
    if(bb->usedSpaceSem == NULL) {//semCreate failed
        bbDestroy(bb);
        return NULL;
    }

    bb->insertpos = 0;
    bb->extractpos = 0;
    return bb;
}

void bbDestroy(BNDBUF *bb) {
    if(bb == NULL) return;
    free(bb->buff);//each does nothing if NULL
    semDestroy(bb->freeSpaceSem);
    semDestroy(bb->usedSpaceSem);
    free(bb);
}

void bbPut(BNDBUF *bb, int value) {
    P(bb->freeSpaceSem);
    bb->buff[bb->insertpos] = value;
    bb->insertpos++;
    if(bb->insertpos >= bb->size) bb->insertpos = 0;
    V(bb->usedSpaceSem);
}

int bbGet(BNDBUF *bb) {
    int value, extractpos, newExtractpos;
    P(bb->usedSpaceSem);
    do{
        extractpos = bb->extractpos;
        value = bb->buff[extractpos];
        newExtractpos = extractpos + 1;
        if(newExtractpos >= bb->size) newExtractpos = 0;
    } while(! atomic_compare_exchange_strong(&(bb->extractpos), &extractpos, newExtractpos));
    V(bb->freeSpaceSem);
    return value;
}

<!-- /include -->

open File

<!-- include (fopen.c lang=c) -->

# include "die.h"
# include <stdio.h>

int main() {
    FILE* file = fopen("../fopen.c", "w");
    if ( file == NULL ) die("fopen");

    //do stuff

    if (fclose(file)) perror("failed to save file");//errorhandling optional
}

<!-- /include -->

listfiles

<!-- include (listFiles.c lang=c) -->

# include "die.h"
# include <dirent.h>
# include <stdio.h>
# include <stdlib.h>
# include <sys/stat.h>
# include <string.h>

# define BASEDIR "."

static int filter(const struct dirent *dir) {
    if(dir->d_name[0] == '.') {
        return 0;//filter out .files
    }
    struct stat st;
    char name[strlen(BASEDIR) + strlen(dir->d_name) + 2];
    sprintf(name, "%s/%s", BASEDIR, dir->d_name);
    if(stat(name, &st) == -1) die("stat");//lstat don't folow symlinks
    if(S_ISREG(st.st_mode)) {
        return 1;
    }
    return 0;
}

/** list files
 */
static void listDir(){
    struct dirent **namelist; //array of dirents
    int n = scandir(BASEDIR, &namelist, filter, alphasort);//filter oder NULL
    if (n == -1) {
        die("scandir");
    }

    while (n--) {
        printf("%s/%s\n", BASEDIR, namelist[n]->d_name);
        free(namelist[n]);
    }
    free(namelist);
}

int main() {
    listDir();
}

<!-- /include -->

reading / handling strings

printf formats

<!-- include (getline.c lang=c) -->

# include "die.h"
# include <stdio.h>

static void mygetline() {
    char *lineptr = NULL;
    size_t lineSize = 0;
    ssize_t strLen = 0;

    strLen = getline(&lineptr, &lineSize, stdin);
    if(strLen < 0) {//eof
        if (ferror(stdin)) die("getline");
    }
    free(lineptr);
}

int main() {
    mygetline();
}

<!-- /include -->

fgets -> sscanf

strncmp

inputredirection dup2

//TODO

optional

sem

<!-- include (sem.c lang=c) -->

# include <errno.h>
# include <pthread.h>
# include <stdlib.h>
# include "sem.h"

struct SEM
{
    pthread_mutex_t m;
    pthread_cond_t c;
    int value;
};

SEM *semCreate(int initVal) {
    SEM *sem = malloc(sizeof(SEM));
    if(sem == NULL) return NULL;// malloc failed
    int err=0;
    err = pthread_mutex_init(&sem->m, NULL);
    if(err) {
        free(sem);
        errno = err;
        return NULL;
    }
    err = pthread_cond_init(&sem->c, NULL);
    if (err) {
        pthread_mutex_destroy(&sem->m);
        free(sem);
        errno = err;
        return NULL;
    }

    sem->value = initVal;
    return sem;
}

void semDestroy(SEM *sem) {
    if(sem == NULL) return;
    
    pthread_cond_destroy(&sem->c);//ignore returned errors
    pthread_mutex_destroy(&sem->m);//ignore returned errors
    free(sem);
}

void P(SEM *sem) {//fast mutex has only EINVAL error
    pthread_mutex_lock(&sem->m);
    while(sem->value <= 0) {
        pthread_cond_wait(&sem->c, &sem->m);
    }
    sem->value--;

    pthread_mutex_unlock(&sem->m);
}

void V(SEM *sem) {//fast mutex has only EINVAL error
    pthread_mutex_lock(&sem->m);
    sem->value++;
    pthread_cond_signal(&sem->c);

    pthread_mutex_unlock(&sem->m);
}

<!-- /include -->

stroulSave

<!-- include (strtoulSave.c lang=c) -->

# include <errno.h>
# include <stdlib.h>
/**
 * @brief strtoul with error checks
 * 
 * @param str String to be parsed
 * @param num return param where the unsigned long gets written to (gets only changed on success)
 * @return 0 on success, none zero on error
 */
static int strtoulSave(const char *str, unsigned long *num) {
    char *endptr;
    int eno = errno;
    errno = 0;
    unsigned long value = strtoul(str, &endptr, 10);
    if(errno) return errno;
    errno = eno; // restore errno, so we're not unintentionally loosing errors when calling this function

    if (endptr == str) return EINVAL; // no digits found
    if(*endptr != '\0') return EINVAL; // contains invalid character

    *num = value;
    return 0;
}

int main(){
    unsigned long num;
    strtoulSave("123", &num);
}

<!-- /include -->

other

RAID

deadlock

fether- / light- / heavy- weight threads/processeses