#include "fdtable.hpp"
#include <unistd.h>
#include <dirent.h>
#include <sys/resource.h>


HOOK(INIT, HOOK_PRIO_MID) { ///< adjusts fdlimit according to FdTable::size
    const rlim_t lim = FdTable<int>::size;

    struct rlimit rlim;
    if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) {
        log_errno(error, "getrlimit(NOFILE)");
        return;
    }
    log(debug, "getrlimit(RLIMIT_NOFILE): %lu/%lu (%lu)", rlim.rlim_cur, rlim.rlim_max, lim);

    // decrease or un-priviledged increase?
    if (rlim.rlim_cur > lim || rlim.rlim_max >= lim) {
        rlim.rlim_cur = lim;
        if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) {
            log_errno(error, "setrlimit(NOFILE,%lu,%lu)", lim, rlim.rlim_max);
        }
        return;
    }

    // need priviledged increase
    rlim.rlim_cur = lim;
    rlim.rlim_max = lim;
    if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) {
        log_errno(error, "setrlimit(NOFILE,%lu)", lim);
    }
}


int get_maxfd() {
    return getdtablesize(); // TODO: should we cache this?
}


int get_curr_maxfd() {
    // https://github.com/openssh/openssh-portable/blob/master/openbsd-compat/bsd-closefrom.c
    // https://git.gnome.org/browse/glib/tree/gio/libasyncns/asyncns.c?h=2.21.0#n205
    DIR* dirp = opendir("/proc/self/fd");
    unless (dirp) {
        log_errno(error, "opendir(/proc/self/fd)");
        return get_maxfd();
    }

    int maxfd = -1;
    struct dirent* dent;
    while ((dent = readdir(dirp)) != NULL) {
        int fd = atoi(dent->d_name);
        if (fd >= 0 && fd > maxfd && fd != dirfd(dirp)) {
            maxfd = fd;
        }
    }
    closedir(dirp);
    ++maxfd;

    #ifdef DEBUG
        if (is_valgrind()) {
            return MIN(maxfd, get_maxfd());
        } else {
            assert(maxfd <= get_maxfd());
        }
    #endif
    return maxfd;
}