From 47d6d3b579379167a2d6bac1fd14b0ac52041e1b Mon Sep 17 00:00:00 2001 From: LordMZTE Date: Mon, 3 Jul 2023 19:10:00 +0200 Subject: [PATCH] optimize openbrowser process scanning --- scripts/openbrowser/src/ProcessInfo.zig | 67 ------------------------ scripts/openbrowser/src/info.zig | 68 +++++++++++++++++++++++++ scripts/openbrowser/src/main.zig | 21 ++++---- 3 files changed, 80 insertions(+), 76 deletions(-) delete mode 100644 scripts/openbrowser/src/ProcessInfo.zig create mode 100644 scripts/openbrowser/src/info.zig diff --git a/scripts/openbrowser/src/ProcessInfo.zig b/scripts/openbrowser/src/ProcessInfo.zig deleted file mode 100644 index 6eedd87..0000000 --- a/scripts/openbrowser/src/ProcessInfo.zig +++ /dev/null @@ -1,67 +0,0 @@ -const std = @import("std"); - -running: bool, -exepath: ?[]const u8, - -const Self = @This(); - -/// Frees the data of this ProcessInfo. -/// `alloc` must be the same allocator that was supplied to `get`! -pub fn deinit(self: *Self, alloc: std.mem.Allocator) void { - if (self.exepath) |exepath| - alloc.free(exepath); - self.* = undefined; -} - -/// Gets information about a process given it's name -pub fn get( - name: []const u8, - alloc: std.mem.Allocator, -) !Self { - var proc_dir = try std.fs.openIterableDirAbsolute("/proc", .{}); - defer proc_dir.close(); - - var proc_iter = proc_dir.iterate(); - procs: while (try proc_iter.next()) |proc| { - // Filter directories that aren't PIDs - for (std.fs.path.basename(proc.name)) |letter| - if (!std.ascii.isDigit(letter)) - continue :procs; - - var buf: [512]u8 = undefined; - const cmdline_f = std.fs.openFileAbsolute( - try std.fmt.bufPrint(&buf, "/proc/{s}/cmdline", .{proc.name}), - .{}, - ) catch |e| { - // This just happens when we're dealing with another user's process. - if (e == error.AccessDenied) - continue; - - return e; - }; - - defer cmdline_f.close(); - - const cmdline_data = try cmdline_f.readToEndAlloc(alloc, std.math.maxInt(usize)); - defer alloc.free(cmdline_data); - - var cmdline_splits = std.mem.split(u8, cmdline_data, &.{0}); - const exepath = cmdline_splits.next() orelse return error.InvalidCmdline; - - // this is a startsWith instead of an eql because the arguments in the - // cmdline file are sometimes (and only sometimes!) separated by spaces - // and not null bytes. - if (!std.mem.startsWith(u8, std.fs.path.basename(exepath), name)) - continue; - - return .{ - .running = true, - .exepath = try alloc.dupe(u8, exepath), - }; - } - - return .{ - .running = false, - .exepath = null, - }; -} diff --git a/scripts/openbrowser/src/info.zig b/scripts/openbrowser/src/info.zig new file mode 100644 index 0000000..3e2fa3d --- /dev/null +++ b/scripts/openbrowser/src/info.zig @@ -0,0 +1,68 @@ +const std = @import("std"); + +pub const ProcessQuery = struct { + name: []const u8, + found_exepath: ?[]const u8 = null, + + pub fn deinit(self: *ProcessQuery, alloc: std.mem.Allocator) void { + if (self.found_exepath) |p| + alloc.free(p); + + self.* = undefined; + } +}; + +pub fn query(alloc: std.mem.Allocator, queries: []ProcessQuery) !void { + var proc_dir = try std.fs.openIterableDirAbsolute("/proc", .{}); + defer proc_dir.close(); + + var proc_iter = proc_dir.iterate(); + procs: while (try proc_iter.next()) |proc| { + // only look at directories which represent PIDs + for (std.fs.path.basename(proc.name)) |c| + if (!std.ascii.isDigit(c)) + continue :procs; + + var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + const cmdline_f = std.fs.openFileAbsolute( + try std.fmt.bufPrint(&buf, "/proc/{s}/cmdline", .{proc.name}), + .{}, + ) catch |e| switch (e) { + // skip other users' processes + error.AccessDenied => continue, + else => return e, + }; + defer cmdline_f.close(); + + // read first part of null-separated data (binary path) + const exepath = epath: { + var fbs = std.io.fixedBufferStream(&buf); + cmdline_f.reader().streamUntilDelimiter(fbs.writer(), 0, null) catch |e| switch (e) { + // occurs if there's no delimiter + error.EndOfStream => {}, + else => return e, + }; + break :epath fbs.getWritten(); + }; + + var found_all = true; + + for (queries) |*q| { + if (q.found_exepath) |_| + continue; + + found_all = false; + + // this is a startsWith instead of an eql because the arguments in the + // cmdline file are sometimes (and only sometimes!) separated by spaces + // and not null bytes. + if (!std.mem.startsWith(u8, std.fs.path.basename(exepath), q.name)) + continue; + + q.found_exepath = try alloc.dupe(u8, exepath); + } + + if (found_all) + break; + } +} diff --git a/scripts/openbrowser/src/main.zig b/scripts/openbrowser/src/main.zig index 10d8307..8c0d430 100644 --- a/scripts/openbrowser/src/main.zig +++ b/scripts/openbrowser/src/main.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const ProcessInfo = @import("ProcessInfo.zig"); +const info = @import("info.zig"); pub const std_options = struct { pub const log_level = .debug; @@ -16,17 +16,20 @@ pub fn main() !void { defer _ = gpa.deinit(); const alloc = gpa.allocator(); - for (browsers) |browser| { - var info = try ProcessInfo.get(browser, alloc); - defer info.deinit(alloc); + var queries: [browsers.len]info.ProcessQuery = undefined; + for (browsers, &queries) |b, *q| + q.* = .{ .name = b }; - if (!info.running) - continue; + try info.query(alloc, &queries); + defer for (&queries) |*q| q.deinit(alloc); - std.log.info("found running browser {s}", .{info.exepath.?}); + for (queries) |q| { + if (q.found_exepath) |path| { + std.log.info("found running browser: {s}", .{path}); - try start(browser, alloc); - return; + try start(q.name, alloc); + return; + } } std.log.info("no running browser, using first choice", .{});