main.zig
const std = @import("std");
const c = @import("c");
const Io = std.Io;
const crypto = std.crypto;
const Parser = @import("bencode.zig").Parser;
const Server = @import("tracker_client.zig").Server;
var global_server_ptr: ?*Server = null;
pub var should_run: std.atomic.Value(bool) = .init(true);
pub fn interrupt_handler(sig: std.os.linux.SIG) callconv(.c) void {
switch (sig) {
.INT => {
should_run.store(false, .release);
if (global_server_ptr) |srv| {
// Forcefully stop the server right now!
srv.listener.socket.close(srv.io);
}
},
else => {},
}
}
pub fn main(init: std.process.Init) !void {
// var debug_allocator = std.heap.DebugAllocator(.{ .thread_safe = true }).init;
// defer _ = debug_allocator.deinit();
// const allocator = debug_allocator.allocator();
const allocator = init.gpa;
const io = init.io;
const file = try std.Io.Dir.openFile(.cwd(), io, "aapun.txt.torrent", .{});
defer file.close(io);
const file_stats = try file.stat(io);
const file_size: usize = file_stats.size;
var file_reader_buffer: [512]u8 = undefined;
var file_reader = file.reader(io, &file_reader_buffer);
var file_iface = &file_reader.interface;
const buffer = try allocator.alloc(u8, file_size);
defer allocator.free(buffer);
file_iface.readSliceAll(buffer) catch |err| switch (err) {
error.ReadFailed => {
return error.ReadFailed;
},
error.EndOfStream => {
return error.EndOfStream;
},
};
std.debug.print("\n\n{s}'\n\n", .{buffer});
var parser = Parser.init(buffer, allocator);
var value = try parser.parse_value();
defer Parser.deinit(&value, allocator);
Parser.print_value(value);
if (Parser.get_dict_val(value.data.dict, "info")) |val| {
const info_bytes = buffer[val.span.start..val.span.end];
// std.debug.print("\nRaw Info Dictonary: \n{s}\n", .{info_bytes});
var hash: [32]u8 = undefined;
crypto.hash.sha2.Sha256.hash(info_bytes, &hash, .{});
std.debug.print("\nInfo Hash: \n", .{});
for (hash) |byte| {
std.debug.print("{x:0>2}", .{byte});
}
std.debug.print("\n", .{});
}
const sigaction: std.os.linux.Sigaction = .{
.flags = 0,
.handler = .{
.handler = &interrupt_handler,
},
.mask = @splat(0),
};
switch (std.os.linux.errno(std.os.linux.sigaction(.INT, &sigaction, null))) {
.SUCCESS => {}, // no error
else => |err| std.log.err("{t}: Sigaction interrupt handler failure!", .{err}),
}
var server = try Server.init("127.0.0.1", 9001, allocator, io);
defer server.deinit();
global_server_ptr = &server;
defer global_server_ptr = null;
try server.accept_connection();
}
and server module
const std = @import("std");
const c = @import("c");
const main_root = @import("main.zig");
pub const Server = struct {
allocator: std.mem.Allocator,
io: std.Io,
listener: std.Io.net.Server,
group: std.Io.Group,
pub fn init(server_url: []const u8, port: u16, allocator: std.mem.Allocator, io: std.Io) !Server {
const ip_addr = try std.Io.net.IpAddress.parse(server_url, port);
return .{
.allocator = allocator,
.io = io,
.listener = try ip_addr.listen(io, .{ .reuse_address = true }),
.group = .init,
};
}
pub fn deinit(self: *Server) void {
std.debug.print("\n\nServer Shutdown Gracefully.\n\n", .{});
self.group.cancel(self.io);
self.group.await(self.io) catch |err| {
std.debug.print("Group await error: {}\n", .{err});
};
}
pub fn accept_connection(self: *Server) !void {
while (main_root.should_run.load(.acquire)) {
const client_stream = self.listener.accept(self.io) catch |err| {
if (!main_root.should_run.load(.monotonic)) break;
std.debug.print("Accept error: {}\n", .{err});
continue;
};
self.group.concurrent(self.io, handle_client, .{ self.io, self.allocator, client_stream }) catch |err| {
std.debug.print("Failed to spawn background task: {}\n", .{err});
client_stream.close(self.io);
continue;
};
}
self.listener.socket.close(self.io);
std.debug.print("\nServer loop exited.", .{});
}
fn handle_client(io: std.Io, allocator: std.mem.Allocator, stream: std.Io.net.Stream) std.Io.Cancelable!void {
defer stream.close(io);
_ = allocator.alloc(u8, 4096) catch |err| {
std.debug.print("err: {any}", .{err});
return std.Io.Cancelable.Canceled;
};
_ = allocator.alloc(u8, 4096) catch |err| {
std.debug.print("err: {any}", .{err});
return std.Io.Cancelable.Canceled;
};
// _ = allocator.alloc(u8, 4096) catch |err| {
// std.debug.print("err: {any}", .{err});
// return std.Io.Cancelable.Canceled;
// };
// _ = allocator.alloc(u8, 4096) catch |err| {
// std.debug.print("err: {any}", .{err});
// return std.Io.Cancelable.Canceled;
// };
// _ = allocator.alloc(u8, 4096) catch |err| {
// std.debug.print("err: {any}", .{err});
// return std.Io.Cancelable.Canceled;
// };
std.debug.print("New Client Connectedddd {any}:{d}\n", .{
stream.socket.address.ip4.bytes, stream.socket.address.ip4.port,
});
// TODO:
// Read request
// Process request
// Write response
io.sleep(std.Io.Duration.fromSeconds(5), .awake) catch |err| {
std.debug.print("Task sleep interrupted or canceled: {}\n", .{err});
return;
};
std.debug.print("Client Disconnected\n", .{});
std.debug.print("Client Disconnected cleanly\n", .{});
}
};