#include "sys.hpp"
#include "common.hpp"
#include <signal.h>


static volatile int SIG = 0;
static void signal_handler(int) {
    ++SIG;
}

volatile int* setup_signals() {
    struct sigaction sa = {};
    sigemptyset(&sa.sa_mask);

    sa.sa_handler = SIG_IGN;
    sigaction(SIGPIPE, &sa, NULL);
    sigaction(SIGCHLD, &sa, NULL); // so no wait needed

    sa.sa_handler = &signal_handler;
    sigaction(SIGINT,  &sa, NULL); // no SA_RESTART
    sigaction(SIGHUP,  &sa, NULL);
    sigaction(SIGTERM, &sa, NULL);

    return &SIG;
}


#ifdef NO_DROP_ROOT
    bool su(const char*) {
        LOG("WARNING: dropping permissions not supported!");
        return true;
    }
#else


#include <pwd.h>
#include <sys/capability.h>
#include <sys/prctl.h>

#ifndef DROP_USER
#define DROP_USER "nobody"
#endif


bool su(const char* uname) {
    if (!uname || !*uname) {
        uname = DROP_USER;
    }

    if (!getuid() || !geteuid() || !getgid() || !getegid()) {
        if (chdir("/") != 0) {
            LOG_ERRNO("chdir(/)");
            return false;
        }
        const struct passwd* pw = getpwnam(uname);
        if (!pw) {
            LOG_ERRNO("cannot drop root priviledges: getpwnam(%s)", uname);
            return false;
        }

        cap_value_t cap_values[] = {CAP_NET_ADMIN};
        cap_t caps = cap_get_proc();
        if (caps == NULL ||
            cap_set_flag(caps, CAP_PERMITTED, 2, cap_values, CAP_SET) != 0 ||
            cap_set_proc(caps) != 0 ||
            prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0 ||
            cap_free(caps) != 0) {
            LOG_ERRNO("CAP_PERMITTED: cannot keep capabilities");
            return false;
        }

        if (setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0 || setgid(0) == 0 || setuid(0) == 0) {
            LOG_ERRNO("cannot drop root priviledges: set[gu]id(%s)", uname);
            return false;
        }

        caps = cap_get_proc();
        if (caps == NULL ||
            cap_set_flag(caps, CAP_EFFECTIVE, 2, cap_values, CAP_SET) != 0 ||
            cap_set_proc(caps) != 0 ||
            cap_free(caps) != 0) {
            LOG_ERRNO("CAP_PERMITTED: cannot restore capabilities");
            return false;
        }

        LOG("running as '%s'", uname);
    }
    return true;
}


#endif // !NO_DROP_ROOT