first
This commit is contained in:
commit
92a83ea883
6 changed files with 305 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/zig-cache
|
||||||
|
/zig-out
|
24
build.zig
Normal file
24
build.zig
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
_ = b.addModule("journalz", .{
|
||||||
|
.root_source_file = .{ .path = "src/root.zig" },
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const lib_unit_tests = b.addTest(.{
|
||||||
|
.root_source_file = .{ .path = "src/root.zig" },
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
|
||||||
|
|
||||||
|
const test_step = b.step("test", "Run unit tests");
|
||||||
|
test_step.dependOn(&run_lib_unit_tests.step);
|
||||||
|
}
|
10
build.zig.zon
Normal file
10
build.zig.zon
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
.{
|
||||||
|
.name = "journalz",
|
||||||
|
.version = "0.0.0",
|
||||||
|
|
||||||
|
.dependencies = .{},
|
||||||
|
|
||||||
|
.paths = .{
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
}
|
47
src/cmsghdr.zig
Normal file
47
src/cmsghdr.zig
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
// "Borrowed" from https://github.com/tupleapp/tuple-launch/blob/master/cmsghdr.zig
|
||||||
|
|
||||||
|
pub fn cmsghdr(comptime T: type) type {
|
||||||
|
const Header = extern struct {
|
||||||
|
len: usize,
|
||||||
|
level: c_int,
|
||||||
|
type: c_int,
|
||||||
|
};
|
||||||
|
|
||||||
|
const data_align = @sizeOf(usize);
|
||||||
|
const data_offset = std.mem.alignForward(usize, @sizeOf(Header), data_align);
|
||||||
|
|
||||||
|
return extern struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
bytes: [data_offset + @sizeOf(T)]u8 align(@alignOf(Header)),
|
||||||
|
|
||||||
|
pub fn init(args: struct {
|
||||||
|
level: c_int,
|
||||||
|
type: c_int,
|
||||||
|
data: T,
|
||||||
|
}) Self {
|
||||||
|
var self: Self = undefined;
|
||||||
|
self.headerPtr().* = .{
|
||||||
|
.len = data_offset + @sizeOf(T),
|
||||||
|
.level = args.level,
|
||||||
|
.type = args.type,
|
||||||
|
};
|
||||||
|
self.dataPtr().* = args.data;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn headerPtr(self: *Self) *Header {
|
||||||
|
return @as(*Header, @ptrCast(self));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dataPtr(self: *Self) *T {
|
||||||
|
return @as(*T, @alignCast(@ptrCast(self.bytes[data_offset..])));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
std.testing.refAllDecls(cmsghdr([3]std.os.fd_t));
|
||||||
|
}
|
24
src/main.zig
Normal file
24
src/main.zig
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
// Prints to stderr (it's a shortcut based on `std.io.getStdErr()`)
|
||||||
|
std.debug.print("All your {s} are belong to us.\n", .{"codebase"});
|
||||||
|
|
||||||
|
// stdout is for the actual output of your application, for example if you
|
||||||
|
// are implementing gzip, then only the compressed bytes should be sent to
|
||||||
|
// stdout, not any debugging messages.
|
||||||
|
const stdout_file = std.io.getStdOut().writer();
|
||||||
|
var bw = std.io.bufferedWriter(stdout_file);
|
||||||
|
const stdout = bw.writer();
|
||||||
|
|
||||||
|
try stdout.print("Run `zig build test` to run the tests.\n", .{});
|
||||||
|
|
||||||
|
try bw.flush(); // don't forget to flush!
|
||||||
|
}
|
||||||
|
|
||||||
|
test "simple test" {
|
||||||
|
var list = std.ArrayList(i32).init(std.testing.allocator);
|
||||||
|
defer list.deinit(); // try commenting this out and see if zig detects the memory leak!
|
||||||
|
try list.append(42);
|
||||||
|
try std.testing.expectEqual(@as(i32, 42), list.pop());
|
||||||
|
}
|
198
src/root.zig
Normal file
198
src/root.zig
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const cmsghdr = @import("cmsghdr.zig");
|
||||||
|
|
||||||
|
pub const Facility = enum(u5) {
|
||||||
|
KERN = 0,
|
||||||
|
USER = 1,
|
||||||
|
MAIL = 2,
|
||||||
|
DAEMON = 3,
|
||||||
|
AUTH = 4,
|
||||||
|
SYSLOG = 5,
|
||||||
|
LPR = 6,
|
||||||
|
NEWS = 7,
|
||||||
|
UUCP = 8,
|
||||||
|
CRON = 9,
|
||||||
|
AUTHPRIV = 10,
|
||||||
|
FTP = 11,
|
||||||
|
LOCAL0 = 16,
|
||||||
|
LOCAL1 = 17,
|
||||||
|
LOCAL2 = 18,
|
||||||
|
LOCAL3 = 19,
|
||||||
|
LOCAL4 = 20,
|
||||||
|
LOCAL5 = 21,
|
||||||
|
LOCAL6 = 22,
|
||||||
|
LOCAL7 = 23,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Priority = enum(u3) {
|
||||||
|
EMERG = 0,
|
||||||
|
ALERT = 1,
|
||||||
|
CRIT = 2,
|
||||||
|
ERR = 3,
|
||||||
|
WARNING = 4,
|
||||||
|
NOTICE = 5,
|
||||||
|
INFO = 6,
|
||||||
|
DEBUG = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
const JOURNAL_SOCKET: []const u8 = "/run/systemd/journal/socket";
|
||||||
|
|
||||||
|
const SCM_RIGHTS = 1;
|
||||||
|
|
||||||
|
const F_ADD_SEALS = 1033;
|
||||||
|
|
||||||
|
const F_SEAL_SEAL = 0x0001;
|
||||||
|
const F_SEAL_SHRINK = 0x0002;
|
||||||
|
const F_SEAL_GROW = 0x0004;
|
||||||
|
const F_SEAL_WRITE = 0x0008;
|
||||||
|
const F_SEAL_FUTURE_WRITE = 0x0010;
|
||||||
|
const F_SEAL_EXEC = 0x0020;
|
||||||
|
|
||||||
|
const MFD_NOEXEC_SEAL = 0x0008;
|
||||||
|
|
||||||
|
pub const Logger = struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
identifier: ?[]const u8,
|
||||||
|
socket: std.os.socket_t,
|
||||||
|
path: std.os.sockaddr.un = undefined,
|
||||||
|
|
||||||
|
pub fn init(identifier: ?[]const u8) !Logger {
|
||||||
|
var logger = Logger{
|
||||||
|
.identifier = identifier,
|
||||||
|
.socket = try std.os.socket(std.os.AF.UNIX, std.os.SOCK.DGRAM | std.os.SOCK.CLOEXEC, 0),
|
||||||
|
};
|
||||||
|
errdefer std.os.close(logger.socket);
|
||||||
|
|
||||||
|
logger.path.family = std.os.AF.UNIX;
|
||||||
|
@memset(logger.path.path[0..logger.path.path.len], 0);
|
||||||
|
@memcpy(logger.path.path[0..JOURNAL_SOCKET.len], JOURNAL_SOCKET);
|
||||||
|
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: Self) void {
|
||||||
|
std.os.close(self.socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Message = struct {
|
||||||
|
logger: Logger,
|
||||||
|
priority: Priority,
|
||||||
|
memfd: std.os.fd_t,
|
||||||
|
|
||||||
|
fn _writeString(self: Message, str: []const u8) !void {
|
||||||
|
const len = try std.os.write(self.memfd, str);
|
||||||
|
if (len != str.len) return error.ShortWrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _writeU64(self: Message, native: u64) !void {
|
||||||
|
const le = std.mem.nativeToLittle(u64, native);
|
||||||
|
const msg = std.mem.asBytes(&le);
|
||||||
|
try self._writeString(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn string(self: Message, key: []const u8, value: []const u8) !void {
|
||||||
|
try self._writeString(key);
|
||||||
|
try self._writeString("\n");
|
||||||
|
try self._writeU64(value.len);
|
||||||
|
try self._writeString(value);
|
||||||
|
try self._writeString("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn int(self: Message, key: []const u8, value: anytype) !void {
|
||||||
|
if (@typeInfo(@TypeOf(value)) != .Int) @compileError("type " ++ @typeName(@TypeOf(value)) ++ " is not an integer type");
|
||||||
|
|
||||||
|
var buf: [32]u8 = undefined;
|
||||||
|
var fbs = std.io.fixedBufferStream(&buf);
|
||||||
|
const writer = fbs.writer();
|
||||||
|
try std.fmt.formatInt(value, 10, .lower, .{}, writer);
|
||||||
|
try self.string(key, fbs.getWritten());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn identifier(self: Message, value: []const u8) !void {
|
||||||
|
try self.string("SYSLOG_IDENTIFIER", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn facility(self: Message, value: Facility) !void {
|
||||||
|
try self.int("SYSLOG_FACILITY", @intFromEnum(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn src(self: Message, loc: std.builtin.SourceLocation) !void {
|
||||||
|
try self.string("CODE_FILE", loc.file);
|
||||||
|
try self.string("CODE_FUNC", loc.fn_name);
|
||||||
|
try self.int("CODE_LINE", loc.line);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send(self: *const Message) !void {
|
||||||
|
_ = try std.os.fcntl(
|
||||||
|
self.memfd,
|
||||||
|
F_ADD_SEALS,
|
||||||
|
F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_EXEC,
|
||||||
|
);
|
||||||
|
|
||||||
|
var cmsg = cmsghdr.cmsghdr(std.os.fd_t).init(
|
||||||
|
.{
|
||||||
|
.level = std.os.SOL.SOCKET,
|
||||||
|
.type = SCM_RIGHTS,
|
||||||
|
.data = self.memfd,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
var msghdr = std.os.msghdr_const{
|
||||||
|
.name = @ptrCast(&self.logger.path),
|
||||||
|
.namelen = @sizeOf(@TypeOf(self.logger.path)),
|
||||||
|
.iov = undefined,
|
||||||
|
.iovlen = 0,
|
||||||
|
.control = &cmsg,
|
||||||
|
.controllen = @sizeOf(@TypeOf(cmsg)),
|
||||||
|
.flags = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = try std.os.sendmsg(
|
||||||
|
self.logger.socket,
|
||||||
|
&msghdr,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
std.debug.assert(result == 0);
|
||||||
|
|
||||||
|
std.os.close(self.memfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cancel(self: Message) void {
|
||||||
|
std.os.close(self.memfd);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn message(self: Logger, priority: Priority) !Message {
|
||||||
|
const memfd = try std.os.memfd_create(
|
||||||
|
"zig",
|
||||||
|
std.os.MFD.CLOEXEC | std.os.MFD.ALLOW_SEALING | MFD_NOEXEC_SEAL,
|
||||||
|
);
|
||||||
|
|
||||||
|
const msg = Message{
|
||||||
|
.logger = self,
|
||||||
|
.priority = priority,
|
||||||
|
.memfd = memfd,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (self.identifier) |i| try msg.identifier(i);
|
||||||
|
try msg.int("PRIORITY", @intFromEnum(priority));
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
test "test" {
|
||||||
|
const logger = try Logger.init("library test");
|
||||||
|
defer logger.deinit();
|
||||||
|
|
||||||
|
const message = try logger.message(.INFO);
|
||||||
|
errdefer message.cancel();
|
||||||
|
|
||||||
|
try message.facility(.LOCAL0);
|
||||||
|
try message.string("MESSAGE", "hello");
|
||||||
|
try message.src(@src());
|
||||||
|
|
||||||
|
try message.send();
|
||||||
|
}
|
Loading…
Reference in a new issue