posix.cpp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <felspar/io/posix.hpp>

#ifdef FELSPAR_POSIX_SOCKETS
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/resource.h>
#include <sys/socket.h>
#endif


auto felspar::posix::pipe::create() -> pipe {
    int fds[2] = {};
    if (::pipe2(fds, O_NONBLOCK) != 0) {
        throw felspar::stdexcept::system_error{
                errno, std::system_category(), "Calling pipe2"};
    }
    return {fd{fds[0]}, fd{fds[1]}};
}


std::pair<std::size_t, std::size_t>
        felspar::posix::promise_to_never_use_select() {
#ifdef FELSPAR_POSIX_SOCKETS
    ::rlimit limits;
    if (::getrlimit(RLIMIT_NOFILE, &limits) != 0) {
        throw felspar::stdexcept::system_error{
                errno, std::system_category(), "getrlimit RLIMIT_NOFILE error"};
    }
    if (limits.rlim_cur < limits.rlim_max) {
        std::size_t const curr = limits.rlim_cur;
        limits.rlim_cur = limits.rlim_max;
        if (::setrlimit(RLIMIT_NOFILE, &limits) != 0) {
            throw felspar::stdexcept::system_error{
                    errno, std::system_category(),
                    "setrlimit RLIMIT_NOFILE error"};
        }
        return {curr, limits.rlim_max};
    } else {
        return {limits.rlim_cur, limits.rlim_max};
    }
#else
    return {{}, {}};
#endif
}


void felspar::posix::listen(
        io::socket_descriptor fd,
        int backlog,
        felspar::source_location const &loc) {
    if (::listen(fd, backlog) == -1) {
        throw felspar::stdexcept::system_error{
                io::get_error(), std::system_category(), "listen error", loc};
    }
}

void felspar::posix::set_non_blocking(
        io::socket_descriptor sock, felspar::source_location const &loc) {
#if defined(FELSPAR_POSIX_SOCKETS)
    if (int const err =
                ::fcntl(sock, F_SETFL, ::fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
        err != 0) {
        throw felspar::stdexcept::system_error{
                io::get_error(), std::system_category(), "fcntl F_SETFL error",
                loc};
    }
#elif defined(FELSPAR_WINSOCK2)
    u_long mode = 1;
    if (int const err = ::ioctlsocket(sock, FIONBIO, &mode);
        err == SOCKET_ERROR) {
        throw felspar::stdexcept::system_error{
                io::get_error(), std::system_category(),
                "ioctlsocket FIONBIO error", loc};
    }
#else
#error "No implementation for this platform"
#endif
}


void felspar::posix::set_reuse_port(
        io::socket_descriptor const sock, felspar::source_location const &loc) {
    int optval = 1;
#if defined(FELSPAR_WINSOCK2)

For Windows it looks like SO_REUSEADDR is the closest we can get

 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
    constexpr auto reuse_flag = SO_REUSEADDR;
    char const *popt = reinterpret_cast<char const *>(&optval);
#else
    constexpr auto reuse_flag = SO_REUSEPORT;
    int *popt = &optval;
#endif
    if (::setsockopt(sock, SOL_SOCKET, reuse_flag, popt, sizeof(optval))
        == -1) {
        throw felspar::stdexcept::system_error{
                io::get_error(), std::system_category(),
                "setsockopt SO_REUSEPORT failed", loc};
    }
}


void felspar::posix::bind_to_any_address(
        io::socket_descriptor const sock,
        std::uint16_t const port,
        felspar::source_location const &loc) {
    sockaddr_in in;
    in.sin_family = AF_INET;
    in.sin_port = htons(port);
    in.sin_addr.s_addr = htonl(INADDR_ANY);
    if (::bind(sock, reinterpret_cast<sockaddr const *>(&in), sizeof(in))
        != 0) {
        throw felspar::stdexcept::system_error{
                io::get_error(), std::system_category(),
                "Binding server socket", loc};
    }
}