diff --git a/build.zig b/build.zig index 450eebb..60560c7 100644 --- a/build.zig +++ b/build.zig @@ -15,25 +15,13 @@ pub fn build(b: *std.Build) void { // set a preferred release mode, allowing the user to decide how to optimize. const optimize = b.standardOptimizeOption(.{}); - const relib = b.addStaticLibrary(.{ - .name = "regex_slim", - .optimize = .Debug, - .target = target, - }); - relib.addIncludePath(.{ .path = "lib" }); - relib.addCSourceFile(.{ .file = b.path("lib/regex_slim.c") }); - relib.linkLibC(); - const exe = b.addExecutable(.{ .name = "unclog", .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, - .link_libc = true, }); exe.addIncludePath(.{ .path = "lib" }); - exe.linkLibC(); - exe.linkLibrary(relib); // This declares intent for the executable to be installed into the // standard location when the user invokes the "install" step (the default @@ -65,17 +53,15 @@ pub fn build(b: *std.Build) void { // Creates a step for unit testing. This only builds the test executable // but does not run it. - const procnet_unit_tests = b.addTest(.{ - .root_source_file = b.path("src/procnet.zig"), + const sockets_unit_tests = b.addTest(.{ + .root_source_file = b.path("src/sockets.zig"), .target = target, .optimize = optimize, - .link_libc = true, }); - procnet_unit_tests.addIncludePath(.{ .path = "lib" }); - procnet_unit_tests.linkLibrary(relib); + sockets_unit_tests.addIncludePath(.{ .path = "lib" }); - const run_procnet_unit_tests = b.addRunArtifact(procnet_unit_tests); - run_procnet_unit_tests.has_side_effects = true; // needed so tests aren't cached + const run_sockets_unit_tests = b.addRunArtifact(sockets_unit_tests); + run_sockets_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. @@ -83,10 +69,8 @@ pub fn build(b: *std.Build) void { .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 @@ -95,7 +79,6 @@ pub fn build(b: *std.Build) void { .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, - .link_libc = true, }); exe_unit_tests.addIncludePath(.{ .path = "lib" }); @@ -105,7 +88,7 @@ pub fn build(b: *std.Build) void { // the `zig build --help` menu, providing a way for the user to request // 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_sockets_unit_tests.step); test_step.dependOn(&run_process_unit_tests.step); test_step.dependOn(&run_exe_unit_tests.step); } diff --git a/lib/regex_slim.c b/lib/regex_slim.c deleted file mode 100644 index 44bd1c3..0000000 --- a/lib/regex_slim.c +++ /dev/null @@ -1,9 +0,0 @@ -#include "regex_slim.h" - -regex_t* alloc_regex_t(void) { - return (regex_t*)malloc(sizeof(regex_t)); -} - -void free_regex_t(regex_t* ptr) { - free(ptr); -} diff --git a/lib/regex_slim.h b/lib/regex_slim.h deleted file mode 100644 index 60d8c2f..0000000 --- a/lib/regex_slim.h +++ /dev/null @@ -1,8 +0,0 @@ -// Workaround for using regex in Zig. -// https://www.openmymind.net/Regular-Expressions-in-Zig/ -// https://stackoverflow.com/questions/73086494/how-to-allocate-a-struct-of-incomplete-type-in-zig -#include -#include - -regex_t* alloc_regex_t(void); -void free_regex_t(regex_t* ptr); diff --git a/src/main.zig b/src/main.zig index f943407..a9d5877 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,14 +1,8 @@ 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"); -}); +const sockets = @import("sockets.zig"); +const process = @import("process.zig"); -pub const std_options = .{ .log_level = .err }; +pub const std_options = .{ .log_level = .info }; pub fn main() !void { var argsit = std.process.args(); @@ -20,99 +14,19 @@ pub fn main() !void { const alloc = gpa.allocator(); - var clogged = false; + const clog_sockets = try sockets.parse(alloc, null); + defer alloc.free(clog_sockets); - var procs = std.ArrayList(pid.Process).init(alloc); - defer procs.deinit(); - - const tcp = try proc.read_proc_net(alloc, .V4, "/proc/net/tcp"); - defer alloc.free(tcp); - for (tcp) |pn| { - if (pn.V4.port == port) { - clogged = true; - try print_proc_net(&pn, "TCP/IPv4"); - try append_processes(alloc, pn.V4.inode, &procs); - } - } - - const tcp6 = try proc.read_proc_net(alloc, .V6, "/proc/net/tcp6"); - defer alloc.free(tcp6); - for (tcp6) |pn| { - if (pn.V6.port == port) { - clogged = true; - try print_proc_net(&pn, "TCP/IPv6"); - try append_processes(alloc, pn.V6.inode, &procs); - } - } - - const udp = try proc.read_proc_net(alloc, .V4, "/proc/net/udp"); - defer alloc.free(udp); - for (udp) |pn| { - if (pn.V4.port == port) { - clogged = true; - try print_proc_net(&pn, "UDP/IPv4"); - try append_processes(alloc, pn.V4.inode, &procs); - } - } - - const udp6 = try proc.read_proc_net(alloc, .V6, "/proc/net/udp6"); - defer alloc.free(udp6); - for (udp6) |pn| { - if (pn.V6.port == port) { - clogged = true; - try print_proc_net(&pn, "UDP/IPv6"); - try append_processes(alloc, pn.V6.inode, &procs); - } - } - - if (!clogged) { - try std.io.getStdOut().writer().print("Port {d} looks unclogged already\n", .{port}); - } else { - _ = try std.io.getStdOut().writer().write("Kill? "); - var buf: [10]u8 = undefined; - if (try std.io.getStdIn().reader().readUntilDelimiterOrEof(buf[0..], '\n')) |input| { - const killproc = try std.fmt.parseInt(usize, input, 10); - const process = procs.items[killproc - 1]; - _ = c.kill(@intCast(process.proc_pid.pid), c.SIGTERM); - } - } -} - -fn append_processes(alloc: std.mem.Allocator, inode: u32, buf: *std.ArrayList(pid.Process)) !void { - const pids = try pid.find_proc(alloc, inode); - defer alloc.free(pids); - try std.io.getStdOut().writer().print("\t{s: <5}{s: <10}{s: <20}{s}\n", .{ "#", "PID", "CMD", "ARGS" }); - - for (pids) |p| { - const process = try pid.resolve_process(alloc, p); - defer process.deinit(); - try buf.append(process); - - const cmdline = try std.mem.join(alloc, " ", process.cmdline[1..]); - defer alloc.free(cmdline); - try std.io.getStdOut().writer().print("\t{d: <5}{d: <10}{s: <20}{s}\n", .{ buf.items.len, process.proc_pid.pid, process.comm[0 .. process.comm.len - 1], cmdline }); - } - _ = try std.io.getStdOut().writer().write("\n"); -} - -fn print_proc_net(entry: *const proc.ProcNet, addr_type: []const u8) !void { - var stdio = std.io.getStdOut(); - - switch (entry.*) { - .V4 => |v4| { - const src_addr_pp = c.inet_ntoa(.{ .s_addr = v4.addr }); // allocates static global buffer, don't free - try stdio.writer().print("Port {d} clogged on {s} Address {s} with socket inode {d}\n", .{ v4.port, addr_type, src_addr_pp, v4.inode }); - }, - .V6 => |v6| { - try stdio.writer().print("Port {d} clogged on {s} Address ", .{ v6.port, addr_type }); - for (@as(*const [16]u8, @ptrCast(&v6.addr)), 0..) |h, idx| { - if (idx % 2 == 0 and idx != 0) { - _ = try stdio.writer().write(":"); - } - - try stdio.writer().print("{X:0<2}", .{h}); + for (clog_sockets) |clog_socket| { + if (clog_socket.port == port) { + const p = try process.find_by_inode(alloc, clog_socket.inode, null); + defer p.deinit(); + switch (clog_socket.protocol_data) { + .tcp_v4 => |v4| std.log.info("Found process {any} clogging address {any}", .{ p, v4.addr }), + .udp_v4 => |v4| std.log.info("Found process {any} clogging address {any}", .{ p, v4.addr }), + .tcp_v6 => |v6| std.log.info("Found process {any} clogging address {any}", .{ p, v6.addr }), + .udp_v6 => |v6| std.log.info("Found process {any} clogging address {any}", .{ p, v6.addr }), } - try stdio.writer().print(" with socket inode {d}\n", .{v6.inode}); - }, + } } } diff --git a/src/process.zig b/src/process.zig index 1e86447..3d74ca7 100644 --- a/src/process.zig +++ b/src/process.zig @@ -2,6 +2,10 @@ const std = @import("std"); +/// A simple array of clogging processes. Mainly a memory-owning wrapper for +/// handling deallocation correctly. Call `deinit` to deallocate the memory. +/// +/// Processes can be accessed via `.items` pub const Clogs = struct { items: []ClogProcess, alloc: std.mem.Allocator, @@ -10,7 +14,7 @@ pub const Clogs = struct { try writer.print("{any}", .{value.items}); } - fn deinit(self: @This()) void { + pub fn deinit(self: @This()) void { for (self.items) |item| { item.deinit(); } @@ -18,6 +22,7 @@ pub const Clogs = struct { } }; +/// Process metadata of a clogging process pub const ClogProcess = struct { // central data pid: std.posix.pid_t, @@ -49,24 +54,29 @@ pub const ClogProcess = struct { } }; -pub fn find_by_inode(alloc: std.mem.Allocator, inode: std.posix.ino_t, proc_path: ?[]u8) !Clogs { +/// Find clogging processes that hold a file handle on an inode +pub fn find_by_inode(alloc: std.mem.Allocator, inode: std.posix.ino_t, proc_path: ?[]const 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 + defer clogs.deinit(); // noop we re-own memory at the end - const proc_dir = try std.fs.openDirAbsolute(base, .{ .iterate = true }); + var proc_dir = try std.fs.openDirAbsolute(base, .{ .iterate = true }); + defer proc_dir.close(); 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) { + var 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) { + defer process_dir.close(); + + var fd_dir = process_dir.openDir("fd", .{ .iterate = true }) catch |err| switch (err) { error.AccessDenied => continue, else => return err, }; + defer fd_dir.close(); var fd_it = FdIterator{ .it = AccessSafeIterator{ .it = fd_dir.iterate() } }; fdit: while (try fd_it.next()) |fd_entry| { @@ -87,39 +97,15 @@ pub fn find_by_inode(alloc: std.mem.Allocator, inode: std.posix.ino_t, proc_path } } - 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"))); - } + const pfd: ProcessFileData = try parse_process_files(alloc, &process_dir) orelse continue; 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), + .comm = pfd.comm, + .cmdline = pfd.cmdline, + .exe = pfd.exe, .alloc = alloc, }); } @@ -129,6 +115,46 @@ pub fn find_by_inode(alloc: std.mem.Allocator, inode: std.posix.ino_t, proc_path return Clogs{ .items = try clogs.toOwnedSlice(), .alloc = alloc }; } +const ProcessFileData = struct { + comm: []u8, + cmdline: [][]u8, + exe: []u8, +}; + +fn parse_process_files(alloc: std.mem.Allocator, process_dir: *const std.fs.Dir) !?ProcessFileData { + var comm_file = process_dir.openFile("comm", .{}) catch |err| switch (err) { + error.AccessDenied => return null, + else => return err, + }; + var cmdline_file = process_dir.openFile("cmdline", .{}) catch |err| switch (err) { + error.AccessDenied => return null, + 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 => return null, + 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"))); + } + + return .{ + .comm = try alloc.dupe(u8, std.mem.trim(u8, comm, "\n")), + .cmdline = try cmdline.toOwnedSlice(), + .exe = try alloc.dupe(u8, exe_path), + }; +} + const FdIterator = struct { it: AccessSafeIterator, @@ -178,7 +204,7 @@ const AccessSafeIterator = struct { test "simple" { std.testing.log_level = .info; - const clogs = try find_by_inode(std.testing.allocator, 3568, null); + const clogs = try find_by_inode(std.testing.allocator, 721, null); defer clogs.deinit(); std.log.info("clogs: {any}", .{clogs}); } diff --git a/src/procnet.zig b/src/procnet.zig deleted file mode 100644 index 507ac33..0000000 --- a/src/procnet.zig +++ /dev/null @@ -1,166 +0,0 @@ -const std = @import("std"); -const log = std.log; -const testing = std.testing; -const Allocator = std.mem.Allocator; - -const c = @cImport({ - @cInclude("arpa/inet.h"); -}); - -pub const ProcNet = union(enum) { - V4: struct { addr: u32, port: u16, inode: u32 }, - V6: struct { addr: u128, port: u16, inode: u32 }, - - pub fn format(value: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { - switch (value) { - .V4 => |v4| { - const src_addr_pp = c.inet_ntoa(.{ .s_addr = v4.addr }); // allocates static global buffer, don't free - try writer.print("{s: <15}{d: <10}{d}", .{ src_addr_pp, v4.port, v4.inode }); - }, - - .V6 => |v6| { - for (@as(*const [16]u8, @ptrCast(&v6.addr)), 0..) |h, idx| { - if (idx % 2 == 0 and idx != 0) { - _ = try writer.write(":"); - } - - try writer.print("{X:0<2}", .{h}); - } - - try writer.print("\t{d: <10}{d}", .{ v6.port, v6.inode }); - }, - } - } -}; - -/// Read a protocol file in /proc/net -/// -/// The memory of the resulting []ProcNet is caller owned and must be freed by -/// the caller -pub fn read_proc_net(alloc: Allocator, comptime addr_len: enum { V4, V6 }, path: []const u8) ![]ProcNet { - var arena = std.heap.ArenaAllocator.init(alloc); - defer arena.deinit(); - const proc_net_parsed: [][3][]u8 = try parse_proc_net(arena.allocator(), path); - - var buf = try std.ArrayList(ProcNet).initCapacity(alloc, proc_net_parsed.len); - defer buf.deinit(); - - const addr_len_t = switch (addr_len) { - .V4 => u32, - .V6 => u128, - }; - - for (proc_net_parsed) |line| { - const src_addr = try std.fmt.parseUnsigned(addr_len_t, line[0], 16); - const src_port = try std.fmt.parseUnsigned(u16, line[1], 16); - const inode = try std.fmt.parseUnsigned(u32, line[2], 10); - - try buf.append(switch (addr_len) { - .V4 => ProcNet{ .V4 = .{ .addr = src_addr, .port = src_port, .inode = inode } }, - .V6 => ProcNet{ .V6 = .{ .addr = src_addr, .port = src_port, .inode = inode } }, - }); - } - - return buf.toOwnedSlice(); -} - -fn parse_proc_net(alloc: Allocator, path: []const u8) ![][3][]u8 { - var proc_net_file = try std.fs.openFileAbsolute(path, .{}); - defer proc_net_file.close(); - - var buf_reader = std.io.bufferedReader(proc_net_file.reader()); - var reader = buf_reader.reader(); - - try reader.skipUntilDelimiterOrEof('\n'); // skip header - - // allocates caller-owned memory - var res = std.ArrayList([3][]u8).init(alloc); - defer res.deinit(); - - // used for internal allocations, owned - var arena = std.heap.ArenaAllocator.init(alloc); - defer arena.deinit(); - - while (try reader.readUntilDelimiterOrEofAlloc(arena.allocator(), '\n', 256)) |line| { - var tokens = std.ArrayList([]const u8).init(arena.allocator()); // buffer for a line in the proc file split by tokenizer - defer tokens.deinit(); - var tokenizer = std.mem.tokenize(u8, line, " \t\n"); - while (tokenizer.next()) |elem| { - try tokens.append(elem); - } - - var src_it = std.mem.splitScalar(u8, tokens.items[1], ':'); - - try res.append(.{ try alloc.dupe(u8, src_it.next().?), try alloc.dupe(u8, src_it.next().?), try alloc.dupe(u8, tokens.items[9]) }); - } - - return res.toOwnedSlice(); -} - -test "basic 3" { - std.testing.log_level = .info; - const alloc = std.testing.allocator; - - var arena = std.heap.ArenaAllocator.init(alloc); - defer arena.deinit(); - - const res = try parse_proc_net(arena.allocator(), "/proc/net/tcp"); - for (res) |line| { - for (line) |field| { - std.debug.print("{s}\t", .{field}); - } - std.debug.print("\n", .{}); - } -} - -test "basic 4" { - std.testing.log_level = .info; - const alloc = std.testing.allocator; - - var arena = std.heap.ArenaAllocator.init(alloc); - defer arena.deinit(); - - const res = try read_proc_net(arena.allocator(), .V4, "/proc/net/tcp"); - for (res) |pn| { - std.debug.print("{any}\n", .{pn}); - } -} - -test "basic 5" { - std.testing.log_level = .info; - const alloc = std.testing.allocator; - - var arena = std.heap.ArenaAllocator.init(alloc); - defer arena.deinit(); - - const res = try read_proc_net(arena.allocator(), .V6, "/proc/net/tcp6"); - for (res) |pn| { - std.debug.print("{any}\n", .{pn}); - } -} - -test "basic 6" { - std.testing.log_level = .info; - const alloc = std.testing.allocator; - - var arena = std.heap.ArenaAllocator.init(alloc); - defer arena.deinit(); - - const res = try read_proc_net(arena.allocator(), .V4, "/proc/net/udp"); - for (res) |pn| { - std.debug.print("{any}\n", .{pn}); - } -} - -test "basic 7" { - std.testing.log_level = .info; - const alloc = std.testing.allocator; - - var arena = std.heap.ArenaAllocator.init(alloc); - defer arena.deinit(); - - const res = try read_proc_net(arena.allocator(), .V6, "/proc/net/udp6"); - for (res) |pn| { - std.debug.print("{any}\n", .{pn}); - } -} diff --git a/src/procpid.zig b/src/procpid.zig deleted file mode 100644 index 18ec1dd..0000000 --- a/src/procpid.zig +++ /dev/null @@ -1,104 +0,0 @@ -const std = @import("std"); -const log = std.log; -const testing = std.testing; -const Allocator = std.mem.Allocator; - -const c = @cImport({ - @cInclude("regex.h"); - @cInclude("regex_slim.h"); -}); - -pub const ProcPid = struct { - pid: u32, - inode: u32, - fd: u32, -}; - -pub const Process = struct { - proc_pid: ProcPid, - comm: []u8, - cmdline: [][]u8, - alloc: std.heap.ArenaAllocator, - - pub fn deinit(self: @This()) void { - self.alloc.deinit(); - } -}; - -pub fn find_proc(alloc: Allocator, inode: u32) ![]ProcPid { - var proc_dir = try std.fs.openDirAbsolute("/proc", .{ .iterate = true }); - defer proc_dir.close(); - - const regex_t = c.alloc_regex_t() orelse { - log.err("Could not allocate regex_t memory", .{}); - return error.RegexAllocFailed; - }; - defer c.free_regex_t(regex_t); - - if (c.regcomp(regex_t, "^[0-9]\\{1,\\}/fd/[0-9]\\{1,\\}$", c.REG_NOSUB) != 0) { - return error.REGCOMP; - } - - var walker = try proc_dir.walk(alloc); - defer walker.deinit(); - - var buf = std.ArrayList(ProcPid).init(alloc); - defer buf.deinit(); - - while (true) { - const entry = walker.next() catch |err| switch (err) { - error.AccessDenied => continue, - else => return err, - } orelse { - log.info("No more entry. Exiting.", .{}); - break; - }; - - if (c.regexec(regex_t, entry.path, 0, null, 0) == 0) { - const stat = proc_dir.statFile(entry.path) catch |err| switch (err) { - error.AccessDenied => continue, - else => return err, - }; - - if (stat.kind == .unix_domain_socket) { - if (stat.inode == inode) { - log.info("Found procpid path {s}", .{entry.path}); - - // /fd/ - var compit = try std.fs.path.componentIterator(entry.path); - const pid = try std.fmt.parseInt(u32, compit.next().?.name, 10); // parse - _ = compit.next().?.name; // skip /fd/ - const fd = try std.fmt.parseInt(u32, compit.next().?.name, 10); // parse - - try buf.append(ProcPid{ .pid = pid, .inode = inode, .fd = fd }); - } - } - } - } - - return buf.toOwnedSlice(); -} - -pub fn resolve_process(alloc: Allocator, proc_pid: ProcPid) !Process { - var fmt_buf = [_]u8{0} ** 20; - const path = try std.fmt.bufPrint(&fmt_buf, "/proc/{d}", .{proc_pid.pid}); - const proc_dir = try std.fs.openDirAbsolute(path, .{}); - - var arena_alloc = std.heap.ArenaAllocator.init(alloc); - - const comm_file = try proc_dir.openFile("comm", .{}); - const comm = try comm_file.reader().readAllAlloc(arena_alloc.allocator(), 4096); - - const cmdline_file = try proc_dir.openFile("cmdline", .{}); - const cmdline_raw = try cmdline_file.reader().readAllAlloc(alloc, 4096); - defer alloc.free(cmdline_raw); - var cmdline_it = std.mem.splitScalar(u8, cmdline_raw, 0x0); - - var cmdline_buf = std.ArrayList([]u8).init(arena_alloc.allocator()); - defer cmdline_buf.deinit(); - while (cmdline_it.next()) |cmdline_elem| { - try cmdline_buf.append(try arena_alloc.allocator().dupe(u8, cmdline_elem)); - } - - return Process{ .proc_pid = proc_pid, .comm = comm, .cmdline = try cmdline_buf.toOwnedSlice(), .alloc = arena_alloc }; -} diff --git a/src/proc.zig b/src/sockets.zig similarity index 100% rename from src/proc.zig rename to src/sockets.zig