#define _GNU_SOURCE
#include "channel.h"
#include "exchange.h"
#include "fd_transfer.h"
#include <fcntl.h>
#include <stdlib.h>
#include <sys/mman.h>

int main(int argc, char **argv)
{
    if (argc != 2) {
        fprintf(stderr, "usage: %s SOCKET\n", argv[0]);
        return EXIT_FAILURE;
    }
    int result = EXIT_FAILURE, listener = -1, peer = -1;
    int initialized = 0, published = 0;
    int fd = memfd_create("exchange-lab", MFD_CLOEXEC | MFD_ALLOW_SEALING);
    struct exchange *box = MAP_FAILED;
    if (fd == -1 || ftruncate(fd, (off_t)sizeof(*box)) == -1)
        goto out;
    if (fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_SEAL) == -1)
        goto out;
    box = mmap(NULL, sizeof(*box), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (box == MAP_FAILED || exchange_init(box) == -1)
        goto out;
    initialized = 1;
    listener = channel_open(argv[1], 1);
    if (listener == -1)
        goto out;
    peer = channel_accept(listener);
    if (peer == -1)
        goto out;
    published = 1;
    if (descriptor_send(peer, fd) == -1 || exchange_server(box) == -1 ||
        channel_finished(peer) == -1)
        goto out;
    puts("exchange complete");
    result = EXIT_SUCCESS;
out:
    if (result != EXIT_SUCCESS)
        perror("memfd server");
    if (initialized && (!published || result == EXIT_SUCCESS) &&
        exchange_destroy(box) == -1) {
        perror("sem_destroy");
        result = EXIT_FAILURE;
    }
    if (box != MAP_FAILED && munmap(box, sizeof(*box)) == -1) {
        perror("munmap");
        result = EXIT_FAILURE;
    }
    if (fd != -1 && close(fd) == -1) {
        perror("close");
        result = EXIT_FAILURE;
    }
    if (peer != -1 && close(peer) == -1) {
        perror("peer close");
        result = EXIT_FAILURE;
    }
    if (listener != -1) {
        if (channel_remove(listener, argv[1]) == -1) {
            perror("listener cleanup");
            result = EXIT_FAILURE;
        }
    }
    return result;
}
