journalz/src/root.zig
2024-01-13 22:02:57 -06:00

199 lines
5.3 KiB
Zig

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();
}