Finish restructuring, remove old code
This commit is contained in:
parent
72ba68744e
commit
056da26cc0
8 changed files with 82 additions and 446 deletions
29
build.zig
29
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.
|
// set a preferred release mode, allowing the user to decide how to optimize.
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
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(.{
|
const exe = b.addExecutable(.{
|
||||||
.name = "unclog",
|
.name = "unclog",
|
||||||
.root_source_file = b.path("src/main.zig"),
|
.root_source_file = b.path("src/main.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
.link_libc = true,
|
|
||||||
});
|
});
|
||||||
exe.addIncludePath(.{ .path = "lib" });
|
exe.addIncludePath(.{ .path = "lib" });
|
||||||
exe.linkLibC();
|
|
||||||
exe.linkLibrary(relib);
|
|
||||||
|
|
||||||
// This declares intent for the executable to be installed into the
|
// This declares intent for the executable to be installed into the
|
||||||
// standard location when the user invokes the "install" step (the default
|
// 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
|
// Creates a step for unit testing. This only builds the test executable
|
||||||
// but does not run it.
|
// but does not run it.
|
||||||
const procnet_unit_tests = b.addTest(.{
|
const sockets_unit_tests = b.addTest(.{
|
||||||
.root_source_file = b.path("src/procnet.zig"),
|
.root_source_file = b.path("src/sockets.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
.link_libc = true,
|
|
||||||
});
|
});
|
||||||
procnet_unit_tests.addIncludePath(.{ .path = "lib" });
|
sockets_unit_tests.addIncludePath(.{ .path = "lib" });
|
||||||
procnet_unit_tests.linkLibrary(relib);
|
|
||||||
|
|
||||||
const run_procnet_unit_tests = b.addRunArtifact(procnet_unit_tests);
|
const run_sockets_unit_tests = b.addRunArtifact(sockets_unit_tests);
|
||||||
run_procnet_unit_tests.has_side_effects = true; // needed so tests aren't cached
|
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
|
// Creates a step for unit testing. This only builds the test executable
|
||||||
// but does not run it.
|
// but does not run it.
|
||||||
|
@ -83,10 +69,8 @@ pub fn build(b: *std.Build) void {
|
||||||
.root_source_file = b.path("src/process.zig"),
|
.root_source_file = b.path("src/process.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
.link_libc = true,
|
|
||||||
});
|
});
|
||||||
process_unit_tests.addIncludePath(.{ .path = "lib" });
|
process_unit_tests.addIncludePath(.{ .path = "lib" });
|
||||||
process_unit_tests.linkLibrary(relib);
|
|
||||||
|
|
||||||
const run_process_unit_tests = b.addRunArtifact(process_unit_tests);
|
const run_process_unit_tests = b.addRunArtifact(process_unit_tests);
|
||||||
run_process_unit_tests.has_side_effects = true; // needed so tests aren't cached
|
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"),
|
.root_source_file = b.path("src/main.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
.link_libc = true,
|
|
||||||
});
|
});
|
||||||
exe_unit_tests.addIncludePath(.{ .path = "lib" });
|
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
|
// the `zig build --help` menu, providing a way for the user to request
|
||||||
// running the unit tests.
|
// running the unit tests.
|
||||||
const test_step = b.step("test", "Run 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_process_unit_tests.step);
|
||||||
test_step.dependOn(&run_exe_unit_tests.step);
|
test_step.dependOn(&run_exe_unit_tests.step);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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 <regex.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
regex_t* alloc_regex_t(void);
|
|
||||||
void free_regex_t(regex_t* ptr);
|
|
114
src/main.zig
114
src/main.zig
|
@ -1,14 +1,8 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const proc = @import("procnet.zig");
|
const sockets = @import("sockets.zig");
|
||||||
const pid = @import("procpid.zig");
|
const process = @import("process.zig");
|
||||||
const clog = @import("proc.zig");
|
|
||||||
const proces = @import("process.zig");
|
|
||||||
const c = @cImport({
|
|
||||||
@cInclude("arpa/inet.h");
|
|
||||||
@cInclude("signal.h");
|
|
||||||
});
|
|
||||||
|
|
||||||
pub const std_options = .{ .log_level = .err };
|
pub const std_options = .{ .log_level = .info };
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
var argsit = std.process.args();
|
var argsit = std.process.args();
|
||||||
|
@ -20,99 +14,19 @@ pub fn main() !void {
|
||||||
|
|
||||||
const alloc = gpa.allocator();
|
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);
|
for (clog_sockets) |clog_socket| {
|
||||||
defer procs.deinit();
|
if (clog_socket.port == port) {
|
||||||
|
const p = try process.find_by_inode(alloc, clog_socket.inode, null);
|
||||||
const tcp = try proc.read_proc_net(alloc, .V4, "/proc/net/tcp");
|
defer p.deinit();
|
||||||
defer alloc.free(tcp);
|
switch (clog_socket.protocol_data) {
|
||||||
for (tcp) |pn| {
|
.tcp_v4 => |v4| std.log.info("Found process {any} clogging address {any}", .{ p, v4.addr }),
|
||||||
if (pn.V4.port == port) {
|
.udp_v4 => |v4| std.log.info("Found process {any} clogging address {any}", .{ p, v4.addr }),
|
||||||
clogged = true;
|
.tcp_v6 => |v6| std.log.info("Found process {any} clogging address {any}", .{ p, v6.addr }),
|
||||||
try print_proc_net(&pn, "TCP/IPv4");
|
.udp_v6 => |v6| std.log.info("Found process {any} clogging address {any}", .{ p, v6.addr }),
|
||||||
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});
|
|
||||||
}
|
|
||||||
try stdio.writer().print(" with socket inode {d}\n", .{v6.inode});
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
const std = @import("std");
|
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 {
|
pub const Clogs = struct {
|
||||||
items: []ClogProcess,
|
items: []ClogProcess,
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
|
@ -10,7 +14,7 @@ pub const Clogs = struct {
|
||||||
try writer.print("{any}", .{value.items});
|
try writer.print("{any}", .{value.items});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deinit(self: @This()) void {
|
pub fn deinit(self: @This()) void {
|
||||||
for (self.items) |item| {
|
for (self.items) |item| {
|
||||||
item.deinit();
|
item.deinit();
|
||||||
}
|
}
|
||||||
|
@ -18,6 +22,7 @@ pub const Clogs = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Process metadata of a clogging process
|
||||||
pub const ClogProcess = struct {
|
pub const ClogProcess = struct {
|
||||||
// central data
|
// central data
|
||||||
pid: std.posix.pid_t,
|
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";
|
const base = proc_path orelse "/proc";
|
||||||
|
|
||||||
var clogs = std.ArrayList(ClogProcess).init(alloc);
|
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() } };
|
var process_it = PidIterator{ .it = AccessSafeIterator{ .it = proc_dir.iterate() } };
|
||||||
while (try process_it.next()) |process_entry| {
|
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,
|
error.AccessDenied => continue,
|
||||||
else => return err,
|
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,
|
error.AccessDenied => continue,
|
||||||
else => return err,
|
else => return err,
|
||||||
};
|
};
|
||||||
|
defer fd_dir.close();
|
||||||
|
|
||||||
var fd_it = FdIterator{ .it = AccessSafeIterator{ .it = fd_dir.iterate() } };
|
var fd_it = FdIterator{ .it = AccessSafeIterator{ .it = fd_dir.iterate() } };
|
||||||
fdit: while (try fd_it.next()) |fd_entry| {
|
fdit: while (try fd_it.next()) |fd_entry| {
|
||||||
|
@ -87,18 +97,43 @@ pub fn find_by_inode(alloc: std.mem.Allocator, inode: std.posix.ino_t, proc_path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 = pfd.comm,
|
||||||
|
.cmdline = pfd.cmdline,
|
||||||
|
.exe = pfd.exe,
|
||||||
|
.alloc = alloc,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
var comm_file = process_dir.openFile("comm", .{}) catch |err| switch (err) {
|
||||||
error.AccessDenied => continue,
|
error.AccessDenied => return null,
|
||||||
else => return err,
|
else => return err,
|
||||||
};
|
};
|
||||||
var cmdline_file = process_dir.openFile("cmdline", .{}) catch |err| switch (err) {
|
var cmdline_file = process_dir.openFile("cmdline", .{}) catch |err| switch (err) {
|
||||||
error.AccessDenied => continue,
|
error.AccessDenied => return null,
|
||||||
else => return err,
|
else => return err,
|
||||||
};
|
};
|
||||||
|
|
||||||
var exe_path_buf: [std.fs.max_path_bytes]u8 = undefined;
|
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) {
|
const exe_path = process_dir.readLink("exe", &exe_path_buf) catch |err| switch (err) {
|
||||||
error.AccessDenied => continue,
|
error.AccessDenied => return null,
|
||||||
else => return err,
|
else => return err,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -113,20 +148,11 @@ pub fn find_by_inode(alloc: std.mem.Allocator, inode: std.posix.ino_t, proc_path
|
||||||
try cmdline.append(try alloc.dupe(u8, std.mem.trim(u8, cl, "\n")));
|
try cmdline.append(try alloc.dupe(u8, std.mem.trim(u8, cl, "\n")));
|
||||||
}
|
}
|
||||||
|
|
||||||
try clogs.append(ClogProcess{
|
return .{
|
||||||
.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")),
|
.comm = try alloc.dupe(u8, std.mem.trim(u8, comm, "\n")),
|
||||||
.cmdline = try cmdline.toOwnedSlice(),
|
.cmdline = try cmdline.toOwnedSlice(),
|
||||||
.exe = try alloc.dupe(u8, exe_path),
|
.exe = try alloc.dupe(u8, exe_path),
|
||||||
.alloc = alloc,
|
};
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Clogs{ .items = try clogs.toOwnedSlice(), .alloc = alloc };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const FdIterator = struct {
|
const FdIterator = struct {
|
||||||
|
@ -178,7 +204,7 @@ const AccessSafeIterator = struct {
|
||||||
test "simple" {
|
test "simple" {
|
||||||
std.testing.log_level = .info;
|
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();
|
defer clogs.deinit();
|
||||||
std.log.info("clogs: {any}", .{clogs});
|
std.log.info("clogs: {any}", .{clogs});
|
||||||
}
|
}
|
||||||
|
|
166
src/procnet.zig
166
src/procnet.zig
|
@ -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});
|
|
||||||
}
|
|
||||||
}
|
|
104
src/procpid.zig
104
src/procpid.zig
|
@ -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});
|
|
||||||
|
|
||||||
// <pid>/fd/<fd>
|
|
||||||
var compit = try std.fs.path.componentIterator(entry.path);
|
|
||||||
const pid = try std.fmt.parseInt(u32, compit.next().?.name, 10); // parse <pid>
|
|
||||||
_ = compit.next().?.name; // skip /fd/
|
|
||||||
const fd = try std.fmt.parseInt(u32, compit.next().?.name, 10); // parse <fd>
|
|
||||||
|
|
||||||
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 };
|
|
||||||
}
|
|
Loading…
Reference in a new issue