/**
 * Copyright © 2019, Oracle and/or its affiliates. All rights reserved.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl
 *
 */
#include "fdleak.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/resource.h>              // for getrlimit, rlimit, RLIMIT_NO...

// File descriptor leak check
static size_t n_open_fds_at_start = 0;
enum { OFAS_SIZE = 100 };
enum { CHECK_FD_LIMIT = 1024 };
static int open_fds_at_start[OFAS_SIZE];
static int fd_is_open(int fd) {
    // Use fcntl F_GETFL to figure out if file is open
    return fcntl(fd, F_GETFL) != -1;
}
void find_open_fds_at_start(void) {
    struct rlimit rlim;
    if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
        fprintf(stderr, "FATAL: getrlimit() failed\n");
        abort();
    }
    for (int fd = 0; fd < (int)rlim.rlim_cur && fd < CHECK_FD_LIMIT; ++fd) {
        if (fd_is_open(fd)) {
            if (n_open_fds_at_start < OFAS_SIZE) {
                open_fds_at_start[n_open_fds_at_start++] = fd;
            } else {
                fprintf(stderr, "FATAL: too many open_fds_at_start %zd\n",
                        n_open_fds_at_start);
                abort();
            }
        }
    }
}

static int fd_was_open(int fd) {
    for (size_t i = 0; i < n_open_fds_at_start; ++i) {
        if (fd == open_fds_at_start[i]) {
            return !!1;
        }
    }
    return !!0;
}

void fdleak_check(void)
{
    struct rlimit rlim;
    if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
        fprintf(stderr, "FATAL: getrlimit() failed\n");
        abort();
    }

    for (int fd = 0; fd < (int)rlim.rlim_cur && fd < CHECK_FD_LIMIT; ++fd) {
        if (!fd_was_open(fd) && fd_is_open(fd)) {
            fprintf(stderr, "BUG: fd %d still open at end of program\n", fd);
        }
    }
}
