first
This commit is contained in:
commit
02c1648823
6 changed files with 322 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
/zig-out
|
||||||
|
/.zig-cache
|
||||||
|
/result*
|
||||||
|
|
34
build.zig
Normal file
34
build.zig
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
// We will also create a module for our other entry point, 'main.zig'.
|
||||||
|
const exe_mod = b.createModule(.{
|
||||||
|
.root_source_file = b.path("src/main.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = "zig_logging_wrapper",
|
||||||
|
.root_module = exe_mod,
|
||||||
|
});
|
||||||
|
b.installArtifact(exe);
|
||||||
|
const run_cmd = b.addRunArtifact(exe);
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
if (b.args) |args| {
|
||||||
|
run_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
const run_step = b.step("run", "Run the app");
|
||||||
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
|
||||||
|
const exe_unit_tests = b.addTest(.{
|
||||||
|
.root_module = exe_mod,
|
||||||
|
});
|
||||||
|
|
||||||
|
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
|
||||||
|
const test_step = b.step("test", "Run unit tests");
|
||||||
|
test_step.dependOn(&run_exe_unit_tests.step);
|
||||||
|
}
|
14
build.zig.zon
Normal file
14
build.zig.zon
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
.{
|
||||||
|
.name = .zig_logging_wrapper,
|
||||||
|
|
||||||
|
.version = "0.0.0",
|
||||||
|
.fingerprint = 0x259c6307f5768bcf, // Changing this has security and trust implications.
|
||||||
|
.minimum_zig_version = "0.14.0",
|
||||||
|
.dependencies = .{},
|
||||||
|
|
||||||
|
.paths = .{
|
||||||
|
"build.zig",
|
||||||
|
"build.zig.zon",
|
||||||
|
"src",
|
||||||
|
},
|
||||||
|
}
|
56
src/PipeReader.zig
Normal file
56
src/PipeReader.zig
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
const PipeReader = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const log = std.log.scoped(.pipe_reader);
|
||||||
|
|
||||||
|
spawn_failed: *bool,
|
||||||
|
exe: *std.process.Child,
|
||||||
|
pipe: enum { stdout, stderr },
|
||||||
|
sem: std.Thread.Semaphore = .{},
|
||||||
|
thread: std.Thread = undefined,
|
||||||
|
|
||||||
|
pub fn spawn(self: *PipeReader) !void {
|
||||||
|
self.thread = try std.Thread.spawn(.{}, reader, .{self});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wait(self: *PipeReader) void {
|
||||||
|
self.sem.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn join(self: *PipeReader) void {
|
||||||
|
self.thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reader(self: *PipeReader) void {
|
||||||
|
const fd: std.fs.File = fd: {
|
||||||
|
defer self.sem.post();
|
||||||
|
while (!self.spawn_failed.*) {
|
||||||
|
switch (self.pipe) {
|
||||||
|
.stdout => if (self.exe.stdout) |fd| break :fd fd,
|
||||||
|
.stderr => if (self.exe.stderr) |fd| break :fd fd,
|
||||||
|
}
|
||||||
|
std.time.sleep(10 * std.time.ns_per_ms);
|
||||||
|
}
|
||||||
|
self.sem.post();
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
const rdr = fd.reader();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
var buf: [2048]u8 = undefined;
|
||||||
|
const line = rdr.readUntilDelimiter(&buf, '\n') catch |err| switch (err) {
|
||||||
|
error.EndOfStream => return,
|
||||||
|
error.NotOpenForReading => return,
|
||||||
|
else => {
|
||||||
|
log.err("{s}: {}", .{ @tagName(self.pipe), err });
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const ts = std.time.nanoTimestamp();
|
||||||
|
if (line.len == 0) break;
|
||||||
|
log.info("{s}: {d} {s}", .{ @tagName(self.pipe), ts, line });
|
||||||
|
}
|
||||||
|
}
|
129
src/UnixSocketReader.zig
Normal file
129
src/UnixSocketReader.zig
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
const UnixSocketReader = @This();
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const log = std.log.scoped(.socket_reader);
|
||||||
|
|
||||||
|
sock: std.posix.socket_t = undefined,
|
||||||
|
thread: std.Thread = undefined,
|
||||||
|
|
||||||
|
pub fn init(self: *UnixSocketReader) !void {
|
||||||
|
const path: []const u8 = "/tmp/wrapper";
|
||||||
|
|
||||||
|
var dir = try std.fs.openDirAbsolute(std.fs.path.dirname(path) orelse return error.BadPath, .{});
|
||||||
|
defer dir.close();
|
||||||
|
dir.deleteFile(std.fs.path.basename(path)) catch {};
|
||||||
|
|
||||||
|
var addr = std.mem.zeroes(std.posix.sockaddr.un);
|
||||||
|
addr.family = std.posix.AF.UNIX;
|
||||||
|
@memcpy(addr.path[0..path.len], path);
|
||||||
|
|
||||||
|
self.sock = try std.posix.socket(std.posix.AF.UNIX, std.posix.SOCK.DGRAM, 0);
|
||||||
|
try std.posix.bind(self.sock, @ptrCast(&addr), @sizeOf(std.posix.sockaddr.un));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn(self: *UnixSocketReader) !void {
|
||||||
|
self.thread = try std.Thread.spawn(.{}, reader, .{self});
|
||||||
|
self.thread.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Severity = enum(u8) {
|
||||||
|
EMERG = 0,
|
||||||
|
ALERT = 1,
|
||||||
|
CRIT = 2,
|
||||||
|
ERR = 3,
|
||||||
|
WARNING = 4,
|
||||||
|
NOTICE = 5,
|
||||||
|
INFO = 6,
|
||||||
|
DEBUG = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Facility = enum(u8) {
|
||||||
|
KERN = (0 << 3),
|
||||||
|
USER = (1 << 3),
|
||||||
|
MAIL = (2 << 3),
|
||||||
|
DAEMON = (3 << 3),
|
||||||
|
AUTH = (4 << 3),
|
||||||
|
SYSLOG = (5 << 3),
|
||||||
|
LPR = (6 << 3),
|
||||||
|
NEWS = (7 << 3),
|
||||||
|
UUCP = (8 << 3),
|
||||||
|
CRON = (9 << 3),
|
||||||
|
AUTHPRIV = (10 << 3),
|
||||||
|
FTP = (11 << 3),
|
||||||
|
NTP = (12 << 3),
|
||||||
|
AUDIT = (13 << 3),
|
||||||
|
ALERT = (14 << 3),
|
||||||
|
CLOCK = (15 << 3),
|
||||||
|
LOCAL0 = (16 << 3),
|
||||||
|
LOCAL1 = (17 << 3),
|
||||||
|
LOCAL2 = (18 << 3),
|
||||||
|
LOCAL3 = (19 << 3),
|
||||||
|
LOCAL4 = (20 << 3),
|
||||||
|
LOCAL5 = (21 << 3),
|
||||||
|
LOCAL6 = (22 << 3),
|
||||||
|
LOCAL7 = (23 << 3),
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn reader(self: *UnixSocketReader) void {
|
||||||
|
while (true) {
|
||||||
|
var buf: [4096]u8 = undefined;
|
||||||
|
var addr: std.posix.sockaddr = undefined;
|
||||||
|
var addrlen: std.posix.socklen_t = @sizeOf(std.posix.sockaddr);
|
||||||
|
const len = std.posix.recvfrom(self.sock, &buf, 0, &addr, &addrlen) catch |err| {
|
||||||
|
log.info("socket reader: {}", .{err});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
const line = buf[0..len];
|
||||||
|
syslog: {
|
||||||
|
if (line.len < 3) {
|
||||||
|
log.warn("data too short to be syslog", .{});
|
||||||
|
break :syslog;
|
||||||
|
}
|
||||||
|
const pri_start_char = std.mem.indexOfScalar(u8, line, '<') orelse {
|
||||||
|
log.warn("syslog: can't find '<'", .{});
|
||||||
|
break :syslog;
|
||||||
|
};
|
||||||
|
if (pri_start_char != 0) {
|
||||||
|
log.warn("syslog does not start with '<'", .{});
|
||||||
|
break :syslog;
|
||||||
|
}
|
||||||
|
const pri_end_char = std.mem.indexOfScalarPos(u8, line, 1, '>') orelse {
|
||||||
|
log.warn("syslog: can't find '>'", .{});
|
||||||
|
break :syslog;
|
||||||
|
};
|
||||||
|
const pri = line[pri_start_char + 1 .. pri_end_char];
|
||||||
|
if (pri.len < 1) {
|
||||||
|
log.warn("pri is too short", .{});
|
||||||
|
break :syslog;
|
||||||
|
}
|
||||||
|
if (pri.len > 3) {
|
||||||
|
log.warn("pri is too long", .{});
|
||||||
|
break :syslog;
|
||||||
|
}
|
||||||
|
for (pri) |ch| if (!std.ascii.isDigit(ch)) {
|
||||||
|
log.warn("invalid digit in pri", .{});
|
||||||
|
break :syslog;
|
||||||
|
};
|
||||||
|
const prival = std.fmt.parseUnsigned(u8, pri, 10) catch |err| {
|
||||||
|
log.warn("unable to convert pri to an integer: {}", .{err});
|
||||||
|
break :syslog;
|
||||||
|
};
|
||||||
|
if (prival > 191) {
|
||||||
|
log.warn("prival out of range", .{});
|
||||||
|
break :syslog;
|
||||||
|
}
|
||||||
|
log.info("pri: \'{s}\'", .{pri});
|
||||||
|
const severity = std.meta.intToEnum(Severity, prival & 0b00000111) catch {
|
||||||
|
log.warn("unable to convert severity", .{});
|
||||||
|
break :syslog;
|
||||||
|
};
|
||||||
|
const facility = std.meta.intToEnum(Facility, prival & 0b11111000) catch {
|
||||||
|
log.warn("unable to convert facility", .{});
|
||||||
|
break :syslog;
|
||||||
|
};
|
||||||
|
log.info("severity: {s} facility: {s}", .{ @tagName(severity), @tagName(facility) });
|
||||||
|
}
|
||||||
|
log.info("{s}", .{line});
|
||||||
|
}
|
||||||
|
}
|
85
src/main.zig
Normal file
85
src/main.zig
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
|
const log = std.log.scoped(.wrapper);
|
||||||
|
|
||||||
|
const PipeReader = @import("PipeReader.zig");
|
||||||
|
const SocketReader = @import("SocketReader.zig");
|
||||||
|
|
||||||
|
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
const alloc, const is_debug = switch (builtin.mode) {
|
||||||
|
.Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true },
|
||||||
|
.ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false },
|
||||||
|
};
|
||||||
|
defer if (is_debug) {
|
||||||
|
_ = debug_allocator.deinit();
|
||||||
|
};
|
||||||
|
|
||||||
|
var socket: SocketReader = .{};
|
||||||
|
try socket.init();
|
||||||
|
try socket.spawn();
|
||||||
|
|
||||||
|
var exe = std.process.Child.init(
|
||||||
|
&.{
|
||||||
|
"logger",
|
||||||
|
"--stderr",
|
||||||
|
"--unix=/tmp/wrapper",
|
||||||
|
"hello",
|
||||||
|
},
|
||||||
|
alloc,
|
||||||
|
);
|
||||||
|
|
||||||
|
exe.stdin_behavior = .Ignore;
|
||||||
|
exe.stdout_behavior = .Pipe;
|
||||||
|
exe.stderr_behavior = .Pipe;
|
||||||
|
|
||||||
|
var spawn_failed: bool = false;
|
||||||
|
|
||||||
|
var stdout: PipeReader = .{
|
||||||
|
.spawn_failed = &spawn_failed,
|
||||||
|
.exe = &exe,
|
||||||
|
.pipe = .stdout,
|
||||||
|
};
|
||||||
|
try stdout.spawn();
|
||||||
|
defer stdout.join();
|
||||||
|
|
||||||
|
var stderr: PipeReader = .{
|
||||||
|
.spawn_failed = &spawn_failed,
|
||||||
|
.exe = &exe,
|
||||||
|
.pipe = .stderr,
|
||||||
|
};
|
||||||
|
try stderr.spawn();
|
||||||
|
defer stderr.join();
|
||||||
|
|
||||||
|
exe.spawn() catch |err| {
|
||||||
|
log.err("err: {}", .{err});
|
||||||
|
spawn_failed = true;
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
stdout.wait();
|
||||||
|
stderr.wait();
|
||||||
|
|
||||||
|
const rc = try exe.wait();
|
||||||
|
|
||||||
|
switch (rc) {
|
||||||
|
.Exited => |code| {
|
||||||
|
if (code != 0) {
|
||||||
|
log.warn("exited with error code {d}", .{code});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Signal => |signal| {
|
||||||
|
log.warn("terminated with signal {}", .{signal});
|
||||||
|
},
|
||||||
|
.Stopped => |signal| {
|
||||||
|
log.warn("stopped with signal {}", .{signal});
|
||||||
|
},
|
||||||
|
.Unknown => |code| {
|
||||||
|
log.warn("unknown error {}", .{code});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("finished!", .{});
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue