#define _GNU_SOURCE
#include "file_fixture.h"
#include <signal.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/wait.h>

int main(void)
{
    struct file_fixture file = {.fd = -1};
    int result = EXIT_FAILURE;
    volatile unsigned char *area = MAP_FAILED;
    if (file_fixture_open(&file, "./mmap-truncate-XXXXXX") == -1)
        goto out;
    area = mmap(NULL, file.length, PROT_READ | PROT_WRITE,
                MAP_PRIVATE, file.fd, 0);
    if (area == MAP_FAILED)
        goto out;
    area[0] = 'P';
    pid_t child = fork();
    if (child == -1)
        goto out;
    if (child == 0) {
        struct rlimit no_core = {0, 0};
        struct sigaction action = {.sa_handler = SIG_DFL};
        sigset_t signals;
        sigemptyset(&action.sa_mask);
        sigemptyset(&signals);
        sigaddset(&signals, SIGBUS);
        if (setrlimit(RLIMIT_CORE, &no_core) == -1 ||
            sigaction(SIGBUS, &action, NULL) == -1 ||
            sigprocmask(SIG_UNBLOCK, &signals, NULL) == -1 ||
            ftruncate(file.fd, 0) == -1)
            _exit(2);
        unsigned char byte = area[0];
        (void)byte;
        _exit(3);
    }
    int status;
    pid_t waited;
    do {
        waited = waitpid(child, &status, 0);
    } while (waited == -1 && errno == EINTR);
    if (waited == -1)
        goto out;
    if (WIFSIGNALED(status) && WTERMSIG(status) == SIGBUS) {
        puts("child received SIGBUS after truncate-to-zero");
        result = EXIT_SUCCESS;
    } else {
        errno = EPROTO;
    }
out:
    if (result != EXIT_SUCCESS)
        perror("truncate probe");
    if (area != MAP_FAILED && munmap((void *)area, file.length) == -1) {
        perror("munmap");
        result = EXIT_FAILURE;
    }
    if (file_fixture_close(&file) == -1)
        result = EXIT_FAILURE;
    return result;
}
