Thanks everyone for helping me out with this. It took me a while to understand: I don’t need errno
if I simply stick to the functions from posix
module. It returns all the errors nicely (as named enums). Here’s the re-written version of the script (leaving it here for reference):
const std = @import("std");
const posix = std.posix;
const os = std.os.linux;
const io = std.io;
const mem = std.mem;
const net = std.net;
const time = std.time;
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
fn debug_print(text: []const u8) void {
const head = "[Debug] ";
var buff: []u8 = allocator.alloc(u8, text.len + head.*.len) catch {
return;
};
defer allocator.free(buff);
@memcpy(
buff[0..head.*.len],
head[0..head.*.len],
);
@memcpy(
buff[head.*.len .. head.*.len + text.len],
text[0..],
);
_ = io.getStdOut().writer().write(buff) catch |e| {
std.debug.print(
"Error in debug print: {any}\n",
.{e},
);
};
}
fn handle_connection(
sock: posix.fd_t,
ev: *os.epoll_event,
addr: net.Address,
epollfd: usize,
) !*posix.socket_t {
var client_addr_len = addr.getOsSockLen();
var conn_sock = posix.accept(
sock,
@constCast(&addr.any),
&client_addr_len,
posix.SOCK.NONBLOCK,
) catch |accept_error| {
switch (accept_error) {
error.WouldBlock => {
debug_print("Would block :(\n");
return @constCast(&@as(i32, -4));
},
else => {
debug_print("Some error while handling connection\n");
std.debug.print(
"Connection error: {any}\n",
.{accept_error},
);
return @constCast(&@as(i32, -1));
},
}
};
_ = posix.fcntl(conn_sock, posix.F.SETFL, posix.SOCK.NONBLOCK) catch |fcntl_error| {
switch (fcntl_error) {
error.DeadLock => {
debug_print("Threads in deadlocked :(\n");
},
else => {
debug_print("Some error while making socket non-block\n");
std.debug.print(
"Connection error: {any}\n",
.{fcntl_error},
);
},
}
return fcntl_error;
};
ev.*.events = os.EPOLL.IN | os.EPOLL.ET;
ev.*.data.fd = conn_sock;
_ = posix.epoll_ctl(
@intCast(epollfd),
os.EPOLL.CTL_ADD,
conn_sock,
ev,
) catch |epolladd_error| {
switch (epolladd_error) {
error.FileDescriptorIncompatibleWithEpoll => {
debug_print("Bad FD for epoll.\n");
},
else => {
std.debug.print(
"Some other error: {any}\n",
.{epolladd_error},
);
},
}
return epolladd_error;
};
// send the intro text
const buff: []const u8 = "Hi there, welcome to our server!\n";
const bytes_written = try posix.write(
conn_sock,
buff,
);
std.debug.print(
"New connection: {any}\n",
.{conn_sock},
);
_ = bytes_written;
return &conn_sock;
}
pub fn start_poll() !void {
defer _ = gpa.deinit();
debug_print("Starting event loop\n");
var addr = net.Address.initIp4(
[4]u8{ 127, 0, 0, 1 },
19690,
);
const listen_sock = try posix.socket(
addr.any.family,
posix.SOCK.STREAM,
posix.IPPROTO.TCP,
);
const len = addr.getOsSockLen();
posix.bind(
listen_sock,
&addr.any,
len,
) catch |bind_error| {
switch (bind_error) {
error.AccessDenied, error.AddressInUse, error.AlreadyBound => {
debug_print("Access denied or addres not free\n");
},
else => {
debug_print("Some other error\n");
},
}
return bind_error;
};
debug_print("Listening socket bound\n");
posix.listen(listen_sock, 2) catch |listen_error| {
switch (listen_error) {
else => {
std.debug.print(
"Error while listening to listen_sock: {any}\n",
.{listen_error},
);
},
}
return listen_error;
};
const epollfd = posix.epoll_create1(0) catch |epoll_error| {
return epoll_error;
};
debug_print("Epoll initialized\n");
var events: [10]os.epoll_event = undefined;
var ev: os.epoll_event = undefined;
ev.events |= posix.POLL.IN;
ev.data.fd = listen_sock;
_ = posix.epoll_ctl(
epollfd,
@intCast(os.EPOLL.CTL_ADD),
listen_sock,
&ev,
) catch |epolladd_error| {
return epolladd_error;
};
debug_print("Listening sock added to epoll\n");
while (true) {
const nfds = posix.epoll_wait(
@intCast(epollfd),
&events,
-1,
);
for (0..nfds) |n| {
if (events[n].data.fd == listen_sock) {
const conn = try handle_connection(
listen_sock,
&events[n],
addr,
@intCast(epollfd),
);
_ = conn;
}
if (events[n].events & os.EPOLL.IN != 0) {
debug_print("Got message; TODO: handle it\n");
}
}
}
}