From 3ccc1867c86aa0e21177420bba879495654b1b09 Mon Sep 17 00:00:00 2001 From: Jan200101 Date: Sun, 9 Jun 2024 20:03:14 +0200 Subject: work towards hooks --- build.zig | 4 +- src/class.zig | 96 ++++++++++-------- src/client.zig | 12 +++ src/engine.zig | 11 ++ src/gameconsole.zig | 81 +++++++++++++++ src/hook.zig | 0 src/interface.zig | 2 + src/interfaces/PluginCallbacks001.zig | 23 +++-- src/main.zig | 8 +- src/methods/execute_command.zig | 1 + src/methods/list_methods.zig | 4 +- src/northstar.zig | 8 +- src/rpc_server.zig | 171 +++++++++++++++++++++++++++++++ src/server.zig | 185 ---------------------------------- src/sys.zig | 12 ++- 15 files changed, 364 insertions(+), 254 deletions(-) create mode 100644 src/client.zig create mode 100644 src/gameconsole.zig create mode 100644 src/hook.zig create mode 100644 src/rpc_server.zig delete mode 100644 src/server.zig diff --git a/build.zig b/build.zig index ab7f300..1f4e690 100644 --- a/build.zig +++ b/build.zig @@ -10,11 +10,11 @@ pub fn build(b: *std.Build) void { }); const optimize = b.standardOptimizeOption(.{ - .preferred_optimize_mode = .Debug, + .preferred_optimize_mode = .ReleaseFast, }); const lib = b.addSharedLibrary(.{ - .name = "ZigPlugin", + .name = "SouthRPC", .root_source_file = .{ .path = "src/main.zig" }, .target = target, .optimize = optimize, diff --git a/src/class.zig b/src/class.zig index de8002e..93a0304 100644 --- a/src/class.zig +++ b/src/class.zig @@ -21,7 +21,7 @@ pub fn Class(comptime base: anytype, comptime members: anytype) type { const ClassStruct = @Type(.{ .Struct = .{ - .layout = .Extern, + .layout = .@"extern", .fields = comptime blk: { var class_fields = struct { fields: []const StructField = &[_]StructField{}, @@ -31,7 +31,7 @@ pub fn Class(comptime base: anytype, comptime members: anytype) type { .name = "vtable", .type = @Type(.{ .Struct = .{ - .layout = .Extern, + .layout = .@"extern", .fields = &[_]StructField{ field, }, @@ -52,17 +52,23 @@ pub fn Class(comptime base: anytype, comptime members: anytype) type { } else null; if (vtable_index) |i| { - const old_vtable_pointer = self.fields[i]; - const OldVTablePointerType = old_vtable_pointer.type; + const old_vtable = self.fields[i]; + const OldVTableType = old_vtable.type; + const old_vtable_type_info = @typeInfo(OldVTableType); + if (old_vtable_type_info != .Optional) { + @compileError("expected vtable to be optional, found " ++ @typeName(OldVTableType)); + } + + const OldVTablePointerType = old_vtable_type_info.Optional.child; const old_vtable_pointer_type_info = @typeInfo(OldVTablePointerType); if (old_vtable_pointer_type_info != .Pointer) { - @compileError("expected vtable to be pointer, found " ++ @typeName(OldVTablePointerType)); + @compileError("expected vtable to be optional pointer, found " ++ @typeName(OldVTableType)); } - const OldVTableType = old_vtable_pointer_type_info.Pointer.child; - const old_vtable_type_info = @typeInfo(OldVTableType); - if (old_vtable_type_info != .Struct or old_vtable_type_info.Struct.is_tuple != false) { - @compileError("expected vtable to be pointer to struct, found pointer to " ++ @typeName(OldVTableType)); + const OldVTableStructType = old_vtable_pointer_type_info.Pointer.child; + const old_vtable_struct_type_info = @typeInfo(OldVTableStructType); + if (old_vtable_struct_type_info != .Struct or old_vtable_struct_type_info.Struct.is_tuple != false) { + @compileError("expected vtable to be optional pointer to struct, found pointer to " ++ @typeName(OldVTableType)); } const FieldType = field.type; @@ -73,30 +79,34 @@ pub fn Class(comptime base: anytype, comptime members: anytype) type { self.fields = &[_]StructField{ .{ - .name = old_vtable_pointer.name, + .name = old_vtable.name, .type = @Type(.{ - .Pointer = .{ - .size = old_vtable_pointer_type_info.Pointer.size, - .is_const = old_vtable_pointer_type_info.Pointer.is_const, - .is_volatile = old_vtable_pointer_type_info.Pointer.is_volatile, - .alignment = old_vtable_pointer_type_info.Pointer.alignment, - .address_space = old_vtable_pointer_type_info.Pointer.address_space, + .Optional = .{ .child = @Type(.{ - .Struct = .{ - .layout = old_vtable_type_info.Struct.layout, - .backing_integer = old_vtable_type_info.Struct.backing_integer, - .fields = old_vtable_type_info.Struct.fields ++ field_type_info.Struct.fields, - .decls = old_vtable_type_info.Struct.decls, - .is_tuple = old_vtable_type_info.Struct.is_tuple, + .Pointer = .{ + .size = old_vtable_pointer_type_info.Pointer.size, + .is_const = old_vtable_pointer_type_info.Pointer.is_const, + .is_volatile = old_vtable_pointer_type_info.Pointer.is_volatile, + .alignment = old_vtable_pointer_type_info.Pointer.alignment, + .address_space = old_vtable_pointer_type_info.Pointer.address_space, + .child = @Type(.{ + .Struct = .{ + .layout = old_vtable_struct_type_info.Struct.layout, + .backing_integer = old_vtable_struct_type_info.Struct.backing_integer, + .fields = old_vtable_struct_type_info.Struct.fields ++ field_type_info.Struct.fields, + .decls = old_vtable_struct_type_info.Struct.decls, + .is_tuple = old_vtable_struct_type_info.Struct.is_tuple, + }, + }), + .is_allowzero = old_vtable_pointer_type_info.Pointer.is_allowzero, + .sentinel = old_vtable_pointer_type_info.Pointer.sentinel, }, }), - .is_allowzero = old_vtable_pointer_type_info.Pointer.is_allowzero, - .sentinel = old_vtable_pointer_type_info.Pointer.sentinel, }, }), - .default_value = old_vtable_pointer.default_value, - .is_comptime = old_vtable_pointer.is_comptime, - .alignment = old_vtable_pointer.alignment, + .default_value = old_vtable.default_value, + .is_comptime = old_vtable.is_comptime, + .alignment = old_vtable.alignment, }, } ++ self.fields[1..]; } else { @@ -121,22 +131,26 @@ pub fn Class(comptime base: anytype, comptime members: anytype) type { .{ .name = field.name, .type = @Type(.{ - .Pointer = .{ - .size = .One, - .is_const = true, - .is_volatile = false, - .alignment = 0, - .address_space = .generic, + .Optional = .{ .child = @Type(.{ - .Struct = .{ - .layout = .Extern, - .fields = field_type_info.Struct.fields, - .decls = &.{}, - .is_tuple = false, + .Pointer = .{ + .size = .One, + .is_const = true, + .is_volatile = false, + .alignment = 0, + .address_space = .generic, + .child = @Type(.{ + .Struct = .{ + .layout = .@"extern", + .fields = field_type_info.Struct.fields, + .decls = &.{}, + .is_tuple = false, + }, + }), + .is_allowzero = false, + .sentinel = null, }, }), - .is_allowzero = false, - .sentinel = null, }, }), .default_value = null, @@ -163,7 +177,7 @@ pub fn Class(comptime base: anytype, comptime members: anytype) type { if (b_type_info != .Struct or b_type_info.Struct.is_tuple != false) { @compileError("expected struct base, found " ++ @typeName(b)); - } else if (b_type_info.Struct.layout != .Extern) { + } else if (b_type_info.Struct.layout != .@"extern") { @compileError("expected extern struct, found " ++ @tagName(b_type_info.Struct.layout)); } diff --git a/src/client.zig b/src/client.zig new file mode 100644 index 0000000..958e6ef --- /dev/null +++ b/src/client.zig @@ -0,0 +1,12 @@ +const std = @import("std"); +const windows = std.os.windows; + +const interface = @import("interface.zig"); + +pub var handle: ?windows.HMODULE = null; + +pub var create_interface: interface.GetInterfaceType = null; + +pub fn init(module: windows.HMODULE) void { + create_interface = @ptrCast(windows.kernel32.GetProcAddress(module, "CreateInterface")); +} diff --git a/src/engine.zig b/src/engine.zig index e58c1f2..05caa11 100644 --- a/src/engine.zig +++ b/src/engine.zig @@ -1,3 +1,6 @@ +const std = @import("std"); +const windows = std.os.windows; + pub const ECommandTarget_t = enum(c_int) { CBUF_FIRST_PLAYER = 0, CBUF_LAST_PLAYER = 1, // MAX_SPLITSCREEN_CLIENTS - 1, MAX_SPLITSCREEN_CLIENTS = 2 @@ -48,3 +51,11 @@ pub const CbufType = struct { }; pub var Cbuf: ?CbufType = null; + +pub fn init(module: windows.HMODULE) void { + Cbuf = .{ + .GetCurrentPlayer = @ptrFromInt(@intFromPtr(module) + 0x120630), + .AddText = @ptrFromInt(@intFromPtr(module) + 0x1203B0), + .Execute = @ptrFromInt(@intFromPtr(module) + 0x1204B0), + }; +} diff --git a/src/gameconsole.zig b/src/gameconsole.zig new file mode 100644 index 0000000..1b7b8e8 --- /dev/null +++ b/src/gameconsole.zig @@ -0,0 +1,81 @@ +const std = @import("std"); +const windows = std.os.windows; + +const Class = @import("class.zig").Class; + +const interface = @import("interface.zig"); +const client = @import("client.zig"); + +const CGameConsole = Class(.{}, .{ + .unknown = .{ .type = void, .virtual = true }, + + .initialized = .{ .type = bool }, + .console = .{ .type = ?*const CConsoleDialog }, +}); + +const CConsoleDialog = Class(.{}, .{ + .unknown = .{ .type = void, .virtual = true }, + + .padding = .{ .type = [0x398]u8 }, + .console_panel = .{ .type = ?*const CConsolePanel }, +}); + +const CConsolePanel = Class(.{}, .{ + .editable_panel = .{ .type = EditablePanel }, + .iconsole_display_func = .{ .type = IConsoleDisplayFunc }, +}); + +const EditablePanel = Class(.{}, .{ + .unknown = .{ .type = void, .virtual = true }, + + .padding = .{ .type = [0x2B0]u8 }, +}); + +const IConsoleDisplayFunc = Class(.{}, .{ + .color_print = .{ .type = *const fn (this: *anyopaque) callconv(.C) void, .virtual = true }, + .print = .{ .type = *const fn (this: *anyopaque, message: [*:0]const u8) callconv(.C) void, .virtual = true }, + .dprint = .{ .type = *const fn (this: *anyopaque, message: [*:0]const u8) callconv(.C) void, .virtual = true }, +}); + +pub fn hook() void { + const client_create_interface = client.create_interface orelse { + std.log.err("Client CreateInterface not resolved, cannot hook game console", .{}); + return; + }; + + var status: interface.InterfaceStatus = .IFACE_OK; + const pgame_console: ?*CGameConsole = @ptrCast(@alignCast(client_create_interface("GameConsole004", &status))); + if (pgame_console == null or status != .IFACE_OK) { + std.log.err("Failed to create GameConsole004 interface: {}", .{status}); + return; + } + const game_console = pgame_console.?; + + if (!game_console.initialized) { + std.log.warn("Game console is not initialized yet, waiting for it", .{}); + while (!game_console.initialized) {} + std.log.info("Game console is now initialized", .{}); + } + + const display_func = blk: { + if (game_console.console) |console| { + if (console.console_panel) |console_panel| { + break :blk console_panel.iconsole_display_func; + } + } + + break :blk null; + }; + + const print = blk: { + if (display_func) |func| { + if (func.vtable) |vtable| { + break :blk vtable.print; + } + } + + break :blk null; + }; + + _ = print; +} diff --git a/src/hook.zig b/src/hook.zig new file mode 100644 index 0000000..e69de29 diff --git a/src/interface.zig b/src/interface.zig index 530d8cf..a952562 100644 --- a/src/interface.zig +++ b/src/interface.zig @@ -3,6 +3,8 @@ const std = @import("std"); const PluginCallbacks001 = @import("interfaces/PluginCallbacks001.zig").plugin_interface; const PluginId001 = @import("interfaces/PluginId001.zig").plugin_interface; +pub const GetInterfaceType = ?*const fn ([*:0]const u8, ?*const InterfaceStatus) callconv(.C) *anyopaque; + pub const interfaces = .{ PluginCallbacks001, PluginId001, diff --git a/src/interfaces/PluginCallbacks001.zig b/src/interfaces/PluginCallbacks001.zig index 17d687d..cd0f52c 100644 --- a/src/interfaces/PluginCallbacks001.zig +++ b/src/interfaces/PluginCallbacks001.zig @@ -1,14 +1,18 @@ const std = @import("std"); const windows = std.os.windows; +const Thread = std.Thread; + const Class = @import("../class.zig").Class; const interface = @import("../interface.zig"); const northstar = @import("../northstar.zig"); const squirrel = @import("../squirrel.zig"); const sys = @import("../sys.zig"); -const server = @import("../server.zig"); +const rpc_server = @import("../rpc_server.zig"); +const gameconsole = @import("../gameconsole.zig"); const engine = @import("../engine.zig"); +const client = @import("../client.zig"); const CSquirrelVM = squirrel.CSquirrelVM; @@ -52,11 +56,11 @@ pub fn Init(self: *anyopaque, module: windows.HMODULE, data: *northstar.Northsta sys.init(); if (reloaded != 0) { - server.stop(); + rpc_server.stop(); } - if (!server.running) { - server.start() catch std.log.err("Failed to start HTTP Server", .{}); + if (!rpc_server.running) { + rpc_server.start() catch std.log.err("Failed to start HTTP Server", .{}); } std.log.info("Loaded", .{}); @@ -69,7 +73,7 @@ pub fn Finalize(self: *anyopaque) callconv(.C) void { pub fn Unload(self: *anyopaque) callconv(.C) bool { _ = self; - server.stop(); + rpc_server.stop(); return true; } @@ -92,10 +96,11 @@ pub fn OnLibraryLoaded(self: *anyopaque, module: windows.HMODULE, name_ptr: [*:0 const name = std.mem.span(name_ptr); if (std.mem.eql(u8, name, "engine.dll")) { - engine.Cbuf = .{ - .GetCurrentPlayer = @ptrFromInt(@intFromPtr(module) + 0x120630), - .AddText = @ptrFromInt(@intFromPtr(module) + 0x1203B0), - .Execute = @ptrFromInt(@intFromPtr(module) + 0x1204B0), + engine.init(module); + } else if (std.mem.eql(u8, name, "client.dll")) { + client.init(module); + _ = Thread.spawn(.{}, gameconsole.hook, .{}) catch |err| { + std.log.err("Failed to hook GameConsole {}", .{err}); }; } } diff --git a/src/main.zig b/src/main.zig index ef5efab..3a6a8c4 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4,11 +4,9 @@ pub const PLUGIN_NAME = "Zig Plugin"; pub const LOG_NAME = "ZIGPLUGIN"; pub const DEPENDENCY_NAME = "ZigPlugin"; -pub const std_options = struct { - // Set the log level to info - pub const log_level = .info; - // Define logFn to override the std implementation - pub const logFn = @import("sys.zig").log; +pub const std_options = .{ + .logFn = @import("sys.zig").log, + .log_level = .info, }; comptime { diff --git a/src/methods/execute_command.zig b/src/methods/execute_command.zig index c253738..add17e9 100644 --- a/src/methods/execute_command.zig +++ b/src/methods/execute_command.zig @@ -41,6 +41,7 @@ fn execute_command(allocator: Allocator, params: ?std.json.Value) !?std.json.Val break :blk try allocator.dupeZ(u8, command_item.string); }; + defer allocator.free(command); if (engine.Cbuf) |Cbuf| { const cur_player = Cbuf.GetCurrentPlayer(); diff --git a/src/methods/list_methods.zig b/src/methods/list_methods.zig index b396100..dfb8a95 100644 --- a/src/methods/list_methods.zig +++ b/src/methods/list_methods.zig @@ -1,7 +1,7 @@ const std = @import("std"); -const server = @import("../server.zig"); -const RpcMethods = server.RpcMethods; +const rpc_server = @import("../rpc_server.zig"); +const RpcMethods = rpc_server.RpcMethods; const Allocator = std.mem.Allocator; diff --git a/src/northstar.zig b/src/northstar.zig index 8c659a7..10fd21b 100644 --- a/src/northstar.zig +++ b/src/northstar.zig @@ -7,13 +7,11 @@ pub const NorthstarData = extern struct { handle: ?windows.HMODULE, }; -pub var data: NorthstarData = .{ - .handle = null, -}; +pub var plugin_handle: ?windows.HMODULE = null; -pub var create_interface: ?*const fn ([*:0]const u8, ?*const interface.InterfaceStatus) callconv(.C) *anyopaque = null; +pub var create_interface: interface.GetInterfaceType = null; pub fn init(ns_module: windows.HMODULE, init_data: *NorthstarData) void { create_interface = @ptrCast(windows.kernel32.GetProcAddress(ns_module, "CreateInterface")); - data.handle = init_data.*.handle; + plugin_handle = init_data.*.handle; } diff --git a/src/rpc_server.zig b/src/rpc_server.zig new file mode 100644 index 0000000..c1868f5 --- /dev/null +++ b/src/rpc_server.zig @@ -0,0 +1,171 @@ +const std = @import("std"); + +const http = std.http; +const Server = http.Server; +const NetServer = std.net.Server; +const Thread = std.Thread; + +const max_header_size = 8192; + +pub var running = false; +var server_thread: ?Thread = null; + +var gpa = std.heap.GeneralPurposeAllocator(.{ .stack_trace_frames = 12 }){}; +var allocator = gpa.allocator(); + +const execute_command = @import("methods/execute_command.zig").method; +const list_methods = @import("methods/list_methods.zig").method; + +const server_log = std.log.scoped(.http_server); + +pub const RpcMethods = .{ + execute_command, + list_methods, +}; + +const JsonRpcRequest = struct { + jsonrpc: []const u8, + method: []const u8, + params: ?std.json.Value = null, + id: ?std.json.Value = null, +}; + +const JsonRpcError = struct { + code: isize, + message: []const u8, + data: ?std.json.Value = null, +}; + +const JsonRpcResponse = struct { + jsonrpc: []const u8, + result: ?std.json.Value = null, + @"error": ?JsonRpcError = null, + id: ?std.json.Value = null, +}; + +fn handleRequest(request: *Server.Request) !void { + server_log.info("{s} {s} {s}", .{ @tagName(request.head.method), @tagName(request.head.version), request.head.target }); + + if (!std.mem.startsWith(u8, request.head.target, "/rpc")) { + try request.respond("not found", .{ + .status = .not_found, + .extra_headers = &.{ + .{ .name = "content-type", .value = "text/plain" }, + }, + }); + return; + } + + const reader = try request.reader(); + const body = try reader.readAllAlloc(allocator, 8192); + defer allocator.free(body); + + const resp = blk: { + var response: JsonRpcResponse = .{ + .jsonrpc = "2.0", + }; + + const parsed = std.json.parseFromSlice(JsonRpcRequest, allocator, body, .{}) catch |err| { + server_log.err("Failed to parse request body {}", .{err}); + + if (@errorReturnTrace()) |trace| { + std.debug.dumpStackTrace(trace.*); + } + + response.@"error" = .{ + .code = -32700, + .message = "Parse error", + }; + + break :blk response; + }; + defer parsed.deinit(); + + var data = parsed.value; + + if (data.id) |request_id| { + if (request_id != .integer and request_id != .string) { + data.id = null; + } + } + response.id = data.id; + + if (data.params) |request_params| { + if (request_params != .object and request_params != .array) { + response.@"error" = .{ + .code = -32602, + .message = "Invalid params", + }; + + break :blk response; + } + } + + inline for (RpcMethods) |method| { + if (std.mem.eql(u8, method.name, data.method)) { + response.result = method.func(allocator, data.params) catch |err| method_blk: { + response.@"error" = .{ + .code = -32603, + .message = @errorName(err), + }; + break :method_blk null; + }; + break; + } + } else { + response.@"error" = .{ + .code = -32601, + .message = "Method not found", + }; + } + + break :blk response; + }; + + const json_resp = try std.json.stringifyAlloc(allocator, resp, .{}); + defer allocator.free(json_resp); + + try request.respond(json_resp, .{ + .extra_headers = &.{ + .{ .name = "content-type", .value = "application/json." }, + }, + }); +} +fn serverThread(addr: std.net.Address) !void { + var read_buffer: [8000]u8 = undefined; + var http_server = try addr.listen(.{}); + + accept: while (true) { + const connection = try http_server.accept(); + defer connection.stream.close(); + + var server = std.http.Server.init(connection, &read_buffer); + while (server.state == .ready) { + var request = server.receiveHead() catch |err| { + std.debug.print("error: {s}\n", .{@errorName(err)}); + continue :accept; + }; + try handleRequest(&request); + } + } +} + +pub fn start() !void { + if (server_thread == null) { + const addr = try std.net.Address.parseIp("127.0.0.1", 26505); + + running = true; + server_thread = try Thread.spawn(.{}, serverThread, .{addr}); + + server_log.info("Started HTTP Server on {}", .{addr}); + } +} + +pub fn stop() void { + if (server_thread) |thread| { + running = false; + thread.join(); + + server_log.info("Stopped HTTP Server", .{}); + } +} diff --git a/src/server.zig b/src/server.zig deleted file mode 100644 index fe44e83..0000000 --- a/src/server.zig +++ /dev/null @@ -1,185 +0,0 @@ -const std = @import("std"); - -const http = std.http; -const Server = http.Server; -const Thread = std.Thread; - -const max_header_size = 8192; - -pub var running = false; -var server_thread: ?Thread = null; - -var gpa = std.heap.GeneralPurposeAllocator(.{ .stack_trace_frames = 12 }){}; -var allocator = gpa.allocator(); - -const execute_command = @import("methods/execute_command.zig").method; -const list_methods = @import("methods/list_methods.zig").method; - -const server_log = std.log.scoped(.http_server); - -pub const RpcMethods = .{ - execute_command, - list_methods, -}; - -const JsonRpcRequest = struct { - jsonrpc: []const u8, - method: []const u8, - params: ?std.json.Value = null, - id: ?std.json.Value = null, -}; - -const JsonRpcError = struct { - code: isize, - message: []const u8, - data: ?std.json.Value = null, -}; - -const JsonRpcResponse = struct { - jsonrpc: []const u8, - result: ?std.json.Value = null, - @"error": ?JsonRpcError = null, - id: ?std.json.Value = null, -}; - -fn handleRequest(res: *Server.Response) !void { - server_log.info("{s} {s} {s}", .{ @tagName(res.request.method), @tagName(res.request.version), res.request.target }); - - if (!std.mem.startsWith(u8, res.request.target, "/rpc")) { - res.status = .not_found; - try res.do(); - return; - } - - const body = try res.reader().readAllAlloc(allocator, 8192); - defer allocator.free(body); - - const resp = blk: { - var response: JsonRpcResponse = .{ - .jsonrpc = "2.0", - }; - - const parsed = std.json.parseFromSlice(JsonRpcRequest, allocator, body, .{}) catch |err| { - server_log.err("Failed to parse request body {}", .{err}); - - if (@errorReturnTrace()) |trace| { - std.debug.dumpStackTrace(trace.*); - } - - response.@"error" = .{ - .code = -32700, - .message = "Parse error", - }; - - break :blk response; - }; - defer parsed.deinit(); - - var request = parsed.value; - - if (request.id) |request_id| { - if (request_id != .integer and request_id != .string) { - request.id = null; - } - } - response.id = request.id; - - if (request.params) |request_params| { - if (request_params != .object and request_params != .array) { - response.@"error" = .{ - .code = -32602, - .message = "Invalid params", - }; - - break :blk response; - } - } - - inline for (RpcMethods) |method| { - if (std.mem.eql(u8, method.name, request.method)) { - response.result = method.func(allocator, request.params) catch |err| method_blk: { - response.@"error" = .{ - .code = -32603, - .message = @errorName(err), - }; - break :method_blk null; - }; - break; - } - } else { - response.@"error" = .{ - .code = -32601, - .message = "Method not found", - }; - } - - break :blk response; - }; - - const json_resp = try std.json.stringifyAlloc(allocator, resp, .{}); - res.transfer_encoding = .{ .content_length = json_resp.len }; - - try res.do(); - try res.writeAll(json_resp); - try res.finish(); -} - -fn runServer(srv: *Server) !void { - outer: while (running) { - var res = try srv.accept(.{ - .allocator = allocator, - .header_strategy = .{ .dynamic = max_header_size }, - }); - defer res.deinit(); - - while (res.reset() != .closing) { - res.wait() catch |err| switch (err) { - error.HttpHeadersInvalid => continue :outer, - error.EndOfStream => continue, - else => return err, - }; - - try handleRequest(&res); - } - } -} - -fn serverThread(addr: std.net.Address) !void { - var server = Server.init(allocator, .{ .reuse_address = true }); - - try server.listen(addr); - defer server.deinit(); - - defer _ = gpa.deinit(); - - runServer(&server) catch |err| { - server_log.err("server error: {}\n", .{err}); - - if (@errorReturnTrace()) |trace| { - std.debug.dumpStackTrace(trace.*); - } - - _ = gpa.deinit(); - std.os.exit(1); - }; -} - -pub fn start() !void { - if (server_thread == null) { - var addr = try std.net.Address.parseIp("127.0.0.1", 26505); - - running = true; - server_thread = try Thread.spawn(.{}, serverThread, .{addr}); - - server_log.info("Started HTTP Server on {}", .{addr}); - } -} - -pub fn stop() void { - if (server_thread) |thread| { - running = false; - thread.join(); - - server_log.info("Stopped HTTP Server", .{}); - } -} diff --git a/src/sys.zig b/src/sys.zig index ea71ec2..6cb0864 100644 --- a/src/sys.zig +++ b/src/sys.zig @@ -37,7 +37,7 @@ pub fn log( ) void { const scope_prefix = switch (scope) { std.log.default_log_scope => "", - else => "(" ++ @tagName(scope) ++ ")", + else => "(" ++ @tagName(scope) ++ ") ", }; var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); @@ -55,9 +55,11 @@ pub fn log( const msg = std.fmt.allocPrintZ(allocator, scope_prefix ++ format, args) catch unreachable; if (sys) |s| { - s.vtable.log(s, northstar.data.handle, log_level, msg); - } else { - // Northstar log has not been established, fallback to default log - std.log.defaultLog(level, scope, format, args); + if (s.vtable) |vtable| { + vtable.log(s, northstar.plugin_handle, log_level, msg); + return; + } } + + std.log.defaultLog(level, scope, format, args); } -- cgit v1.2.3