Make print_clogs only print clogs

Externalize matching functionality between user port spec and found
clogs (socket + process information).
This commit is contained in:
Armin Friedl 2024-07-14 20:46:56 +02:00
parent 85f097eb3b
commit 37f51c6002
4 changed files with 160 additions and 78 deletions

View file

@ -2,6 +2,7 @@ const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const sockets = @import("sockets.zig"); const sockets = @import("sockets.zig");
const process = @import("process.zig"); const process = @import("process.zig");
const match = @import("match.zig");
const c = @cImport({ const c = @cImport({
@cInclude("pwd.h"); @cInclude("pwd.h");
@cInclude("arpa/inet.h"); @cInclude("arpa/inet.h");
@ -26,16 +27,19 @@ pub fn main() !u8 {
}; };
defer alloc.free(ports); defer alloc.free(ports);
const pids = try print_clogs(alloc, ports); const clogs = try match.match(alloc, ports);
defer alloc.free(pids); defer clogs.deinit();
if (pids.len == 0) { if (clogs.items.len == 0) {
try std.io.getStdOut().writeAll("Ports look unclogged\n");
return 0; return 0;
} }
try print_clogs(clogs);
var kills: []u16 = undefined; var kills: []u16 = undefined;
while(true) { 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) { if (builtin.mode == std.builtin.Mode.Debug) {
try kill_usage(); try kill_usage();
return err; return err;
@ -49,7 +53,16 @@ pub fn main() !u8 {
defer alloc.free(kills); defer alloc.free(kills);
for(kills) |k| { 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; return 0;
@ -125,85 +138,60 @@ fn choose_kill(alloc: std.mem.Allocator, choices: usize) ![]u16 {
return kills; 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 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" }); 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; var idx: usize = 1;
for (clog_sockets) |cs| { for (clogs.items) |clog| {
if (std.mem.indexOfScalar(u16, ports, cs.port)) |_| { const user = c.getpwuid(clog.sock.uid);
const clogs = try process.find_by_inode(alloc, cs.inode, null);
defer clogs.deinit();
const user = c.getpwuid(cs.uid); for (clog.procs.items) |proc| {
switch (clog.sock.protocol_data) {
for (clogs.items) |clog| { // zig fmt: off
switch (cs.protocol_data) { .tcp_v4 => |proto| {
// zig fmt: off var addr: [c.INET_ADDRSTRLEN:0]u8 = undefined;
.tcp_v4 => |proto| { _ = c.inet_ntop(c.AF_INET, &proto.addr, &addr, c.INET_ADDRSTRLEN);
var addr: [c.INET_ADDRSTRLEN:0]u8 = undefined; try writer.print("{d: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {d: <6} {d: <5}\n", .{
_ = c.inet_ntop(c.AF_INET, &proto.addr, &addr, c.INET_ADDRSTRLEN); idx,
try writer.print("{d: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {d: <6} {d: <5}\n", .{ proc.comm[0..@min(10, proc.comm.len)], proc.exe[0..@min(30, proc.exe.len)],
idx, "TCP/IPv4", std.mem.sliceTo(&addr, 0),
clog.comm[0..@min(10, clog.comm.len)], clog.exe[0..@min(30, clog.exe.len)], user.*.pw_name, clog.sock.inode, clog.port });
"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;
.tcp_v6 => |proto| { _ = c.inet_ntop(c.AF_INET6, &proto.addr, &addr, c.INET6_ADDRSTRLEN);
var addr: [c.INET6_ADDRSTRLEN:0]u8 = undefined; try writer.print("{d: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {d: <6} {d: <5}\n", .{
_ = c.inet_ntop(c.AF_INET6, &proto.addr, &addr, c.INET6_ADDRSTRLEN); idx,
try writer.print("{d: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {d: <6} {d: <5}\n", .{ proc.comm[0..@min(10, proc.comm.len)], proc.exe[0..@min(30, proc.exe.len)],
idx, "TCP/IPv6", std.mem.sliceTo(&addr, 0),
clog.comm[0..@min(10, clog.comm.len)], clog.exe[0..@min(30, clog.exe.len)], user.*.pw_name, clog.sock.inode, clog.port });
"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;
.udp_v4 => |proto| { _ = c.inet_ntop(c.AF_INET, &proto.addr, &addr, c.INET_ADDRSTRLEN);
var addr: [c.INET_ADDRSTRLEN:0]u8 = undefined; try writer.print("{d: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {d: <6} {d: <5}\n", .{
_ = c.inet_ntop(c.AF_INET, &proto.addr, &addr, c.INET_ADDRSTRLEN); idx,
try writer.print("{d: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {d: <6} {d: <5}\n", .{ proc.comm[0..@min(10, proc.comm.len)], proc.exe[0..@min(30, proc.exe.len)],
idx, "UDP/IPv4", std.mem.sliceTo(&addr, 0),
clog.comm[0..@min(10, clog.comm.len)], clog.exe[0..@min(30, clog.exe.len)], user.*.pw_name, clog.sock.inode, clog.port });
"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;
.udp_v6 => |proto| { _ = c.inet_ntop(c.AF_INET6, &proto.addr, &addr, c.INET6_ADDRSTRLEN);
var addr: [c.INET6_ADDRSTRLEN:0]u8 = undefined; try writer.print("{d: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {d: <6} {d: <5}\n", .{
_ = c.inet_ntop(c.AF_INET6, &proto.addr, &addr, c.INET6_ADDRSTRLEN); idx,
try writer.print("{d: <3}{s: <10} {s: <30} {s: <12} {s: <15} {s: <10} {d: <6} {d: <5}\n", .{ proc.comm[0..@min(10, proc.comm.len)], proc.exe[0..@min(30, proc.exe.len)],
idx, "UDP/IPv6", std.mem.sliceTo(&addr, 0),
clog.comm[0..@min(10, clog.comm.len)], clog.exe[0..@min(30, clog.exe.len)], user.*.pw_name, clog.sock.inode, clog.port });
"UDP/IPv6", std.mem.sliceTo(&addr, 0), },
user.*.pw_name, cs.inode, cs.port }); // zig fmt: on
},
// zig fmt: on
}
try pids.append(clog.pid);
idx += 1;
} }
idx += 1;
} }
} }
return pids.toOwnedSlice();
} }
fn usage() !void { fn usage() !void {

55
src/match.zig Normal file
View file

@ -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,
};
}

View file

@ -6,7 +6,7 @@ const std = @import("std");
/// handling deallocation correctly. Call `deinit` to deallocate the memory. /// handling deallocation correctly. Call `deinit` to deallocate the memory.
/// ///
/// Processes can be accessed via `.items` /// Processes can be accessed via `.items`
pub const Clogs = struct { pub const ClogProcesses = struct {
items: []ClogProcess, items: []ClogProcess,
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
@ -14,6 +14,18 @@ pub const Clogs = struct {
try writer.print("{any}", .{value.items}); 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 { pub fn deinit(self: @This()) void {
for (self.items) |item| { for (self.items) |item| {
item.deinit(); item.deinit();
@ -44,6 +56,24 @@ pub const ClogProcess = struct {
try writer.writeAll("}"); 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 { fn deinit(self: @This()) void {
self.alloc.free(self.comm); self.alloc.free(self.comm);
self.alloc.free(self.exe); self.alloc.free(self.exe);
@ -55,7 +85,7 @@ pub const ClogProcess = struct {
}; };
/// Find clogging processes that hold a file handle on an inode /// 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"; const base = proc_path orelse "/proc";
var clogs = std.ArrayList(ClogProcess).init(alloc); 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 { const ProcessFileData = struct {

View file

@ -11,6 +11,15 @@ pub const ClogSocket = struct {
inode: std.posix.ino_t, inode: std.posix.ino_t,
uid: std.posix.uid_t, uid: std.posix.uid_t,
protocol_data: ProtocolData, 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 /// Known protocols