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