solar_hackers/0_smoketest/main.cpp

97 lines
3.3 KiB
C++

//
// Created by bspeice on 10/5/24.
//
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <unistd.h>
#include <array>
#include <iostream>
#include <stdexcept>
#include <vector>
#define CHECK_SYSCALL(expr) [&](){ const auto ret = expr; if (ret < 0) { throw std::runtime_error(#expr); } else { return ret; } }()
int main(int argc, char** argv) {
if (argc != 2) {
throw std::runtime_error("Usage: ./0_smoketest <interface>");
}
// Get the address tied to our intended interface
const auto interface = std::string_view(argv[1]);
ifaddrs* interface_addrs{};
CHECK_SYSCALL(getifaddrs(&interface_addrs));
while (interface_addrs) {
if (interface == interface_addrs->ifa_name && interface_addrs->ifa_addr->sa_family == AF_INET) {
break;
}
interface_addrs = interface_addrs->ifa_next;
}
if (!interface_addrs) {
throw std::runtime_error("Expected interface not found");
}
std::array<char, 1024> name_buffer{};
getnameinfo(interface_addrs->ifa_addr, sizeof(sockaddr_in), name_buffer.data(), name_buffer.size(), nullptr, 0, NI_NUMERICHOST);
auto* address = reinterpret_cast<sockaddr_in*>(interface_addrs->ifa_addr);
address->sin_port = htons(3490); // Port must be in network byte order
std::cout << "Address: " << std::string(name_buffer.data()) << ' ' << ntohs(address->sin_port) << '\n';
// Set up a socket
const auto socket_fd = CHECK_SYSCALL(socket(AF_INET, SOCK_STREAM, 0));
int enable = 1;
setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
CHECK_SYSCALL(bind(socket_fd, reinterpret_cast<sockaddr*>(address), sizeof(sockaddr_in)));
CHECK_SYSCALL(listen(socket_fd, 10));
// Set up epoll
const auto epoll_fd = CHECK_SYSCALL(epoll_create(10));
epoll_event event{EPOLLIN};
event.data.fd = socket_fd;
CHECK_SYSCALL(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &event));
std::vector<epoll_event> events{10};
std::vector<std::byte> buffer(1024);
while (true) {
std::cout << "Waiting for events..." << '\n';
const auto num_fds = CHECK_SYSCALL(epoll_wait(epoll_fd, events.data(), events.size(), -1));
std::cout << "Received events=" << num_fds << '\n';
for (auto i = 0; i < num_fds; i++) {
if (events[i].data.fd == socket_fd) {
// Connection attempt; add to the interest list and continue
const auto connection_fd = CHECK_SYSCALL(accept(socket_fd, nullptr, nullptr));
event.data.fd = connection_fd;
CHECK_SYSCALL(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, connection_fd, &event));
std::cout << "Adding connection=" << connection_fd << '\n';
continue;
}
// Received data on a connection socket, read it and send it back
const auto bytes_read = CHECK_SYSCALL(read(events[i].data.fd, buffer.data(), buffer.size()));
std::cout << "Received bytes=" << bytes_read << '\n';
if (bytes_read == 0) {
// Connection closed
std::cout << "Closing fd=" << events[i].data.fd << '\n';
CHECK_SYSCALL(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, &event));
CHECK_SYSCALL(close(events[i].data.fd));
continue;
}
// Blocking I/O, I know, it's bad
std::cout << "Sending bytes=" << bytes_read << '\n';
CHECK_SYSCALL(write(events[i].data.fd, buffer.data(), bytes_read));
}
}
}