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 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 {

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.
///
/// 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 {

View file

@ -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