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