From 37f51c600248ab7f061002d5af6cb64f2070e4fb Mon Sep 17 00:00:00 2001 From: Armin Friedl Date: Sun, 14 Jul 2024 20:46:56 +0200 Subject: [PATCH] Make `print_clogs` only print clogs Externalize matching functionality between user port spec and found clogs (socket + process information). --- src/main.zig | 138 ++++++++++++++++++++++-------------------------- src/match.zig | 55 +++++++++++++++++++ src/process.zig | 36 +++++++++++-- src/sockets.zig | 9 ++++ 4 files changed, 160 insertions(+), 78 deletions(-) create mode 100644 src/match.zig diff --git a/src/main.zig b/src/main.zig index 25794db..6a3cb25 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2,6 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); const sockets = @import("sockets.zig"); const process = @import("process.zig"); +const match = @import("match.zig"); const c = @cImport({ @cInclude("pwd.h"); @cInclude("arpa/inet.h"); @@ -26,16 +27,19 @@ pub fn main() !u8 { }; defer alloc.free(ports); - const pids = try print_clogs(alloc, ports); - defer alloc.free(pids); + const clogs = try match.match(alloc, ports); + defer clogs.deinit(); - if (pids.len == 0) { + if (clogs.items.len == 0) { + try std.io.getStdOut().writeAll("Ports look unclogged\n"); return 0; } + try print_clogs(clogs); + var kills: []u16 = undefined; while(true) { - kills = choose_kill(alloc, pids.len) catch |err| { + kills = choose_kill(alloc, clogs.items.len) catch |err| { if (builtin.mode == std.builtin.Mode.Debug) { try kill_usage(); return err; @@ -49,7 +53,16 @@ pub fn main() !u8 { defer alloc.free(kills); for(kills) |k| { - kill(pids[k-1]); + // go find the kill + var procnum: usize = 0; + for(clogs.items) |clog| { + if(clog.procs.items.len < (k-procnum)) { // kill is in a process in the next clog + procnum += clog.procs.items.len; + } else { // kill is in this clog - the # of clog process we passed + kill(clog.procs.items[k-procnum-1].pid); + break; + } + } } return 0; @@ -125,85 +138,60 @@ fn choose_kill(alloc: std.mem.Allocator, choices: usize) ![]u16 { return kills; } -fn print_clogs(alloc: std.mem.Allocator, ports: []u16) ![]std.posix.pid_t { +fn print_clogs(clogs: match.Clogs) !void { var writer = std.io.getStdOut().writer(); - var pids = std.ArrayList(std.posix.pid_t).init(alloc); - defer pids.deinit(); // noop after re-owning memory at end - - const clog_sockets = try sockets.parse(alloc, null); - defer alloc.free(clog_sockets); - - var any: bool = false; - for (clog_sockets) |cs| { - if(std.mem.indexOfScalar(u16, ports, cs.port)) |_| { - any = true; - } - } - - if (!any) { - try writer.writeAll("Ports look unclogged\n"); - return &[_]std.posix.pid_t{}; - } try writer.print("{s: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {s: <6} {s: <5}\n", .{ "#", "Command", "Path", "Protocol", "Address", "User", "Inode", "Port" }); var idx: usize = 1; - for (clog_sockets) |cs| { - if (std.mem.indexOfScalar(u16, ports, cs.port)) |_| { - const clogs = try process.find_by_inode(alloc, cs.inode, null); - defer clogs.deinit(); + for (clogs.items) |clog| { + const user = c.getpwuid(clog.sock.uid); - const user = c.getpwuid(cs.uid); - - for (clogs.items) |clog| { - switch (cs.protocol_data) { - // zig fmt: off - .tcp_v4 => |proto| { - var addr: [c.INET_ADDRSTRLEN:0]u8 = undefined; - _ = c.inet_ntop(c.AF_INET, &proto.addr, &addr, c.INET_ADDRSTRLEN); - try writer.print("{d: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {d: <6} {d: <5}\n", .{ - idx, - clog.comm[0..@min(10, clog.comm.len)], clog.exe[0..@min(30, clog.exe.len)], - "TCP/IPv4", std.mem.sliceTo(&addr, 0), - user.*.pw_name, cs.inode, cs.port }); - }, - .tcp_v6 => |proto| { - var addr: [c.INET6_ADDRSTRLEN:0]u8 = undefined; - _ = c.inet_ntop(c.AF_INET6, &proto.addr, &addr, c.INET6_ADDRSTRLEN); - try writer.print("{d: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {d: <6} {d: <5}\n", .{ - idx, - clog.comm[0..@min(10, clog.comm.len)], clog.exe[0..@min(30, clog.exe.len)], - "TCP/IPv6", std.mem.sliceTo(&addr, 0), - user.*.pw_name, cs.inode, cs.port }); - }, - .udp_v4 => |proto| { - var addr: [c.INET_ADDRSTRLEN:0]u8 = undefined; - _ = c.inet_ntop(c.AF_INET, &proto.addr, &addr, c.INET_ADDRSTRLEN); - try writer.print("{d: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {d: <6} {d: <5}\n", .{ - idx, - clog.comm[0..@min(10, clog.comm.len)], clog.exe[0..@min(30, clog.exe.len)], - "UDP/IPv4", std.mem.sliceTo(&addr, 0), - user.*.pw_name, cs.inode, cs.port }); - }, - .udp_v6 => |proto| { - var addr: [c.INET6_ADDRSTRLEN:0]u8 = undefined; - _ = c.inet_ntop(c.AF_INET6, &proto.addr, &addr, c.INET6_ADDRSTRLEN); - try writer.print("{d: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {d: <6} {d: <5}\n", .{ - idx, - clog.comm[0..@min(10, clog.comm.len)], clog.exe[0..@min(30, clog.exe.len)], - "UDP/IPv6", std.mem.sliceTo(&addr, 0), - user.*.pw_name, cs.inode, cs.port }); - }, - // zig fmt: on - } - - try pids.append(clog.pid); - idx += 1; + for (clog.procs.items) |proc| { + switch (clog.sock.protocol_data) { + // zig fmt: off + .tcp_v4 => |proto| { + var addr: [c.INET_ADDRSTRLEN:0]u8 = undefined; + _ = c.inet_ntop(c.AF_INET, &proto.addr, &addr, c.INET_ADDRSTRLEN); + try writer.print("{d: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {d: <6} {d: <5}\n", .{ + idx, + proc.comm[0..@min(10, proc.comm.len)], proc.exe[0..@min(30, proc.exe.len)], + "TCP/IPv4", std.mem.sliceTo(&addr, 0), + user.*.pw_name, clog.sock.inode, clog.port }); + }, + .tcp_v6 => |proto| { + var addr: [c.INET6_ADDRSTRLEN:0]u8 = undefined; + _ = c.inet_ntop(c.AF_INET6, &proto.addr, &addr, c.INET6_ADDRSTRLEN); + try writer.print("{d: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {d: <6} {d: <5}\n", .{ + idx, + proc.comm[0..@min(10, proc.comm.len)], proc.exe[0..@min(30, proc.exe.len)], + "TCP/IPv6", std.mem.sliceTo(&addr, 0), + user.*.pw_name, clog.sock.inode, clog.port }); + }, + .udp_v4 => |proto| { + var addr: [c.INET_ADDRSTRLEN:0]u8 = undefined; + _ = c.inet_ntop(c.AF_INET, &proto.addr, &addr, c.INET_ADDRSTRLEN); + try writer.print("{d: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {d: <6} {d: <5}\n", .{ + idx, + proc.comm[0..@min(10, proc.comm.len)], proc.exe[0..@min(30, proc.exe.len)], + "UDP/IPv4", std.mem.sliceTo(&addr, 0), + user.*.pw_name, clog.sock.inode, clog.port }); + }, + .udp_v6 => |proto| { + var addr: [c.INET6_ADDRSTRLEN:0]u8 = undefined; + _ = c.inet_ntop(c.AF_INET6, &proto.addr, &addr, c.INET6_ADDRSTRLEN); + try writer.print("{d: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {d: <6} {d: <5}\n", .{ + idx, + proc.comm[0..@min(10, proc.comm.len)], proc.exe[0..@min(30, proc.exe.len)], + "UDP/IPv6", std.mem.sliceTo(&addr, 0), + user.*.pw_name, clog.sock.inode, clog.port }); + }, + // zig fmt: on } + + idx += 1; } } - - return pids.toOwnedSlice(); } fn usage() !void { diff --git a/src/match.zig b/src/match.zig new file mode 100644 index 0000000..581c0b8 --- /dev/null +++ b/src/match.zig @@ -0,0 +1,55 @@ +const std = @import("std"); +const process = @import("process.zig"); +const sockets = @import("sockets.zig"); + +pub const Clogs = struct { + items: []Clog, + alloc: std.mem.Allocator, + + pub fn deinit(self: @This()) void { + for(self.items) |clog| { + clog.deinit(); + } + + self.alloc.free(self.items); + } +}; + +pub const Clog = struct { + port: u16, + sock: sockets.ClogSocket, + procs: process.ClogProcesses, + + alloc: std.mem.Allocator, + + pub fn deinit(self: @This()) void { + self.procs.deinit(); + } +}; + +pub fn match(alloc: std.mem.Allocator, ports: []u16) !Clogs { + var buf = std.ArrayList(Clog).init(alloc); + defer(buf.deinit()); // noop after re-owned at end + + const socks = try sockets.parse(alloc, null); + defer alloc.free(socks); + + for (socks) |sock| { + if (std.mem.indexOfScalar(u16, ports, sock.port) == null) continue; + + const procs = try process.find_by_inode(alloc, sock.inode, null); + defer procs.deinit(); + + try buf.append(Clog { + .port = sock.port, + .sock = sock.clone(), + .procs = try procs.clone(alloc), + .alloc = alloc, + }); + } + + return Clogs{ + .items = try buf.toOwnedSlice(), + .alloc = alloc, + }; +} diff --git a/src/process.zig b/src/process.zig index 3d74ca7..564b4cf 100644 --- a/src/process.zig +++ b/src/process.zig @@ -6,7 +6,7 @@ const std = @import("std"); /// handling deallocation correctly. Call `deinit` to deallocate the memory. /// /// Processes can be accessed via `.items` -pub const Clogs = struct { +pub const ClogProcesses = struct { items: []ClogProcess, alloc: std.mem.Allocator, @@ -14,6 +14,18 @@ pub const Clogs = struct { try writer.print("{any}", .{value.items}); } + pub fn clone(self: @This(), alloc: std.mem.Allocator) !ClogProcesses { + var items = try alloc.alloc(ClogProcess, self.items.len); + for(self.items, 0..) |item, idx| { + items[idx] = try item.clone(alloc); + } + + return ClogProcesses { + .items = items, + .alloc = alloc + }; + } + pub fn deinit(self: @This()) void { for (self.items) |item| { item.deinit(); @@ -44,6 +56,24 @@ pub const ClogProcess = struct { try writer.writeAll("}"); } + pub fn clone(self: @This(), alloc: std.mem.Allocator) !ClogProcess { + var cmdline: [][]u8 = try alloc.alloc([]u8, self.cmdline.len); + for(self.cmdline, 0..) |line, idx| { + cmdline[idx] = try alloc.dupe(u8, line); + } + + return ClogProcess { + .pid = self.pid, + .inode = self.inode, + .fd = self.fd, + .comm = try alloc.dupe(u8, self.comm), + .exe = try alloc.dupe(u8, self.exe), + .cmdline = cmdline, + + .alloc = alloc + }; + } + fn deinit(self: @This()) void { self.alloc.free(self.comm); self.alloc.free(self.exe); @@ -55,7 +85,7 @@ pub const ClogProcess = struct { }; /// 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 { +pub fn find_by_inode(alloc: std.mem.Allocator, inode: std.posix.ino_t, proc_path: ?[]const u8) !ClogProcesses { const base = proc_path orelse "/proc"; var clogs = std.ArrayList(ClogProcess).init(alloc); @@ -112,7 +142,7 @@ pub fn find_by_inode(alloc: std.mem.Allocator, inode: std.posix.ino_t, proc_path } } - return Clogs{ .items = try clogs.toOwnedSlice(), .alloc = alloc }; + return ClogProcesses{ .items = try clogs.toOwnedSlice(), .alloc = alloc }; } const ProcessFileData = struct { diff --git a/src/sockets.zig b/src/sockets.zig index 31f3adc..d721064 100644 --- a/src/sockets.zig +++ b/src/sockets.zig @@ -11,6 +11,15 @@ pub const ClogSocket = struct { inode: std.posix.ino_t, uid: std.posix.uid_t, protocol_data: ProtocolData, + + pub fn clone(self: @This()) ClogSocket { + return ClogSocket { + .port = self.port, + .inode = self.inode, + .uid = self.uid, + .protocol_data = self.protocol_data + }; + } }; /// Known protocols