Blame tests/proc_helpers.at

Packit 4f15d5
# -*- Autotest -*-
Packit 4f15d5
Packit 4f15d5
AT_BANNER([/proc helpers])
Packit 4f15d5
Packit 4f15d5
## -------------------- ##
Packit 4f15d5
## get_env_variable_ext ##
Packit 4f15d5
## -------------------- ##
Packit 4f15d5
Packit 4f15d5
AT_TESTFUN([get_env_variable_ext],
Packit 4f15d5
[[
Packit 4f15d5
Packit 4f15d5
#include "testsuite.h"
Packit 4f15d5
Packit 4f15d5
void test_delim(char delim)
Packit 4f15d5
{
Packit 4f15d5
    const char *test_data[][2] = {
Packit 4f15d5
            { "EMPTY", "" },
Packit 4f15d5
            { "SATYR", "awesome" },
Packit 4f15d5
            { "STUFF", "" },
Packit 4f15d5
            { "OPENSOURCE", "brilliant" },
Packit 4f15d5
            { "LIBREPORT", "great" },
Packit 4f15d5
            { "TRICK", "" },
Packit 4f15d5
            { "ABRT", "fabulous" },
Packit 4f15d5
            { "SENTINEL", NULL}
Packit 4f15d5
        };
Packit 4f15d5
Packit 4f15d5
Packit 4f15d5
    char fdname[] = "/tmp/libreprt-testsuite.XXXXXX";
Packit 4f15d5
    int fd = mkstemp(fdname);
Packit 4f15d5
    assert(fd >= 0);
Packit 4f15d5
    printf("Temporary file: %s\n", fdname);
Packit 4f15d5
Packit 4f15d5
    int fddup = dup(fd);
Packit 4f15d5
    assert(fddup >= 0);
Packit 4f15d5
Packit 4f15d5
    FILE *f = fdopen(fddup, "w");
Packit 4f15d5
    assert(f);
Packit 4f15d5
Packit 4f15d5
    for (size_t i = 0; i < ARRAY_SIZE(test_data); ++i)
Packit 4f15d5
    {
Packit 4f15d5
        if (test_data[i][1] == NULL)
Packit 4f15d5
            continue;
Packit 4f15d5
Packit 4f15d5
        fprintf(f, "%s=%s", test_data[i][0], test_data[i][1]);
Packit 4f15d5
Packit 4f15d5
        /* Do not add delimiter after the last entry */
Packit 4f15d5
        if (i < ARRAY_SIZE(test_data) - 1)
Packit 4f15d5
            fputc(delim, f);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    fclose(f);
Packit 4f15d5
Packit 4f15d5
    for (size_t i = ARRAY_SIZE(test_data); i != 0; --i)
Packit 4f15d5
    {
Packit 4f15d5
        lseek(fd, 0, SEEK_SET);
Packit 4f15d5
        char *value = NULL;
Packit 4f15d5
        printf("Looking for '%s'\n", test_data[i-1][0]);
Packit 4f15d5
        TS_ASSERT_FUNCTION(get_env_variable_ext(fd, delim, test_data[i-1][0], &value));
Packit 4f15d5
        TS_ASSERT_STRING_EQ(value, test_data[i-1][1], "Environment value at 'i'");
Packit 4f15d5
        free(value);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    close(fd);
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
TS_MAIN
Packit 4f15d5
{
Packit 4f15d5
    test_delim('\n');
Packit 4f15d5
    test_delim('\0');
Packit 4f15d5
}
Packit 4f15d5
TS_RETURN_MAIN
Packit 4f15d5
]])
Packit 4f15d5
Packit 4f15d5
Packit 4f15d5
## ---------------- ##
Packit 4f15d5
## get_env_variable ##
Packit 4f15d5
## ---------------- ##
Packit 4f15d5
Packit 4f15d5
AT_TESTFUN([get_env_variable],
Packit 4f15d5
[[
Packit 4f15d5
Packit 4f15d5
#include "testsuite.h"
Packit 4f15d5
Packit 4f15d5
TS_MAIN
Packit 4f15d5
{
Packit 4f15d5
    char cwd[257];
Packit 4f15d5
    getcwd(cwd, sizeof(cwd));
Packit 4f15d5
Packit 4f15d5
    char *value = NULL;
Packit 4f15d5
    TS_ASSERT_FUNCTION(get_env_variable(getpid(), "PWD", &value));
Packit 4f15d5
    TS_ASSERT_STRING_EQ(value, cwd, "Test environment variable - PWD");
Packit 4f15d5
    free(value);
Packit 4f15d5
}
Packit 4f15d5
TS_RETURN_MAIN
Packit 4f15d5
]])
Packit 4f15d5
Packit 4f15d5
## ----------- ##
Packit 4f15d5
## get_cmdline ##
Packit 4f15d5
## ----------- ##
Packit 4f15d5
Packit 4f15d5
AT_TESTFUN([get_cmdline], [[
Packit 4f15d5
#include "testsuite.h"
Packit 4f15d5
#include <err.h>
Packit 4f15d5
Packit 4f15d5
void test(const char *program, const char *args[], const char *expected)
Packit 4f15d5
{
Packit 4f15d5
    int inout[2];
Packit 4f15d5
    xpipe(inout);
Packit 4f15d5
Packit 4f15d5
    pid_t pid = fork();
Packit 4f15d5
    if (pid < 0) {
Packit 4f15d5
        err(EXIT_FAILURE, "fork");
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (pid == 0) {
Packit 4f15d5
        close(STDOUT_FILENO);
Packit 4f15d5
        xdup2(inout[1], STDOUT_FILENO);
Packit 4f15d5
        close(inout[0]);
Packit 4f15d5
Packit 4f15d5
        execv(program, (char **)args);
Packit 4f15d5
        err(EXIT_FAILURE, "exec(%s)", program);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    close(inout[1]);
Packit 4f15d5
    int status = 0;
Packit 4f15d5
    if (safe_waitpid(pid, &status, 0) < 0) {
Packit 4f15d5
        err(EXIT_FAILURE, "waitpid");
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (WEXITSTATUS(status) != 0) {
Packit 4f15d5
        errx(EXIT_FAILURE, "Child not exited with 0");
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    const size_t buffer_size = strlen(expected) * 2;
Packit 4f15d5
    char cmdline[buffer_size];
Packit 4f15d5
    const ssize_t total = full_read(inout[0], cmdline, buffer_size);
Packit 4f15d5
    close(inout[0]);
Packit 4f15d5
Packit 4f15d5
    if (total < 0) {
Packit 4f15d5
        err(EXIT_FAILURE, "full_read");
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    cmdline[total] = '\0';
Packit 4f15d5
    TS_ASSERT_STRING_EQ(cmdline, expected, "/proc/[pid]/cmd");
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
TS_MAIN
Packit 4f15d5
{
Packit 4f15d5
    if (argc > 1) {
Packit 4f15d5
        char *cmdline = NULL;
Packit 4f15d5
        if (strcmp(argv[0], "get_cmdline") == 0) {
Packit 4f15d5
            cmdline = get_cmdline(getpid());
Packit 4f15d5
        }
Packit 4f15d5
        else if (strcmp(argv[0], "get_cmdline_at") == 0) {
Packit 4f15d5
            int pid_proc_fd = open("/proc/self", O_DIRECTORY);
Packit 4f15d5
            if (pid_proc_fd < 0) {
Packit 4f15d5
                err(EXIT_FAILURE, "open(/proc/self, O_DIRECTORY)");
Packit 4f15d5
            }
Packit 4f15d5
            cmdline = get_cmdline_at(pid_proc_fd);
Packit 4f15d5
            close(pid_proc_fd);
Packit 4f15d5
        }
Packit 4f15d5
        else {
Packit 4f15d5
            errx(EXIT_FAILURE, "Unsupported function type '%s'", argv[0]);
Packit 4f15d5
        }
Packit 4f15d5
Packit 4f15d5
        fprintf(stdout, "%s", cmdline);
Packit 4f15d5
        fflush(stdout);
Packit 4f15d5
        exit(EXIT_SUCCESS);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    char *binary = malloc_readlink("/proc/self/exe");
Packit 4f15d5
    const char *args[] = { NULL, "!fo\" \"o", "@blah", "b\na'r", "g\rea\t", "regular", NULL };
Packit 4f15d5
Packit 4f15d5
#define EXPECTED " '!fo\\\" \\\"o' @blah 'b\\na\\'r' 'g\\rea\\t' regular"
Packit 4f15d5
Packit 4f15d5
    args[0] = "get_cmdline";
Packit 4f15d5
    test(binary, args, "get_cmdline" EXPECTED);
Packit 4f15d5
Packit 4f15d5
    args[0] = "get_cmdline_at";
Packit 4f15d5
    test(binary, args, "get_cmdline_at" EXPECTED);
Packit 4f15d5
Packit 4f15d5
    free(binary);
Packit 4f15d5
}
Packit 4f15d5
TS_RETURN_MAIN
Packit 4f15d5
]])
Packit 4f15d5
Packit 4f15d5
## -------------- ##
Packit 4f15d5
## get_executable ##
Packit 4f15d5
## -------------- ##
Packit 4f15d5
Packit 4f15d5
AT_TESTFUN([get_executable], [[
Packit 4f15d5
#include "testsuite.h"
Packit 4f15d5
#include <sys/sendfile.h>
Packit 4f15d5
#include <err.h>
Packit 4f15d5
Packit 4f15d5
#define PRELINK_BASENAME "/tmp/libreport.testsuite.get_executable"
Packit 4f15d5
#
Packit 4f15d5
void test(const char *program, const char *expected, const char *function, const char *argv1)
Packit 4f15d5
{
Packit 4f15d5
    int inout[2];
Packit 4f15d5
    xpipe(inout);
Packit 4f15d5
Packit 4f15d5
    pid_t pid = fork();
Packit 4f15d5
    if (pid < 0) {
Packit 4f15d5
        err(EXIT_FAILURE, "fork");
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (pid == 0) {
Packit 4f15d5
        close(STDOUT_FILENO);
Packit 4f15d5
        xdup2(inout[1], STDOUT_FILENO);
Packit 4f15d5
        close(inout[0]);
Packit 4f15d5
Packit 4f15d5
        const char *args[3] = { function, argv1, NULL };
Packit 4f15d5
        execv(program, (char **)args);
Packit 4f15d5
        err(EXIT_FAILURE, "execv(%s) : %d", program, errno);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    close(inout[1]);
Packit 4f15d5
    int status = 0;
Packit 4f15d5
    if (safe_waitpid(pid, &status, 0) < 0) {
Packit 4f15d5
        err(EXIT_FAILURE, "waitpid");
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (WEXITSTATUS(status) != 0) {
Packit 4f15d5
        errx(EXIT_FAILURE, "Child not exited with 0");
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    const size_t buffer_size = strlen(expected) * 2;
Packit 4f15d5
    char executable[buffer_size];
Packit 4f15d5
    const ssize_t total = full_read(inout[0], executable, buffer_size);
Packit 4f15d5
    close(inout[0]);
Packit 4f15d5
Packit 4f15d5
    if (total < 0) {
Packit 4f15d5
        err(EXIT_FAILURE, "full_read");
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    executable[total] = '\0';
Packit 4f15d5
    TS_ASSERT_STRING_EQ(executable, expected, "/proc/[pid]/exe");
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
int copy_to_temporary(const char *source, char *dest)
Packit 4f15d5
{
Packit 4f15d5
    int dest_fd = mkstemp(dest);
Packit 4f15d5
    if (dest_fd < 0) {
Packit 4f15d5
        err(EXIT_FAILURE, "mkstemp(%s)", dest);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    int src_fd = open(source, O_RDONLY);
Packit 4f15d5
    if (src_fd < 0) {
Packit 4f15d5
        err(EXIT_FAILURE, "open(%s, O_RDONLY)", source);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    struct stat src_stat;
Packit 4f15d5
    if (fstat(src_fd, &src_stat) < 0) {
Packit 4f15d5
        err(EXIT_FAILURE, "fstat(%s)", source);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (sendfile(dest_fd, src_fd, NULL, src_stat.st_size) < 0) {
Packit 4f15d5
        err(EXIT_FAILURE, "splice(%s, %s, %zu)", source, dest, src_stat.st_size);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    close(src_fd);
Packit 4f15d5
Packit 4f15d5
    fchmod(dest_fd, src_stat.st_mode);
Packit 4f15d5
    return dest_fd;
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
TS_MAIN
Packit 4f15d5
{
Packit 4f15d5
    if (argc > 1) {
Packit 4f15d5
        if (strcmp(argv[1], "delete") == 0) {
Packit 4f15d5
            char *binary = malloc_readlink("/proc/self/exe");
Packit 4f15d5
            unlink(binary);
Packit 4f15d5
            if (access(binary, R_OK) != -1 && errno != !ENOENT) {
Packit 4f15d5
                err(EXIT_FAILURE, "failed to remove %s", binary);
Packit 4f15d5
            }
Packit 4f15d5
            free(binary);
Packit 4f15d5
        }
Packit 4f15d5
Packit 4f15d5
        char *executable = NULL;
Packit 4f15d5
        if (strcmp(argv[0], "get_executable") == 0) {
Packit 4f15d5
            executable = get_executable(getpid());
Packit 4f15d5
        }
Packit 4f15d5
        else if (strcmp(argv[0], "get_executable_at") == 0) {
Packit 4f15d5
            int pid_proc_fd = open("/proc/self", O_DIRECTORY);
Packit 4f15d5
            if (pid_proc_fd < 0) {
Packit 4f15d5
                err(EXIT_FAILURE, "open(/proc/self, O_DIRECTORY)");
Packit 4f15d5
            }
Packit 4f15d5
            executable = get_executable_at(pid_proc_fd);
Packit 4f15d5
            close(pid_proc_fd);
Packit 4f15d5
        }
Packit 4f15d5
        else {
Packit 4f15d5
            errx(EXIT_FAILURE, "Unsupported function type '%s'", argv[0]);
Packit 4f15d5
        }
Packit 4f15d5
Packit 4f15d5
        fprintf(stdout, "%s", executable);
Packit 4f15d5
        fflush(stdout);
Packit 4f15d5
        exit(EXIT_SUCCESS);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    {
Packit 4f15d5
        char *binary = malloc_readlink("/proc/self/exe");
Packit 4f15d5
Packit 4f15d5
        test(binary, binary, "get_executable", "keep");
Packit 4f15d5
        test(binary, binary, "get_executable_at", "keep");
Packit 4f15d5
Packit 4f15d5
        free(binary);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    {
Packit 4f15d5
        char binary[] = PRELINK_BASENAME ".#prelink#.XXXXXX";
Packit 4f15d5
        int binary_fd = copy_to_temporary("/proc/self/exe", binary);
Packit 4f15d5
        close(binary_fd);
Packit 4f15d5
Packit 4f15d5
        test(binary, PRELINK_BASENAME, "get_executable", "keep");
Packit 4f15d5
Packit 4f15d5
        unlink(binary);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    {
Packit 4f15d5
        char binary[] = PRELINK_BASENAME ".#prelink#.XXXXXX";
Packit 4f15d5
        int binary_fd = copy_to_temporary("/proc/self/exe", binary);
Packit 4f15d5
        close(binary_fd);
Packit 4f15d5
Packit 4f15d5
        test(binary, PRELINK_BASENAME, "get_executable_at", "keep");
Packit 4f15d5
Packit 4f15d5
        unlink(binary);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    {
Packit 4f15d5
        char binary[] = PRELINK_BASENAME ".#prelink#.XXXXXX";
Packit 4f15d5
        int binary_fd = copy_to_temporary("/proc/self/exe", binary);
Packit 4f15d5
        close(binary_fd);
Packit 4f15d5
Packit 4f15d5
        test(binary, PRELINK_BASENAME, "get_executable", "delete");
Packit 4f15d5
Packit 4f15d5
        if (unlink(binary) == 0) {
Packit 4f15d5
            errx(EXIT_FAILURE, "should be already removed %s", binary);
Packit 4f15d5
        }
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    {
Packit 4f15d5
        char binary[] = PRELINK_BASENAME ".#prelink#.XXXXXX";
Packit 4f15d5
        int binary_fd = copy_to_temporary("/proc/self/exe", binary);
Packit 4f15d5
        close(binary_fd);
Packit 4f15d5
Packit 4f15d5
        test(binary, PRELINK_BASENAME, "get_executable_at", "delete");
Packit 4f15d5
Packit 4f15d5
        if (unlink(binary) == 0) {
Packit 4f15d5
            errx(EXIT_FAILURE, "should be already removed %s", binary);
Packit 4f15d5
        }
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    {
Packit 4f15d5
        char binary[] = "/tmp/libreport.testsuite.get_executable.XXXXXX";
Packit 4f15d5
        int binary_fd = copy_to_temporary("/proc/self/exe", binary);
Packit 4f15d5
        close(binary_fd);
Packit 4f15d5
Packit 4f15d5
        test(binary, binary, "get_executable", "delete");
Packit 4f15d5
Packit 4f15d5
        if (unlink(binary) == 0) {
Packit 4f15d5
            errx(EXIT_FAILURE, "should be already removed %s", binary);
Packit 4f15d5
        }
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    {
Packit 4f15d5
        char binary[] = "/tmp/libreport.testsuite.get_executable.XXXXXX";
Packit 4f15d5
        int binary_fd = copy_to_temporary("/proc/self/exe", binary);
Packit 4f15d5
        close(binary_fd);
Packit 4f15d5
Packit 4f15d5
        test(binary, binary, "get_executable_at", "delete");
Packit 4f15d5
Packit 4f15d5
        if (unlink(binary) == 0) {
Packit 4f15d5
            errx(EXIT_FAILURE, "should be already removed %s", binary);
Packit 4f15d5
        }
Packit 4f15d5
    }
Packit 4f15d5
}
Packit 4f15d5
TS_RETURN_MAIN
Packit 4f15d5
]])
Packit 4f15d5
Packit 4f15d5
## ------- ##
Packit 4f15d5
## get_cwd ##
Packit 4f15d5
## ------- ##
Packit 4f15d5
Packit 4f15d5
AT_TESTFUN([get_cwd], [[
Packit 4f15d5
#include "testsuite.h"
Packit 4f15d5
#include <sys/sendfile.h>
Packit 4f15d5
#include <err.h>
Packit 4f15d5
Packit 4f15d5
TS_MAIN
Packit 4f15d5
{
Packit 4f15d5
    char wd[PATH_MAX];
Packit 4f15d5
    getcwd(wd, sizeof(wd));
Packit 4f15d5
Packit 4f15d5
    char *cwd = get_cwd(getpid());
Packit 4f15d5
    TS_ASSERT_STRING_EQ(cwd, wd, "get_cwd(getpid())");
Packit 4f15d5
    free(cwd);
Packit 4f15d5
Packit 4f15d5
Packit 4f15d5
    int pid_proc_fd = open("/proc/self", O_DIRECTORY | O_PATH);
Packit 4f15d5
    if (pid_proc_fd < 0) {
Packit 4f15d5
        err(EXIT_FAILURE, "open(/proc/self, O_DIRECTORY | O_PATH)");
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    char *cwd_at = get_cwd_at(pid_proc_fd);
Packit 4f15d5
    TS_ASSERT_STRING_EQ(cwd_at, wd, "get_cwd_at(open(/proc/self))");
Packit 4f15d5
    close(pid_proc_fd);
Packit 4f15d5
    free(cwd_at);
Packit 4f15d5
}
Packit 4f15d5
TS_RETURN_MAIN
Packit 4f15d5
]])
Packit 4f15d5
Packit 4f15d5
## ----------- ##
Packit 4f15d5
## get_rootdir ##
Packit 4f15d5
## ----------- ##
Packit 4f15d5
Packit 4f15d5
AT_TESTFUN([get_rootdir], [[
Packit 4f15d5
#include "testsuite.h"
Packit 4f15d5
#include <sys/sendfile.h>
Packit 4f15d5
#include <err.h>
Packit 4f15d5
Packit 4f15d5
TS_MAIN
Packit 4f15d5
{
Packit 4f15d5
    char *proc_self_root = malloc_readlink("/proc/self/root");
Packit 4f15d5
Packit 4f15d5
    char *root_dir = get_rootdir(getpid());
Packit 4f15d5
    TS_ASSERT_STRING_EQ(root_dir, proc_self_root, "get_rootdir(getpid())");
Packit 4f15d5
    free(root_dir);
Packit 4f15d5
Packit 4f15d5
Packit 4f15d5
    int pid_proc_fd = open("/proc/self", O_DIRECTORY | O_PATH);
Packit 4f15d5
    if (pid_proc_fd < 0) {
Packit 4f15d5
        err(EXIT_FAILURE, "open(/proc/self, O_DIRECTORY | O_PATH)");
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    char *root_dir_at = get_rootdir_at(pid_proc_fd);
Packit 4f15d5
    TS_ASSERT_STRING_EQ(root_dir_at, proc_self_root, "get_rootdir_at(open(/proc/self))");
Packit 4f15d5
    close(pid_proc_fd);
Packit 4f15d5
    free(root_dir_at);
Packit 4f15d5
}
Packit 4f15d5
TS_RETURN_MAIN
Packit 4f15d5
]])
Packit 4f15d5
Packit 4f15d5
## ------------ ##
Packit 4f15d5
## dump_fd_info ##
Packit 4f15d5
## ------------ ##
Packit 4f15d5
Packit 4f15d5
AT_TESTFUN([dump_fd_info], [[
Packit 4f15d5
#include "testsuite.h"
Packit 4f15d5
#include <sys/sendfile.h>
Packit 4f15d5
#include <err.h>
Packit 4f15d5
Packit 4f15d5
#define FILENAME_FORMAT "/tmp/libreport.testsuite.fdinfo.%d.%s"
Packit 4f15d5
Packit 4f15d5
pid_t prepare_process(void)
Packit 4f15d5
{
Packit 4f15d5
    int toparent[2];
Packit 4f15d5
    xpipe(toparent);
Packit 4f15d5
Packit 4f15d5
    char *binary = malloc_readlink("/proc/self/exe");
Packit 4f15d5
    pid_t pid = fork();
Packit 4f15d5
    if (pid < 0) {
Packit 4f15d5
        err(EXIT_FAILURE, "fork");
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (pid == 0) {
Packit 4f15d5
        close(STDOUT_FILENO);
Packit 4f15d5
        xdup2(toparent[1], STDOUT_FILENO);
Packit 4f15d5
Packit 4f15d5
        DIR *fddir = opendir("/proc/self/fd");
Packit 4f15d5
        struct dirent *dent;
Packit 4f15d5
        while ((dent = readdir(fddir))) {
Packit 4f15d5
            const int fd = atoi(dent->d_name);
Packit 4f15d5
            if (fd != STDOUT_FILENO) {
Packit 4f15d5
                close(fd);
Packit 4f15d5
            }
Packit 4f15d5
        }
Packit 4f15d5
Packit 4f15d5
        execl(binary, "wait", NULL);
Packit 4f15d5
        exit(EXIT_FAILURE);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    close(toparent[1]);
Packit 4f15d5
Packit 4f15d5
    free(binary);
Packit 4f15d5
Packit 4f15d5
    /* Wait for child */
Packit 4f15d5
    char buf[8];
Packit 4f15d5
    if (full_read(toparent[0], buf, 8) < 0) {
Packit 4f15d5
        fprintf(stderr, "Failed to read from child: %s\n", strerror(errno));
Packit 4f15d5
        fflush(stderr);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    close(toparent[0]);
Packit 4f15d5
Packit 4f15d5
    return pid;
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
void kill_process(pid_t pid)
Packit 4f15d5
{
Packit 4f15d5
    /* Notify child */
Packit 4f15d5
    kill(pid, SIGTERM);
Packit 4f15d5
    int status = 0;
Packit 4f15d5
    if (safe_waitpid(pid, &status, 0) < 0) {
Packit 4f15d5
        fprintf(stderr, "Couldn't wait for child\n");
Packit 4f15d5
    }
Packit 4f15d5
    else if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGTERM) {
Packit 4f15d5
        fprintf(stderr, "Child was not TERMinated - %d\n", status);
Packit 4f15d5
    }
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
void check_file_contents(const char *fdinfo_filename)
Packit 4f15d5
{
Packit 4f15d5
    struct fd {
Packit 4f15d5
        int fd;
Packit 4f15d5
        const char *file;
Packit 4f15d5
    } fds[] = {
Packit 4f15d5
        { .fd = 0, .file = "/etc/services", },
Packit 4f15d5
        { .fd = 2, .file = "/etc/passwd", },
Packit 4f15d5
        { .fd = 3, .file = "/etc/group", },
Packit 4f15d5
    };
Packit 4f15d5
Packit 4f15d5
    char *file = xmalloc_xopen_read_close(fdinfo_filename, NULL);
Packit 4f15d5
    int fdno = 0;
Packit 4f15d5
    char *cursor = file;
Packit 4f15d5
    char *line = file;
Packit 4f15d5
    char *end = file + strlen(file);
Packit 4f15d5
    while (cursor < end) {
Packit 4f15d5
        cursor = strchrnul(line, '\n');
Packit 4f15d5
        if (*cursor != '\0') {
Packit 4f15d5
            *cursor = '\0';
Packit 4f15d5
        }
Packit 4f15d5
        ++cursor;
Packit 4f15d5
Packit 4f15d5
        if (fdno < (sizeof(fds)/sizeof(fds[0]))) {
Packit 4f15d5
            int fd = 0;
Packit 4f15d5
            char *file = NULL;
Packit 4f15d5
            const int res = sscanf(line, "%d:%ms", &fd, &file;;
Packit 4f15d5
            TS_ASSERT_SIGNED_EQ(res, 2);
Packit 4f15d5
            TS_ASSERT_SIGNED_EQ(fd, fds[fdno].fd);
Packit 4f15d5
            TS_ASSERT_STRING_EQ(file, fds[fdno].file, "FD file name");
Packit 4f15d5
            free(file);
Packit 4f15d5
        }
Packit 4f15d5
Packit 4f15d5
        line = cursor;
Packit 4f15d5
        int fieldscnt = 0;
Packit 4f15d5
        while (line < end) {
Packit 4f15d5
            cursor = strchrnul(line, '\n');
Packit 4f15d5
            if (*cursor != '\0') {
Packit 4f15d5
                *cursor = '\0';
Packit 4f15d5
            }
Packit 4f15d5
            ++cursor;
Packit 4f15d5
Packit 4f15d5
            if (strcmp(line, "") == 0) {
Packit 4f15d5
                break;
Packit 4f15d5
            }
Packit 4f15d5
Packit 4f15d5
            int col = 0;
Packit 4f15d5
            for (; col < strlen(line); ++col) {
Packit 4f15d5
                if (line[col] == ':') {
Packit 4f15d5
                    break;
Packit 4f15d5
                }
Packit 4f15d5
Packit 4f15d5
                TS_ASSERT_TRUE(line[col] != ' ' && line[col] != '\t');
Packit 4f15d5
                if (!g_testsuite_last_ok) {
Packit 4f15d5
                    break;
Packit 4f15d5
                }
Packit 4f15d5
            }
Packit 4f15d5
            TS_ASSERT_SIGNED_NEQ(col, 0);
Packit 4f15d5
            TS_ASSERT_SIGNED_LT(col, strlen(line));
Packit 4f15d5
            if (g_testsuite_last_ok) {
Packit 4f15d5
                TS_ASSERT_CHAR_EQ(line[col], ':');
Packit 4f15d5
            }
Packit 4f15d5
Packit 4f15d5
            fieldscnt += g_testsuite_last_ok;
Packit 4f15d5
            line = cursor;
Packit 4f15d5
        }
Packit 4f15d5
        TS_ASSERT_SIGNED_GT(fieldscnt, 2);
Packit 4f15d5
Packit 4f15d5
        ++fdno;
Packit 4f15d5
        line = cursor;
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    TS_ASSERT_SIGNED_EQ(fdno, sizeof(fds)/sizeof(fds[0]));
Packit 4f15d5
    free(file);
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
TS_MAIN
Packit 4f15d5
{
Packit 4f15d5
    if (strcmp(argv[0], "wait") == 0) {
Packit 4f15d5
        FILE *services = fopen("/etc/services", "r");
Packit 4f15d5
        FILE *passwd = fopen("/etc/passwd", "r");
Packit 4f15d5
        FILE *group = fopen("/etc/group", "r");
Packit 4f15d5
Packit 4f15d5
        /* Notify parent */
Packit 4f15d5
        close(STDOUT_FILENO);
Packit 4f15d5
Packit 4f15d5
        /* Wait for parent */
Packit 4f15d5
        while (1) {
Packit 4f15d5
            sleep(1);
Packit 4f15d5
        }
Packit 4f15d5
Packit 4f15d5
        fclose(group);
Packit 4f15d5
        fclose(passwd);
Packit 4f15d5
        fclose(services);
Packit 4f15d5
        exit(EXIT_SUCCESS);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    pid_t pid = prepare_process();
Packit 4f15d5
Packit 4f15d5
    char proc_dir_path[strlen("/proc/%d/fd") + sizeof(pid_t) * 3];
Packit 4f15d5
    if (sizeof(proc_dir_path) <= snprintf(proc_dir_path, sizeof(proc_dir_path), "/proc/%d/fd", pid)) {
Packit 4f15d5
        errx(EXIT_FAILURE, "too small buffer for proc dir path");
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    {
Packit 4f15d5
        TS_PRINTF("%s\n", "dump_fd_info");
Packit 4f15d5
        char fdinfo_filename[strlen(FILENAME_FORMAT) + sizeof(pid_t) * 3 + strlen("dump_fd_info")];
Packit 4f15d5
        if (sizeof(fdinfo_filename) <= snprintf(fdinfo_filename, sizeof(fdinfo_filename), FILENAME_FORMAT, pid, "dump_fd_info")) {
Packit 4f15d5
            errx(EXIT_FAILURE, "too small buffer for file name");
Packit 4f15d5
        }
Packit 4f15d5
Packit 4f15d5
        TS_ASSERT_FUNCTION(dump_fd_info(fdinfo_filename, proc_dir_path));
Packit 4f15d5
Packit 4f15d5
        struct stat st;
Packit 4f15d5
        TS_ASSERT_FUNCTION(stat(fdinfo_filename, &st);;
Packit 4f15d5
        if (g_testsuite_last_ok) {
Packit 4f15d5
            TS_ASSERT_SIGNED_EQ(st.st_mode & 0777, 0600);
Packit 4f15d5
        }
Packit 4f15d5
Packit 4f15d5
        check_file_contents(fdinfo_filename);
Packit 4f15d5
Packit 4f15d5
        unlink(fdinfo_filename);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    {
Packit 4f15d5
        TS_PRINTF("%s\n", "dump_fd_info_ext");
Packit 4f15d5
        char fdinfo_filename[strlen(FILENAME_FORMAT) + sizeof(pid_t) * 3 + strlen("dump_fd_info_ext")];
Packit 4f15d5
        if (sizeof(fdinfo_filename) <= snprintf(fdinfo_filename, sizeof(fdinfo_filename), FILENAME_FORMAT, pid, "dump_fd_info_ext")) {
Packit 4f15d5
            errx(EXIT_FAILURE, "too small buffer for file name");
Packit 4f15d5
        }
Packit 4f15d5
Packit 4f15d5
        const uid_t uid = getuid();
Packit 4f15d5
        const gid_t gid = getgid();
Packit 4f15d5
        TS_ASSERT_FUNCTION(dump_fd_info_ext(fdinfo_filename, proc_dir_path, uid, gid));
Packit 4f15d5
Packit 4f15d5
        struct stat st;
Packit 4f15d5
        TS_ASSERT_FUNCTION(stat(fdinfo_filename, &st);;
Packit 4f15d5
        if (g_testsuite_last_ok) {
Packit 4f15d5
            TS_ASSERT_SIGNED_EQ(st.st_mode & 0777, 0600);
Packit 4f15d5
        }
Packit 4f15d5
Packit 4f15d5
        check_file_contents(fdinfo_filename);
Packit 4f15d5
Packit 4f15d5
        unlink(fdinfo_filename);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    {
Packit 4f15d5
        TS_PRINTF("%s\n", "dump_fd_info_at");
Packit 4f15d5
        char fdinfo_filename[strlen(FILENAME_FORMAT) + sizeof(pid_t) * 3 + strlen("dump_fd_info_at")];
Packit 4f15d5
        if (sizeof(fdinfo_filename) <= snprintf(fdinfo_filename, sizeof(fdinfo_filename), FILENAME_FORMAT, pid, "dump_fd_info_at")) {
Packit 4f15d5
            errx(EXIT_FAILURE, "too small buffer for file name");
Packit 4f15d5
        }
Packit 4f15d5
Packit 4f15d5
        FILE *dest = fopen(fdinfo_filename, "w");
Packit 4f15d5
        const int pid_proc_fd = open_proc_pid_dir(pid);
Packit 4f15d5
Packit 4f15d5
        TS_ASSERT_FUNCTION(dump_fd_info_at(pid_proc_fd, dest));
Packit 4f15d5
Packit 4f15d5
        close(pid_proc_fd);
Packit 4f15d5
        fclose(dest);
Packit 4f15d5
Packit 4f15d5
        check_file_contents(fdinfo_filename);
Packit 4f15d5
Packit 4f15d5
        unlink(fdinfo_filename);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    kill_process(pid);
Packit 4f15d5
}
Packit 4f15d5
TS_RETURN_MAIN
Packit 4f15d5
]])
Packit 4f15d5
Packit 4f15d5
Packit 4f15d5
## ------------- ##
Packit 4f15d5
## get_fs-u_g-id ##
Packit 4f15d5
## ------------- ##
Packit 4f15d5
Packit 4f15d5
AT_TESTFUN([get_fs-u_g-id], [[
Packit 4f15d5
#include "testsuite.h"
Packit 4f15d5
#include <sys/sendfile.h>
Packit 4f15d5
#include <err.h>
Packit 4f15d5
Packit 4f15d5
TS_MAIN
Packit 4f15d5
{
Packit 4f15d5
    char *proc_pid_status = xmalloc_xopen_read_close("/proc/self/status", NULL);
Packit 4f15d5
    TS_ASSERT_SIGNED_EQ(get_fsuid(proc_pid_status), getuid());
Packit 4f15d5
    TS_ASSERT_SIGNED_EQ(get_fsgid(proc_pid_status), getgid());
Packit 4f15d5
    free(proc_pid_status);
Packit 4f15d5
}
Packit 4f15d5
TS_RETURN_MAIN
Packit 4f15d5
]])
Packit 4f15d5
Packit 4f15d5
Packit 4f15d5
## ---------- ##
Packit 4f15d5
## get_ns_ids ##
Packit 4f15d5
## ---------- ##
Packit 4f15d5
Packit 4f15d5
AT_TESTFUN([get_ns_ids], [[
Packit 4f15d5
#include "testsuite.h"
Packit 4f15d5
#include <sys/sendfile.h>
Packit 4f15d5
#include <err.h>
Packit 4f15d5
Packit 4f15d5
void check(struct ns_ids *ids)
Packit 4f15d5
{
Packit 4f15d5
    const int nsfd = open("/proc/self/ns", O_DIRECTORY);
Packit 4f15d5
    assert(nsfd >= 0);
Packit 4f15d5
Packit 4f15d5
    for (size_t i = 0; i < ARRAY_SIZE(libreport_proc_namespaces); ++i) {
Packit 4f15d5
        struct stat st;
Packit 4f15d5
        if (fstatat(nsfd, libreport_proc_namespaces[i], &st, 0) < 0) {
Packit 4f15d5
            TS_ASSERT_SIGNED_OP_MESSAGE(ids->nsi_ids[i], ==, PROC_NS_UNSUPPORTED, libreport_proc_namespaces[i]);
Packit 4f15d5
        }
Packit 4f15d5
        else {
Packit 4f15d5
            TS_ASSERT_SIGNED_OP_MESSAGE(ids->nsi_ids[i], ==, st.st_ino, libreport_proc_namespaces[i]);
Packit 4f15d5
        }
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    DIR *nsdir = fdopendir(nsfd);
Packit 4f15d5
    assert(nsdir != NULL);
Packit 4f15d5
Packit 4f15d5
    struct dirent *dent = NULL;
Packit 4f15d5
    while ((dent = readdir(nsdir))) {
Packit 4f15d5
        if (dot_or_dotdot(dent->d_name))
Packit 4f15d5
            continue;
Packit 4f15d5
Packit 4f15d5
        size_t i = 0;
Packit 4f15d5
        for (; i < ARRAY_SIZE(libreport_proc_namespaces); ++i) {
Packit 4f15d5
            if (strcmp(libreport_proc_namespaces[i], dent->d_name) == 0) {
Packit 4f15d5
                break;
Packit 4f15d5
            }
Packit 4f15d5
        }
Packit 4f15d5
        TS_ASSERT_SIGNED_OP_MESSAGE(i, <, ARRAY_SIZE(libreport_proc_namespaces), dent->d_name);
Packit 4f15d5
Packit 4f15d5
        if (g_testsuite_last_ok) {
Packit 4f15d5
            struct stat st;
Packit 4f15d5
            TS_ASSERT_FUNCTION(fstatat(nsfd, dent->d_name, &st, 0));
Packit 4f15d5
            TS_ASSERT_SIGNED_OP_MESSAGE(ids->nsi_ids[i], ==, st.st_ino, dent->d_name);
Packit 4f15d5
        }
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    closedir(nsdir);
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
TS_MAIN
Packit 4f15d5
{
Packit 4f15d5
    TS_PRINTF("%s\n", "get_ns_ids");
Packit 4f15d5
    struct ns_ids pid_ids;
Packit 4f15d5
    TS_ASSERT_FUNCTION(get_ns_ids(getpid(), &pid_ids));
Packit 4f15d5
    check(&pid_ids);
Packit 4f15d5
Packit 4f15d5
    TS_PRINTF("%s\n", "get_ns_ids_at");
Packit 4f15d5
    const int pid_proc_dir = open_proc_pid_dir(getpid());
Packit 4f15d5
    struct ns_ids proc_ids;
Packit 4f15d5
    TS_ASSERT_FUNCTION(get_ns_ids_at(pid_proc_dir, &proc_ids));
Packit 4f15d5
    check(&proc_ids);
Packit 4f15d5
    close(pid_proc_dir);
Packit 4f15d5
}
Packit 4f15d5
TS_RETURN_MAIN
Packit 4f15d5
]])
Packit 4f15d5
Packit 4f15d5
Packit 4f15d5
## ------------------------------ ##
Packit 4f15d5
##  get_mountinfo_for_mount_point ##
Packit 4f15d5
## ------------------------------ ##
Packit 4f15d5
Packit 4f15d5
Packit 4f15d5
AT_TESTFUN([get_mountinfo_for_mount_point], [[
Packit 4f15d5
#include "testsuite.h"
Packit 4f15d5
#include <err.h>
Packit 4f15d5
Packit 4f15d5
void test_get_mountinfo_for_mount_point(
Packit 4f15d5
    const char *mount_point,
Packit 4f15d5
    const char *description,
Packit 4f15d5
    const char *input,
Packit 4f15d5
    const char *error,
Packit 4f15d5
    int exp_r,
Packit 4f15d5
    const struct mountinfo *exp_mntnf)
Packit 4f15d5
{
Packit 4f15d5
    TS_PRINTF("++++ %s\n", description);
Packit 4f15d5
Packit 4f15d5
    char *buf = NULL;
Packit 4f15d5
Packit 4f15d5
    FILE *mntnf_file = fmemopen((void *)input, strlen(input), "r");
Packit 4f15d5
    if (mntnf_file == NULL) {
Packit 4f15d5
        err(EXIT_FAILURE, "open_memstream");
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    struct mountinfo mntnf;
Packit 4f15d5
    int r;
Packit 4f15d5
    TS_ASSERT_STREAM_FD_CONTENTS_EQ_BEGIN(STDERR_FILENO);
Packit 4f15d5
    r = get_mountinfo_for_mount_point(mntnf_file, &mntnf, mount_point);
Packit 4f15d5
    TS_ASSERT_STREAM_FD_CONTENTS_EQ_END(error, description);
Packit 4f15d5
    TS_ASSERT_SIGNED_OP_MESSAGE(r, ==, exp_r, description);
Packit 4f15d5
Packit 4f15d5
    if (r == 0 && exp_mntnf != NULL) {
Packit 4f15d5
        TS_ASSERT_STRING_EQ(mntnf.mntnf_items[0], exp_mntnf->mntnf_items[0], "Item 0");
Packit 4f15d5
        TS_ASSERT_STRING_EQ(mntnf.mntnf_items[1], exp_mntnf->mntnf_items[1], "Item 1");
Packit 4f15d5
        TS_ASSERT_STRING_EQ(mntnf.mntnf_items[2], exp_mntnf->mntnf_items[2], "Item 2");
Packit 4f15d5
        TS_ASSERT_STRING_EQ(mntnf.mntnf_items[3], exp_mntnf->mntnf_items[3], "Item 3");
Packit 4f15d5
        TS_ASSERT_STRING_EQ(mntnf.mntnf_items[4], exp_mntnf->mntnf_items[4], "Item 4");
Packit 4f15d5
        TS_ASSERT_STRING_EQ(mntnf.mntnf_items[5], exp_mntnf->mntnf_items[5], "Item 5");
Packit 4f15d5
        TS_ASSERT_STRING_EQ(mntnf.mntnf_items[6], exp_mntnf->mntnf_items[6], "Item 6");
Packit 4f15d5
        TS_ASSERT_STRING_EQ(mntnf.mntnf_items[7], exp_mntnf->mntnf_items[7], "Item 7");
Packit 4f15d5
        TS_ASSERT_STRING_EQ(mntnf.mntnf_items[8], exp_mntnf->mntnf_items[8], "Item 8");
Packit 4f15d5
        TS_ASSERT_STRING_EQ(mntnf.mntnf_items[9], exp_mntnf->mntnf_items[9], "Item 9");
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    fclose(mntnf_file);
Packit 4f15d5
    free(buf);
Packit 4f15d5
Packit 4f15d5
    TS_PRINTF("---- %s\n", description);
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
#define TS_MOUNT_INFO(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9) \
Packit 4f15d5
    ((struct mountinfo){ .mntnf_items = { (char *)f0, (char *)f1, (char *)f2, \
Packit 4f15d5
                                          (char *)f3, (char *)f4, (char *)f5, \
Packit 4f15d5
                                          (char *)f6, (char *)f7, (char *)f8, \
Packit 4f15d5
                                          (char *)f9 } })
Packit 4f15d5
Packit 4f15d5
Packit 4f15d5
TS_MAIN
Packit 4f15d5
{
Packit 4f15d5
    test_get_mountinfo_for_mount_point(
Packit 4f15d5
        /* mount point */ "/",
Packit 4f15d5
        /* description */ "Empty file",
Packit 4f15d5
        /* input       */ "",
Packit 4f15d5
        /* error       */ "Mountinfo line does not have enough fields 0\n",
Packit 4f15d5
        /* exp_r       */ 1,
Packit 4f15d5
        /* exp_mntnf   */ NULL
Packit 4f15d5
    );
Packit 4f15d5
Packit 4f15d5
    test_get_mountinfo_for_mount_point(
Packit 4f15d5
        /* mount point */ "/",
Packit 4f15d5
        /* description */ "Not-enough fields",
Packit 4f15d5
        /* input       */ "12 34 567:10 ",
Packit 4f15d5
        /* error       */ "Mountinfo line does not have enough fields 3\n",
Packit 4f15d5
        /* exp_r       */ 1,
Packit 4f15d5
        /* exp_mntnf   */ NULL
Packit 4f15d5
    );
Packit 4f15d5
Packit 4f15d5
    test_get_mountinfo_for_mount_point(
Packit 4f15d5
        /* mount point */ "/",
Packit 4f15d5
        /* description */ "Missing the mount point field",
Packit 4f15d5
        /* input       */ "12 34 567:10 /foo ",
Packit 4f15d5
        /* error       */ "Mountinfo line does not have the mount point field\n",
Packit 4f15d5
        /* exp_r       */ 2,
Packit 4f15d5
        /* exp_mntnf   */ NULL
Packit 4f15d5
    );
Packit 4f15d5
Packit 4f15d5
    test_get_mountinfo_for_mount_point(
Packit 4f15d5
        /* mount point */ "/",
Packit 4f15d5
        /* description */ "Not found mount point",
Packit 4f15d5
        /* input       */ "12 34 567:10 /foo /bar ",
Packit 4f15d5
        /* error       */ "",
Packit 4f15d5
        /* exp_r       */ -ENOKEY,
Packit 4f15d5
        /* exp_mntnf   */ NULL
Packit 4f15d5
    );
Packit 4f15d5
Packit 4f15d5
    test_get_mountinfo_for_mount_point(
Packit 4f15d5
        /* mount point */ "/",
Packit 4f15d5
        /* description */ "Unexpected end of file",
Packit 4f15d5
        /* input       */ "12 34 567:10 /foo / rw,noatime",
Packit 4f15d5
        /* error       */ "Unexpected end of file\n",
Packit 4f15d5
        /* exp_r       */ -ENODATA,
Packit 4f15d5
        /* exp_mntnf   */ NULL
Packit 4f15d5
    );
Packit 4f15d5
Packit 4f15d5
    test_get_mountinfo_for_mount_point(
Packit 4f15d5
        /* mount point */ "/",
Packit 4f15d5
        /* description */ "Escaped path: no mount point field",
Packit 4f15d5
        /* input       */ "12 34 567:10 /foo\\ bar ",
Packit 4f15d5
        /* error       */ "Mountinfo line does not have the mount point field\n",
Packit 4f15d5
        /* exp_r       */ 2,
Packit 4f15d5
        /* exp_mntnf   */ NULL
Packit 4f15d5
    );
Packit 4f15d5
Packit 4f15d5
    test_get_mountinfo_for_mount_point(
Packit 4f15d5
        /* mount point */ "/",
Packit 4f15d5
        /* description */ "Correct, matching line",
Packit 4f15d5
        /* input       */ "12 34 567:10 /foo / rw,noatime shared:1 - xfs /dev/sda1 rw,seclabel,attr2",
Packit 4f15d5
        /* error       */ "",
Packit 4f15d5
        /* exp_r       */ 0,
Packit 4f15d5
        /* exp_mntnf   */ &TS_MOUNT_INFO("12", "34", "567:10", "/foo", "/", "rw,noatime", "shared:1", "xfs", "/dev/sda1", "rw,seclabel,attr2")
Packit 4f15d5
    );
Packit 4f15d5
Packit 4f15d5
    test_get_mountinfo_for_mount_point(
Packit 4f15d5
        /* mount point */ "/",
Packit 4f15d5
        /* description */ "Correct, matching line, empty optional fields",
Packit 4f15d5
        /* input       */ "12 34 567:10 /foo / rw,noatime - xfs /dev/sda1 rw,seclabel,attr2",
Packit 4f15d5
        /* error       */ "",
Packit 4f15d5
        /* exp_r       */ 0,
Packit 4f15d5
        /* exp_mntnf   */ &TS_MOUNT_INFO("12", "34", "567:10", "/foo", "/", "rw,noatime", "", "xfs", "/dev/sda1", "rw,seclabel,attr2")
Packit 4f15d5
    );
Packit 4f15d5
Packit 4f15d5
    test_get_mountinfo_for_mount_point(
Packit 4f15d5
        /* mount point */ "/",
Packit 4f15d5
        /* description */ "Correct, matching line, several optional fields",
Packit 4f15d5
        /* input       */ "12 34 567:10 /foo / rw,noatime shared:1 master:2 unbindable:3 - xfs /dev/sda1 rw,seclabel,attr2",
Packit 4f15d5
        /* error       */ "",
Packit 4f15d5
        /* exp_r       */ 0,
Packit 4f15d5
        /* exp_mntnf   */ &TS_MOUNT_INFO("12", "34", "567:10", "/foo", "/", "rw,noatime", "shared:1 master:2 unbindable:3", "xfs", "/dev/sda1", "rw,seclabel,attr2")
Packit 4f15d5
    );
Packit 4f15d5
Packit 4f15d5
    test_get_mountinfo_for_mount_point(
Packit 4f15d5
        /* mount point */ "/",
Packit 4f15d5
        /* description */ "Escaped path: correct, matching line",
Packit 4f15d5
        /* input       */ "12 34 567:10 /foo\\ bar / rw,noatime shared:1 - xfs /dev/sda1 rw,seclabel,attr2",
Packit 4f15d5
        /* error       */ "",
Packit 4f15d5
        /* exp_r       */ 0,
Packit 4f15d5
        /* exp_mntnf   */ &TS_MOUNT_INFO("12", "34", "567:10", "/foo\\ bar", "/", "rw,noatime", "shared:1", "xfs", "/dev/sda1", "rw,seclabel,attr2")
Packit 4f15d5
    );
Packit 4f15d5
Packit 4f15d5
    test_get_mountinfo_for_mount_point(
Packit 4f15d5
        /* mount point */ "/",
Packit 4f15d5
        /* description */ "Escaped path: correct, matching line + escaped mount source",
Packit 4f15d5
        /* input       */ "12 34 567:10 /foo\\ bar / rw,noatime shared:1 - xfs /dev/sda\\ 1 rw,seclabel,attr2",
Packit 4f15d5
        /* error       */ "",
Packit 4f15d5
        /* exp_r       */ 0,
Packit 4f15d5
        /* exp_mntnf   */ &TS_MOUNT_INFO("12", "34", "567:10", "/foo\\ bar", "/", "rw,noatime", "shared:1", "xfs", "/dev/sda\\ 1", "rw,seclabel,attr2")
Packit 4f15d5
    );
Packit 4f15d5
}
Packit 4f15d5
TS_RETURN_MAIN
Packit 4f15d5
]])
Packit 4f15d5
Packit 4f15d5
Packit 4f15d5
## ------------------- ##
Packit 4f15d5
## dump_namespace_diff ##
Packit 4f15d5
## ------------------- ##
Packit 4f15d5
Packit 4f15d5
AT_TESTFUN([dump_namespace_diff], [[
Packit 4f15d5
#include "testsuite.h"
Packit 4f15d5
#include <err.h>
Packit 4f15d5
Packit 4f15d5
#define FILENAME_FORMAT "/tmp/libreport.testsuite.namespace_diff.%d.%s"
Packit 4f15d5
Packit 4f15d5
void check_file_contents(const char *filename)
Packit 4f15d5
{
Packit 4f15d5
    char *expected;
Packit 4f15d5
    struct stat st;
Packit 4f15d5
Packit 4f15d5
    char const *pid_for_children = "default";
Packit 4f15d5
    char const *cgroup = "default";
Packit 4f15d5
Packit 4f15d5
    if (stat("/proc/self/ns/cgroup", &st) < 0 && errno == ENOENT)
Packit 4f15d5
        cgroup = "unknown";
Packit 4f15d5
Packit 4f15d5
    if (stat("/proc/self/ns/pid_for_children", &st) < 0 && errno == ENOENT)
Packit 4f15d5
        pid_for_children = "unknown";
Packit 4f15d5
Packit 4f15d5
    expected = xasprintf("ipc : default\n"
Packit 4f15d5
                         "mnt : default\n"
Packit 4f15d5
                         "net : default\n"
Packit 4f15d5
                         "pid : default\n"
Packit 4f15d5
                         "uts : default\n"
Packit 4f15d5
                         "user : default\n"
Packit 4f15d5
                         "cgroup : %s\n"
Packit 4f15d5
                         "pid_for_children : %s\n", cgroup, pid_for_children);
Packit 4f15d5
Packit 4f15d5
    char *file = xmalloc_xopen_read_close(filename, NULL);
Packit 4f15d5
    TS_ASSERT_STRING_EQ(file, expected, "Namespaces");
Packit 4f15d5
    free(file);
Packit 4f15d5
    free(expected);
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
TS_MAIN
Packit 4f15d5
{
Packit 4f15d5
    {
Packit 4f15d5
        TS_PRINTF("%s\n", "dump_namespace_diff");
Packit 4f15d5
        char filename[strlen(FILENAME_FORMAT) + sizeof(pid_t) * 3 + strlen("dump_namespace_diff")];
Packit 4f15d5
        if (sizeof(filename) <= snprintf(filename, sizeof(filename), FILENAME_FORMAT, getpid(), "dump_namespace_diff")) {
Packit 4f15d5
            errx(EXIT_FAILURE, "too small buffer for file name");
Packit 4f15d5
        }
Packit 4f15d5
Packit 4f15d5
        TS_ASSERT_FUNCTION(dump_namespace_diff(filename, getpid(), getppid()));
Packit 4f15d5
Packit 4f15d5
        struct stat st;
Packit 4f15d5
        TS_ASSERT_FUNCTION(stat(filename, &st);;
Packit 4f15d5
        if (g_testsuite_last_ok) {
Packit 4f15d5
            TS_ASSERT_SIGNED_EQ(st.st_mode & 0777, 0600);
Packit 4f15d5
        }
Packit 4f15d5
Packit 4f15d5
        check_file_contents(filename);
Packit 4f15d5
Packit 4f15d5
        unlink(filename);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    {
Packit 4f15d5
        TS_PRINTF("%s\n", "dump_namespace_diff_ext");
Packit 4f15d5
        char filename[strlen(FILENAME_FORMAT) + sizeof(pid_t) * 3 + strlen("dump_namespace_diff_ext")];
Packit 4f15d5
        if (sizeof(filename) <= snprintf(filename, sizeof(filename), FILENAME_FORMAT, getpid(), "dump_namespace_diff_ext")) {
Packit 4f15d5
            errx(EXIT_FAILURE, "too small buffer for file name");
Packit 4f15d5
        }
Packit 4f15d5
Packit 4f15d5
        const uid_t uid = getuid();
Packit 4f15d5
        const gid_t gid = getgid();
Packit 4f15d5
        TS_ASSERT_FUNCTION(dump_namespace_diff_ext(filename, getpid(), getppid(), uid, gid));
Packit 4f15d5
Packit 4f15d5
        struct stat st;
Packit 4f15d5
        TS_ASSERT_FUNCTION(stat(filename, &st);;
Packit 4f15d5
        if (g_testsuite_last_ok) {
Packit 4f15d5
            TS_ASSERT_SIGNED_EQ(st.st_mode & 0777, 0600);
Packit 4f15d5
        }
Packit 4f15d5
Packit 4f15d5
        check_file_contents(filename);
Packit 4f15d5
Packit 4f15d5
        unlink(filename);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    {
Packit 4f15d5
        TS_PRINTF("%s\n", "dump_namespace_diff_at");
Packit 4f15d5
        char filename[strlen(FILENAME_FORMAT) + sizeof(pid_t) * 3 + strlen("dump_namespace_diff_at")];
Packit 4f15d5
        if (sizeof(filename) <= snprintf(filename, sizeof(filename), FILENAME_FORMAT, getppid(), "dump_namespace_diff_at")) {
Packit 4f15d5
            errx(EXIT_FAILURE, "too small buffer for file name");
Packit 4f15d5
        }
Packit 4f15d5
Packit 4f15d5
        FILE *dest = fopen(filename, "w");
Packit 4f15d5
        assert(dest != NULL);
Packit 4f15d5
Packit 4f15d5
        const int pid_proc_fd = open_proc_pid_dir(getppid());
Packit 4f15d5
Packit 4f15d5
        TS_ASSERT_FUNCTION(dump_namespace_diff_at(pid_proc_fd, pid_proc_fd, dest));
Packit 4f15d5
Packit 4f15d5
        close(pid_proc_fd);
Packit 4f15d5
        fclose(dest);
Packit 4f15d5
Packit 4f15d5
        check_file_contents(filename);
Packit 4f15d5
Packit 4f15d5
        unlink(filename);
Packit 4f15d5
    }
Packit 4f15d5
}
Packit 4f15d5
TS_RETURN_MAIN
Packit 4f15d5
]])
Packit 4f15d5
Packit 4f15d5
Packit 4f15d5
## -------------------- ##
Packit 4f15d5
## process_has_own_root ##
Packit 4f15d5
## -------------------- ##
Packit 4f15d5
Packit 4f15d5
AT_TESTFUN([process_has_own_root], [[
Packit 4f15d5
#include "testsuite.h"
Packit 4f15d5
#include <sys/sendfile.h>
Packit 4f15d5
#include <err.h>
Packit 4f15d5
Packit 4f15d5
void write_cmd_output_to_fd(int fd, const char *cmd)
Packit 4f15d5
{
Packit 4f15d5
    FILE *proc = popen(cmd, "r");
Packit 4f15d5
    if (proc == NULL) {
Packit 4f15d5
        err(EXIT_FAILURE, "popen(%s)", cmd);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    char *output = xmalloc_fgetline(proc);
Packit 4f15d5
    TS_PRINTF("%s : %s\n", cmd, output);
Packit 4f15d5
Packit 4f15d5
    const int retcode = pclose(proc);
Packit 4f15d5
    if (retcode == -1) {
Packit 4f15d5
        err(EXIT_FAILURE, "pclose(%s)", cmd);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (retcode != 0) {
Packit 4f15d5
        errx(EXIT_FAILURE, "non-0 status %d of '%s'", cmd);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (output == NULL) {
Packit 4f15d5
        errx(EXIT_FAILURE, "no output of '%s'", cmd);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    full_write_str(fd, output);
Packit 4f15d5
    free(output);
Packit 4f15d5
}
Packit 4f15d5
Packit 4f15d5
TS_MAIN
Packit 4f15d5
{
Packit 4f15d5
    char mock_pid_proc[] = "/tmp/libreport.testsuite.pid.XXXXXX";
Packit 4f15d5
Packit 4f15d5
    if (mkdtemp(mock_pid_proc) == NULL) {
Packit 4f15d5
        err(EXIT_FAILURE, "mkdtemp(%s)", mock_pid_proc);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    const int mock_pid_proc_fd = open(mock_pid_proc, O_DIRECTORY);
Packit 4f15d5
    if (mock_pid_proc_fd < 0) {
Packit 4f15d5
        err(EXIT_FAILURE, "open(%s, O_DIRECTORY)", mock_pid_proc);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    {
Packit 4f15d5
        /* TODO: add test for open file descriptors */
Packit 4f15d5
        const int r = process_has_own_root_at(mock_pid_proc_fd);
Packit 4f15d5
        TS_ASSERT_SIGNED_EQ(r, -ENOENT);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    /* Please, notice that the mode is intentionally 0000 - no read, no write,
Packit 4f15d5
     * no execute access */
Packit 4f15d5
    int mntnf_fd = openat(mock_pid_proc_fd, "mountinfo", O_RDWR | O_CREAT | O_EXCL, 0000);
Packit 4f15d5
Packit 4f15d5
    {
Packit 4f15d5
        /* TODO: add test for open file descriptors */
Packit 4f15d5
        const int r = process_has_own_root_at(mock_pid_proc_fd);
Packit 4f15d5
        TS_ASSERT_SIGNED_EQ(r, -EACCES);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    /* Make the file readable & writable */
Packit 4f15d5
    fchmod(mntnf_fd, 0600);
Packit 4f15d5
Packit 4f15d5
    {
Packit 4f15d5
        /* TODO: add test for open file descriptors */
Packit 4f15d5
        const int r = process_has_own_root_at(mock_pid_proc_fd);
Packit 4f15d5
        TS_ASSERT_SIGNED_EQ(r, -ENOKEY);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    full_write_str(mntnf_fd, "36 35 98:0 /madeuproot /foo rw,noatime master:1 - ext3 /dev/myroot rw,errors=continue\n");
Packit 4f15d5
    full_write_str(mntnf_fd, "37 38 99:0 /mnt3 /mnt4 rw,noatime master:2 - ext3 /dev/boot rw,errors=continue\n");
Packit 4f15d5
Packit 4f15d5
    fsync(mntnf_fd);
Packit 4f15d5
    lseek(mntnf_fd, 0, SEEK_SET);
Packit 4f15d5
Packit 4f15d5
    TS_PRINTF("Made-up mountinfo created in %s\n", mock_pid_proc);
Packit 4f15d5
Packit 4f15d5
    {
Packit 4f15d5
        /* TODO: add test for open file descriptors */
Packit 4f15d5
        const int r = process_has_own_root_at(mock_pid_proc_fd);
Packit 4f15d5
        TS_ASSERT_SIGNED_EQ(r, -ENOKEY);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    TS_PRINTF("Going to copy /proc/1/mountinfo to %s\n", mock_pid_proc);
Packit 4f15d5
Packit 4f15d5
    const int pid1_mntnf_fd = open("/proc/1/mountinfo", O_RDONLY);
Packit 4f15d5
    if (pid1_mntnf_fd < 0) {
Packit 4f15d5
        err(EXIT_FAILURE, "/proc/1/mountinfo");
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    TS_PRINTF("Copying /proc/1/mountinfo to %s\n", mock_pid_proc);
Packit 4f15d5
Packit 4f15d5
    {
Packit 4f15d5
        int r = 0;
Packit 4f15d5
Packit 4f15d5
        while ((r = sendfile(mntnf_fd, pid1_mntnf_fd, NULL, 65535)) > 0)
Packit 4f15d5
            ;
Packit 4f15d5
Packit 4f15d5
        if (r < 0) {
Packit 4f15d5
            err(EXIT_FAILURE, "Cannot copy /proc/1/mountinfo to %s", mock_pid_proc);
Packit 4f15d5
        }
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    close(pid1_mntnf_fd);
Packit 4f15d5
Packit 4f15d5
    fsync(mntnf_fd);
Packit 4f15d5
    lseek(mntnf_fd, 0, SEEK_SET);
Packit 4f15d5
Packit 4f15d5
    TS_PRINTF("Copied /proc/1/mountinfo to %s\n", mock_pid_proc);
Packit 4f15d5
Packit 4f15d5
    {
Packit 4f15d5
        /* TODO: add test for open file descriptors */
Packit 4f15d5
        const int r = process_has_own_root_at(mock_pid_proc_fd);
Packit 4f15d5
        TS_ASSERT_SIGNED_EQ(r, 0);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    /* Test different source directory. Swap / with \ in the mock mountinfo. */
Packit 4f15d5
    fsync(mntnf_fd);
Packit 4f15d5
    lseek(mntnf_fd, 0, SEEK_SET);
Packit 4f15d5
Packit 4f15d5
    full_write_str(mntnf_fd, "12 34 567:89 /madeuproot / ");
Packit 4f15d5
    write_cmd_output_to_fd(mntnf_fd, "findmnt -F /proc/1/mountinfo -r -n -o VFS-OPTIONS,OPT-FIELDS -T /");
Packit 4f15d5
    full_write_str(mntnf_fd, " - ");
Packit 4f15d5
    write_cmd_output_to_fd(mntnf_fd, "findmnt -F /proc/1/mountinfo -r -n -o FSTYPE,SOURCE,FS-OPTIONS -T /");
Packit 4f15d5
Packit 4f15d5
    fsync(mntnf_fd);
Packit 4f15d5
    lseek(mntnf_fd, 0, SEEK_SET);
Packit 4f15d5
Packit 4f15d5
    {
Packit 4f15d5
        /* TODO: add test for open file descriptors */
Packit 4f15d5
        const int r = process_has_own_root_at(mock_pid_proc_fd);
Packit 4f15d5
        TS_ASSERT_SIGNED_EQ(r, 1);
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    close(mntnf_fd);
Packit 4f15d5
Packit 4f15d5
    if (unlinkat(mock_pid_proc_fd, "mountinfo", 0) < 0) {
Packit 4f15d5
        perror("unlinkat(fd, mountinfo)");
Packit 4f15d5
    }
Packit 4f15d5
Packit 4f15d5
    if (rmdir(mock_pid_proc) < 0) {
Packit 4f15d5
        perror("rmdir(/mock_pid_dir)");
Packit 4f15d5
    }
Packit 4f15d5
}
Packit 4f15d5
TS_RETURN_MAIN
Packit 4f15d5
]])