Regex free process reader
This commit is contained in:
parent
633713180d
commit
72ba68744e
10 changed files with 244 additions and 22 deletions
15
build.zig
15
build.zig
|
@ -77,6 +77,20 @@ pub fn build(b: *std.Build) void {
|
|||
const run_procnet_unit_tests = b.addRunArtifact(procnet_unit_tests);
|
||||
run_procnet_unit_tests.has_side_effects = true; // needed so tests aren't cached
|
||||
|
||||
// Creates a step for unit testing. This only builds the test executable
|
||||
// but does not run it.
|
||||
const process_unit_tests = b.addTest(.{
|
||||
.root_source_file = b.path("src/process.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.link_libc = true,
|
||||
});
|
||||
process_unit_tests.addIncludePath(.{ .path = "lib" });
|
||||
process_unit_tests.linkLibrary(relib);
|
||||
|
||||
const run_process_unit_tests = b.addRunArtifact(process_unit_tests);
|
||||
run_process_unit_tests.has_side_effects = true; // needed so tests aren't cached
|
||||
|
||||
const exe_unit_tests = b.addTest(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
|
@ -92,5 +106,6 @@ pub fn build(b: *std.Build) void {
|
|||
// running the unit tests.
|
||||
const test_step = b.step("test", "Run unit tests");
|
||||
test_step.dependOn(&run_procnet_unit_tests.step);
|
||||
test_step.dependOn(&run_process_unit_tests.step);
|
||||
test_step.dependOn(&run_exe_unit_tests.step);
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
const std = @import("std");
|
|
@ -2,6 +2,7 @@ const std = @import("std");
|
|||
const proc = @import("procnet.zig");
|
||||
const pid = @import("procpid.zig");
|
||||
const clog = @import("proc.zig");
|
||||
const proces = @import("process.zig");
|
||||
const c = @cImport({
|
||||
@cInclude("arpa/inet.h");
|
||||
@cInclude("signal.h");
|
||||
|
|
28
src/proc.zig
28
src/proc.zig
|
@ -5,7 +5,7 @@
|
|||
|
||||
const std = @import("std");
|
||||
|
||||
// Socket that currently clogs a port with protocol information
|
||||
/// Socket that currently clogs a port with protocol information
|
||||
pub const ClogSocket = struct {
|
||||
port: u16,
|
||||
inode: std.posix.ino_t,
|
||||
|
@ -13,7 +13,7 @@ pub const ClogSocket = struct {
|
|||
protocol_data: ProtocolData,
|
||||
};
|
||||
|
||||
// Known protocols
|
||||
/// Known protocols
|
||||
pub const Protocol = enum {
|
||||
udp_v4,
|
||||
udp_v6,
|
||||
|
@ -21,7 +21,7 @@ pub const Protocol = enum {
|
|||
tcp_v6,
|
||||
};
|
||||
|
||||
// Protocol specific data
|
||||
/// Protocol specific data
|
||||
pub const ProtocolData = union(Protocol) {
|
||||
udp_v4: struct { addr: u32 },
|
||||
udp_v6: struct { addr: u128 },
|
||||
|
@ -29,15 +29,17 @@ pub const ProtocolData = union(Protocol) {
|
|||
tcp_v6: struct { addr: u128 },
|
||||
};
|
||||
|
||||
// Parses `/proc/net` information into a flat list of ClogSockets.
|
||||
//
|
||||
// Memory is owned by caller. []ClogSocket must be freed by caller.
|
||||
/// Parses `/proc/net` information into a flat list of ClogSockets.
|
||||
///
|
||||
/// Memory is owned by caller. []ClogSocket must be freed by caller.
|
||||
///
|
||||
/// If proc_path is `null` will default to `/proc/net`
|
||||
pub fn parse(alloc: std.mem.Allocator, proc_path: ?[]u8) ![]ClogSocket {
|
||||
const base = proc_path orelse "/proc/net";
|
||||
|
||||
var buf = std.ArrayList(ClogSocket).init(alloc);
|
||||
defer buf.deinit(); // noop we re-own memory to parent at the end
|
||||
|
||||
const base = proc_path orelse "/proc/net";
|
||||
|
||||
var arena = std.heap.ArenaAllocator.init(alloc);
|
||||
defer arena.deinit();
|
||||
const child_alloc = arena.allocator();
|
||||
|
@ -133,6 +135,12 @@ fn parse_internal(path: []const u8, protocol: Protocol, buf: *std.ArrayList(Clog
|
|||
|
||||
test "parse" {
|
||||
var alloc = std.testing.allocator;
|
||||
const clogs = try parse(alloc, null);
|
||||
alloc.free(clogs);
|
||||
|
||||
std.testing.log_level = .info;
|
||||
|
||||
const file_path = try std.fs.cwd().realpathAlloc(alloc, "./test/proc1");
|
||||
defer alloc.free(file_path);
|
||||
|
||||
const clogs = try parse(alloc, file_path);
|
||||
defer alloc.free(clogs);
|
||||
}
|
||||
|
|
184
src/process.zig
Normal file
184
src/process.zig
Normal file
|
@ -0,0 +1,184 @@
|
|||
//! Find and get information about a processes
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
pub const Clogs = struct {
|
||||
items: []ClogProcess,
|
||||
alloc: std.mem.Allocator,
|
||||
|
||||
pub fn format(value: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
||||
try writer.print("{any}", .{value.items});
|
||||
}
|
||||
|
||||
fn deinit(self: @This()) void {
|
||||
for (self.items) |item| {
|
||||
item.deinit();
|
||||
}
|
||||
self.alloc.free(self.items);
|
||||
}
|
||||
};
|
||||
|
||||
pub const ClogProcess = struct {
|
||||
// central data
|
||||
pid: std.posix.pid_t,
|
||||
inode: std.posix.ino_t,
|
||||
fd: std.posix.fd_t,
|
||||
comm: []u8,
|
||||
exe: []u8,
|
||||
cmdline: [][]u8,
|
||||
|
||||
// for housekeeping
|
||||
alloc: std.mem.Allocator,
|
||||
|
||||
pub fn format(value: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
||||
try writer.writeAll("\nClogProcess{\n");
|
||||
try writer.print("\t.pid={d}, .inode={d}, .fd={d},\n", .{ value.pid, value.inode, value.fd });
|
||||
try writer.print("\t.comm={s}\n", .{value.comm});
|
||||
try writer.print("\t.exe={s}\n", .{value.exe});
|
||||
try writer.print("\t.cmdline={s}\n", .{value.cmdline});
|
||||
try writer.writeAll("}");
|
||||
}
|
||||
|
||||
fn deinit(self: @This()) void {
|
||||
self.alloc.free(self.comm);
|
||||
self.alloc.free(self.exe);
|
||||
for (self.cmdline) |cl| {
|
||||
self.alloc.free(cl);
|
||||
}
|
||||
self.alloc.free(self.cmdline);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn find_by_inode(alloc: std.mem.Allocator, inode: std.posix.ino_t, proc_path: ?[]u8) !Clogs {
|
||||
const base = proc_path orelse "/proc";
|
||||
|
||||
var clogs = std.ArrayList(ClogProcess).init(alloc);
|
||||
defer clogs.deinit(); // noop we re-own memory to parent at the end
|
||||
|
||||
const proc_dir = try std.fs.openDirAbsolute(base, .{ .iterate = true });
|
||||
|
||||
var process_it = PidIterator{ .it = AccessSafeIterator{ .it = proc_dir.iterate() } };
|
||||
while (try process_it.next()) |process_entry| {
|
||||
const process_dir = proc_dir.openDir(process_entry.name, .{ .iterate = true }) catch |err| switch (err) {
|
||||
error.AccessDenied => continue,
|
||||
else => return err,
|
||||
};
|
||||
const fd_dir = process_dir.openDir("fd", .{ .iterate = true }) catch |err| switch (err) {
|
||||
error.AccessDenied => continue,
|
||||
else => return err,
|
||||
};
|
||||
|
||||
var fd_it = FdIterator{ .it = AccessSafeIterator{ .it = fd_dir.iterate() } };
|
||||
fdit: while (try fd_it.next()) |fd_entry| {
|
||||
_ = std.fmt.parseInt(std.posix.pid_t, fd_entry.name, 10) catch continue;
|
||||
const fd_stat = fd_dir.statFile(fd_entry.name) catch |err| switch (err) {
|
||||
error.AccessDenied => continue,
|
||||
else => return err,
|
||||
};
|
||||
|
||||
if (fd_stat.inode == inode) {
|
||||
std.log.debug("Found inode {d} in {s}/{s}", .{ fd_stat.inode, process_entry.name, fd_entry.name });
|
||||
|
||||
const pid = try std.fmt.parseInt(std.posix.pid_t, process_entry.name, 10);
|
||||
for (clogs.items) |clog| {
|
||||
if (clog.pid == pid) {
|
||||
std.log.debug("pid {d} already in clogs", .{pid});
|
||||
continue :fdit;
|
||||
}
|
||||
}
|
||||
|
||||
var comm_file = process_dir.openFile("comm", .{}) catch |err| switch (err) {
|
||||
error.AccessDenied => continue,
|
||||
else => return err,
|
||||
};
|
||||
var cmdline_file = process_dir.openFile("cmdline", .{}) catch |err| switch (err) {
|
||||
error.AccessDenied => continue,
|
||||
else => return err,
|
||||
};
|
||||
|
||||
var exe_path_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
const exe_path = process_dir.readLink("exe", &exe_path_buf) catch |err| switch (err) {
|
||||
error.AccessDenied => continue,
|
||||
else => return err,
|
||||
};
|
||||
|
||||
const comm = try comm_file.readToEndAlloc(alloc, 4096);
|
||||
defer alloc.free(comm);
|
||||
|
||||
var cmdline = std.ArrayList([]u8).init(alloc);
|
||||
const cmdline_raw = try cmdline_file.readToEndAlloc(alloc, 4096);
|
||||
defer alloc.free(cmdline_raw);
|
||||
var cmdline_it = std.mem.splitScalar(u8, cmdline_raw, 0x0);
|
||||
while (cmdline_it.next()) |cl| {
|
||||
try cmdline.append(try alloc.dupe(u8, std.mem.trim(u8, cl, "\n")));
|
||||
}
|
||||
|
||||
try clogs.append(ClogProcess{
|
||||
.pid = pid,
|
||||
.inode = fd_stat.inode,
|
||||
.fd = try std.fmt.parseInt(std.posix.pid_t, fd_entry.name, 10),
|
||||
.comm = try alloc.dupe(u8, std.mem.trim(u8, comm, "\n")),
|
||||
.cmdline = try cmdline.toOwnedSlice(),
|
||||
.exe = try alloc.dupe(u8, exe_path),
|
||||
.alloc = alloc,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Clogs{ .items = try clogs.toOwnedSlice(), .alloc = alloc };
|
||||
}
|
||||
|
||||
const FdIterator = struct {
|
||||
it: AccessSafeIterator,
|
||||
|
||||
pub fn next(self: *@This()) !?std.fs.Dir.Entry {
|
||||
return try self.it.next();
|
||||
}
|
||||
};
|
||||
|
||||
/// Iterator over directories that look like a process directory
|
||||
const PidIterator = struct {
|
||||
it: AccessSafeIterator,
|
||||
|
||||
pub fn next(self: *@This()) !?std.fs.Dir.Entry {
|
||||
while (try self.it.next()) |entry| {
|
||||
if (entry.kind == .directory) {
|
||||
_ = std.fmt.parseInt(std.posix.pid_t, entry.name, 10) catch continue;
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/// Iterator which ignores `AccessDenied` errors from child iterator
|
||||
const AccessSafeIterator = struct {
|
||||
it: std.fs.Dir.Iterator,
|
||||
|
||||
pub fn next(self: *@This()) !?std.fs.Dir.Entry {
|
||||
while (true) {
|
||||
const elem = self.it.next() catch |err| switch (err) {
|
||||
error.AccessDenied => continue,
|
||||
else => return err,
|
||||
};
|
||||
|
||||
if (elem) |e| {
|
||||
self.it.dir.access(e.name, .{}) catch continue;
|
||||
}
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
test "simple" {
|
||||
std.testing.log_level = .info;
|
||||
|
||||
const clogs = try find_by_inode(std.testing.allocator, 3568, null);
|
||||
defer clogs.deinit();
|
||||
std.log.info("clogs: {any}", .{clogs});
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub const Protocol {
|
||||
proto: enum {
|
||||
TCP_V4,
|
||||
TCP_V6,
|
||||
UDP_V4,
|
||||
UDP_V6
|
||||
},
|
||||
port_map: std.AutoHashMap(u16, Process);
|
||||
};
|
8
test/proc1/tcp
Normal file
8
test/proc1/tcp
Normal file
|
@ -0,0 +1,8 @@
|
|||
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
|
||||
0: 0100007F:0277 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 7987 1 00000000510949ee 99 0 0 10 0
|
||||
1: 0100007F:0521 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 229198 1 000000000cc7de62 99 0 0 10 0
|
||||
2: 017AA8C0:0035 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 12004 1 000000000e13fb3e 99 0 0 10 5
|
||||
3: 9600A8C0:91BE 1A71528C:01BB 01 00000000:00000000 02:00005772 00000000 1000 0 312207 2 0000000039cb8c9e 61 3 31 10 -1
|
||||
4: 9600A8C0:8CF0 5DF36B22:01BB 01 00000000:00000000 02:00001FA2 00000000 1000 0 264134 2 00000000a051e7c1 22 3 31 10 -1
|
||||
5: 9600A8C0:B0A6 AC4C825E:01BB 01 00000000:00000000 02:00097528 00000000 1000 0 304874 2 000000009579cfcc 22 3 30 10 -1
|
||||
6: 9600A8C0:D042 AC4C825E:01BB 01 00000000:00000000 02:0005F59F 00000000 1000 0 279726 2 00000000b9879d52 22 3 24 10 -1
|
7
test/proc1/tcp6
Normal file
7
test/proc1/tcp6
Normal file
|
@ -0,0 +1,7 @@
|
|||
sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
|
||||
0: 00000000000000000000000001000000:0277 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 7986 1 00000000a819e168 99 0 0 10 0
|
||||
1: 0000000000000000FFFF00000100007F:CE5D 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 44943 1 000000009005f71a 99 0 0 10 0
|
||||
2: 8883022A00E0C11A96B7CA3E649815D5:9B08 00470626000000000000000034B11368:01BB 06 00000000:00000000 03:00001606 00000000 0 0 0 3 00000000f49b65c7
|
||||
3: 8883022A00E0C11A96B7CA3E649815D5:9A5A 7C060120F02001140000000001000000:01BB 01 00000000:00000000 00:00000000 00000000 1000 0 291092 1 0000000038d2a6ed 23 3 30 10 -1
|
||||
4: 8883022A00E0C11A96B7CA3E649815D5:E52E 5014002A0C080D40000000000E200000:01BB 01 00000000:00000000 00:00000000 00000000 1000 0 320332 1 00000000ec54adc7 23 3 31 10 -1
|
||||
5: 8883022A00E0C11A96B7CA3E649815D5:A0BA C011002A000066000000000033000000:01BB 01 00000000:00000000 00:00000000 00000000 1000 0 306175 1 00000000e41ac297 23 3 28 10 -1
|
6
test/proc1/udp
Normal file
6
test/proc1/udp
Normal file
|
@ -0,0 +1,6 @@
|
|||
sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode ref pointer drops
|
||||
17624: 017AA8C0:0035 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 12003 2 000000000be6b861 0
|
||||
17638: 00000000:0043 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 12000 2 00000000183b2711 0
|
||||
17639: 9600A8C0:0044 0100A8C0:0043 01 00000000:00000000 00:00000000 00000000 0 0 276252 2 000000000a09b1ba 0
|
||||
22924: 00000000:14E9 00000000:0000 07 00000000:00000000 00:00000000 00000000 974 0 3352 2 000000002c689e03 0
|
||||
31811: 00000000:B7A0 00000000:0000 07 00000000:00000000 00:00000000 00000000 974 0 3354 2 0000000071428bb3 0
|
5
test/proc1/udp6
Normal file
5
test/proc1/udp6
Normal file
|
@ -0,0 +1,5 @@
|
|||
sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode ref pointer drops
|
||||
18117: 000080FE000000000716539EF9245F4B:0222 00000000000000000000000000000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 274919 2 00000000503fce27 0
|
||||
20307: 00000000000000000000000000000000:8AB0 00000000000000000000000000000000:0000 07 00000000:00000000 00:00000000 00000000 974 0 3355 2 00000000e8a66559 0
|
||||
22924: 00000000000000000000000000000000:14E9 00000000000000000000000000000000:0000 07 00000000:00000000 00:00000000 00000000 974 0 3353 2 00000000f71f77f1 0
|
||||
30065: 00000000000000000000000000000000:B0CE 00000000000000000000000000000000:0000 07 00000000:00000000 00:00000000 00000000 1000 0 316229 2 000000008792aef6 640
|
Loading…
Reference in a new issue