generated from general-packages/pika-pkg-template
Bump version to 0.3.0, enhance disk and GPU info retrieval with caching support, and streamline package count management
All checks were successful
PikaOS Package Build & Release (amd64-v3) / build (push) Successful in 47s
All checks were successful
PikaOS Package Build & Release (amd64-v3) / build (push) Successful in 47s
This commit is contained in:
parent
829d085d1f
commit
dd8f231c51
2
.github/release-nest-v3
vendored
2
.github/release-nest-v3
vendored
@ -1 +1 @@
|
||||
2
|
||||
1
|
2
main.sh
2
main.sh
@ -6,7 +6,7 @@ set -e
|
||||
|
||||
echo "$PIKA_BUILD_ARCH" > pika-build-arch
|
||||
|
||||
VERSION="0.2.0"
|
||||
VERSION="0.3.0"
|
||||
|
||||
cd ./pikafetch/
|
||||
|
||||
|
@ -1,3 +1,10 @@
|
||||
pikafetch (0.3.0-101pika1) pika; urgency=medium
|
||||
|
||||
* Multiple disk support, add caching of GPU and packages info
|
||||
|
||||
-- ferreo <harderthanfire@gmail.com> Wed, 18 Jan 2023 21:48:14 +0000
|
||||
|
||||
|
||||
pikafetch (0.2.0-101pika3) pika; urgency=medium
|
||||
|
||||
* Move to zig nightly, remove all use of libc, switch to using arena allocator
|
||||
|
@ -1,11 +1,68 @@
|
||||
const std = @import("std");
|
||||
const os = std.os;
|
||||
const fs = std.fs;
|
||||
const mem = std.mem;
|
||||
const ArrayList = std.ArrayList;
|
||||
|
||||
pub const DiskInfo = struct {
|
||||
mount_point: []const u8,
|
||||
total: u64,
|
||||
used: u64,
|
||||
};
|
||||
|
||||
// Comptime array of physical filesystem types
|
||||
const physical_fs = [_][]const u8{
|
||||
"ext4",
|
||||
"btrfs",
|
||||
"xfs",
|
||||
"ext3",
|
||||
"ext2",
|
||||
"f2fs",
|
||||
"zfs",
|
||||
"bcachefs",
|
||||
"vfat",
|
||||
};
|
||||
|
||||
fn isPhysicalFS(fs_type: []const u8) bool {
|
||||
inline for (physical_fs) |pfs| {
|
||||
if (mem.eql(u8, fs_type, pfs)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn getDisksInfo(allocator: mem.Allocator) ![]DiskInfo {
|
||||
var result = try ArrayList(DiskInfo).initCapacity(allocator, 8);
|
||||
|
||||
const file = try fs.openFileAbsolute("/proc/mounts", .{});
|
||||
defer file.close();
|
||||
|
||||
var buf_reader = std.io.bufferedReader(file.reader());
|
||||
var reader = buf_reader.reader();
|
||||
var buf: [1024]u8 = undefined;
|
||||
|
||||
while (try reader.readUntilDelimiterOrEof(&buf, '\n')) |line| {
|
||||
var iter = mem.splitScalar(u8, line, ' ');
|
||||
const device = iter.next() orelse continue;
|
||||
const mount_point = iter.next() orelse continue;
|
||||
const fs_type = iter.next() orelse continue;
|
||||
|
||||
if (device.len == 0) continue;
|
||||
if (device[0] != '/') continue;
|
||||
if (!isPhysicalFS(fs_type)) continue;
|
||||
|
||||
const info = getDiskInfo(mount_point) catch continue;
|
||||
if (info.total < 1024 * 1024 * 1024) continue;
|
||||
|
||||
try result.append(DiskInfo{
|
||||
.mount_point = try allocator.dupe(u8, mount_point),
|
||||
.total = info.total,
|
||||
.used = info.used,
|
||||
});
|
||||
}
|
||||
|
||||
return try result.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub fn getDiskInfo(mount_point: []const u8) !DiskInfo {
|
||||
const Statfs = extern struct {
|
||||
f_type: u64,
|
||||
@ -23,7 +80,10 @@ pub fn getDiskInfo(mount_point: []const u8) !DiskInfo {
|
||||
};
|
||||
|
||||
var stats: Statfs = undefined;
|
||||
const rc = os.linux.syscall2(.statfs, @intFromPtr(mount_point.ptr), @intFromPtr(&stats));
|
||||
var mount_path_buf: [std.fs.max_path_bytes + 1]u8 = undefined;
|
||||
const path = try std.fmt.bufPrint(&mount_path_buf, "{s}\x00", .{mount_point});
|
||||
|
||||
const rc = os.linux.syscall2(.statfs, @intFromPtr(path.ptr), @intFromPtr(&stats));
|
||||
if (rc != 0) return error.StatFailed;
|
||||
|
||||
const total = stats.f_blocks * stats.f_bsize;
|
||||
@ -31,6 +91,7 @@ pub fn getDiskInfo(mount_point: []const u8) !DiskInfo {
|
||||
const used = total - free;
|
||||
|
||||
return DiskInfo{
|
||||
.mount_point = mount_point,
|
||||
.total = total,
|
||||
.used = used,
|
||||
};
|
||||
|
@ -38,6 +38,25 @@ const GPUInfo = struct {
|
||||
};
|
||||
|
||||
pub fn getGPUInfo(allocator: mem.Allocator) ![][]const u8 {
|
||||
const home = try std.process.getEnvVarOwned(allocator, "HOME");
|
||||
defer allocator.free(home);
|
||||
|
||||
const cache_dir = try fs.path.join(allocator, &.{ home, ".config", "pikafetch" });
|
||||
defer allocator.free(cache_dir);
|
||||
|
||||
fs.makeDirAbsolute(cache_dir) catch |err| {
|
||||
if (err != error.PathAlreadyExists) return err;
|
||||
};
|
||||
|
||||
const cache_path = try fs.path.join(allocator, &.{ cache_dir, "gpu_cache" });
|
||||
defer allocator.free(cache_path);
|
||||
|
||||
if (try isCacheValid(cache_path)) {
|
||||
if (try readFromCache(allocator, cache_path)) |gpus| {
|
||||
return gpus;
|
||||
}
|
||||
}
|
||||
|
||||
var gpus = ArrayList([]const u8).init(allocator);
|
||||
|
||||
var dir = fs.openDirAbsolute("/sys/class/drm", .{ .iterate = true }) catch {
|
||||
@ -62,7 +81,9 @@ pub fn getGPUInfo(allocator: mem.Allocator) ![][]const u8 {
|
||||
try gpus.append(unknown);
|
||||
}
|
||||
|
||||
return gpus.toOwnedSlice();
|
||||
const result = try gpus.toOwnedSlice();
|
||||
try updateCache(cache_path, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn detectGPU(allocator: mem.Allocator, card_name: []const u8) !?[]const u8 {
|
||||
@ -245,3 +266,67 @@ fn queryHwdb(allocator: mem.Allocator, card_name: []const u8) !?[]const u8 {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn getBootTime() !i64 {
|
||||
const file = try fs.openFileAbsolute("/proc/stat", .{});
|
||||
defer file.close();
|
||||
|
||||
var buffer: [4096]u8 = undefined;
|
||||
const bytes_read = try file.readAll(&buffer);
|
||||
const content = buffer[0..bytes_read];
|
||||
|
||||
var lines = std.mem.splitScalar(u8, content, '\n');
|
||||
while (lines.next()) |line| {
|
||||
if (std.mem.startsWith(u8, line, "btime ")) {
|
||||
const time_str = std.mem.trim(u8, line["btime ".len..], " \t\r\n");
|
||||
return try std.fmt.parseInt(i64, time_str, 10);
|
||||
}
|
||||
}
|
||||
|
||||
return error.BootTimeNotFound;
|
||||
}
|
||||
|
||||
fn isCacheValid(cache_path: []const u8) !bool {
|
||||
const cache_file = fs.openFileAbsolute(cache_path, .{}) catch return false;
|
||||
defer cache_file.close();
|
||||
|
||||
const cache_stat = try cache_file.stat();
|
||||
const boot_time = try getBootTime();
|
||||
|
||||
const boot_time_ns = boot_time * std.time.ns_per_s;
|
||||
|
||||
return cache_stat.mtime >= boot_time_ns;
|
||||
}
|
||||
|
||||
fn readFromCache(allocator: mem.Allocator, cache_path: []const u8) !?[][]const u8 {
|
||||
const file = try fs.openFileAbsolute(cache_path, .{});
|
||||
defer file.close();
|
||||
|
||||
const stat = try file.stat();
|
||||
const content = try allocator.alloc(u8, @intCast(stat.size));
|
||||
defer allocator.free(content);
|
||||
|
||||
const bytes_read = try file.readAll(content);
|
||||
const file_content = content[0..bytes_read];
|
||||
|
||||
var gpus = ArrayList([]const u8).init(allocator);
|
||||
var lines = std.mem.splitScalar(u8, file_content, '\n');
|
||||
while (lines.next()) |line| {
|
||||
const trimmed = std.mem.trim(u8, line, " \t\r\n");
|
||||
if (trimmed.len == 0) continue;
|
||||
try gpus.append(try allocator.dupe(u8, trimmed));
|
||||
}
|
||||
|
||||
const result = try gpus.toOwnedSlice();
|
||||
if (result.len == 0) return null;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn updateCache(cache_path: []const u8, gpus: []const []const u8) !void {
|
||||
const file = try fs.createFileAbsolute(cache_path, .{});
|
||||
defer file.close();
|
||||
|
||||
for (gpus) |gpu| {
|
||||
try file.writer().print("{s}\n", .{gpu});
|
||||
}
|
||||
}
|
||||
|
@ -27,8 +27,7 @@ pub const SystemInfo = struct {
|
||||
gpus: [][]const u8,
|
||||
swap_total: u64,
|
||||
swap_used: u64,
|
||||
disk_total: u64,
|
||||
disk_used: u64,
|
||||
disks: []disk.DiskInfo,
|
||||
shell_name: []const u8,
|
||||
cpu_info: []const u8,
|
||||
|
||||
@ -58,14 +57,14 @@ pub const SystemInfo = struct {
|
||||
shell_info = shell_data.name;
|
||||
cpu_info = try cpu.getCPUInfo(allocator);
|
||||
const swap_info = try memory.getSwapInfo();
|
||||
const disk_info = try disk.getDiskInfo("/");
|
||||
const disk_info = try disk.getDisksInfo(allocator);
|
||||
|
||||
return SystemInfo{
|
||||
.username = username.?,
|
||||
.hostname = hostname_str.?,
|
||||
.os_name = os_name.?,
|
||||
.kernel = kernel_ver.?,
|
||||
.uptime = 0, // TODO: Implement uptime
|
||||
.uptime = 0,
|
||||
.memory_total = mem_info.total,
|
||||
.memory_used = mem_info.used,
|
||||
.host = host.?,
|
||||
@ -75,41 +74,46 @@ pub const SystemInfo = struct {
|
||||
.gpus = gpu_list.?,
|
||||
.swap_total = swap_info.total,
|
||||
.swap_used = swap_info.used,
|
||||
.disk_total = disk_info.total,
|
||||
.disk_used = disk_info.used,
|
||||
.disks = disk_info,
|
||||
.shell_name = shell_info.?,
|
||||
.cpu_info = cpu_info.?,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn formatInfo(self: *const SystemInfo, output_allocator: std.mem.Allocator) ![]const []const u8 {
|
||||
var info = try std.ArrayList([]const u8).initCapacity(output_allocator, 13 + self.gpus.len);
|
||||
var info = try std.ArrayList([]const u8).initCapacity(output_allocator, 13 + self.gpus.len + self.disks.len);
|
||||
|
||||
const calculations = struct {
|
||||
mem_percentage: u64,
|
||||
swap_percentage: u64,
|
||||
disk_percentage: u64,
|
||||
disk_percentages: []const u64,
|
||||
mem_fmt: memory.SizeInfo,
|
||||
mem_total_fmt: memory.SizeInfo,
|
||||
swap_fmt: memory.SizeInfo,
|
||||
swap_total_fmt: memory.SizeInfo,
|
||||
disk_fmt: memory.SizeInfo,
|
||||
disk_total_fmt: memory.SizeInfo,
|
||||
usage_colors: [3][]const u8,
|
||||
usage_colors: []const []const u8,
|
||||
}{
|
||||
.mem_percentage = @divFloor(self.memory_used * 100, self.memory_total),
|
||||
.swap_percentage = if (self.swap_total > 0) @divFloor(self.swap_used * 100, self.swap_total) else 0,
|
||||
.disk_percentage = if (self.disk_total > 0) @divFloor(self.disk_used * 100, self.disk_total) else 0,
|
||||
.disk_percentages = blk: {
|
||||
var percentages = try output_allocator.alloc(u64, self.disks.len);
|
||||
for (self.disks, 0..) |disk_info, i| {
|
||||
percentages[i] = @divFloor(disk_info.used * 100, disk_info.total);
|
||||
}
|
||||
break :blk percentages;
|
||||
},
|
||||
.mem_fmt = memory.formatSize(self.memory_used),
|
||||
.mem_total_fmt = memory.formatSize(self.memory_total),
|
||||
.swap_fmt = memory.formatSize(self.swap_used),
|
||||
.swap_total_fmt = memory.formatSize(self.swap_total),
|
||||
.disk_fmt = memory.formatSize(self.disk_used),
|
||||
.disk_total_fmt = memory.formatSize(self.disk_total),
|
||||
.usage_colors = .{
|
||||
getUsageColor(@divFloor(self.memory_used * 100, self.memory_total)),
|
||||
getUsageColor(if (self.swap_total > 0) @divFloor(self.swap_used * 100, self.swap_total) else 0),
|
||||
getUsageColor(if (self.disk_total > 0) @divFloor(self.disk_used * 100, self.disk_total) else 0),
|
||||
.usage_colors = blk: {
|
||||
var colors_array = try output_allocator.alloc([]const u8, 2 + self.disks.len);
|
||||
colors_array[0] = getUsageColor(@divFloor(self.memory_used * 100, self.memory_total));
|
||||
colors_array[1] = getUsageColor(if (self.swap_total > 0) @divFloor(self.swap_used * 100, self.swap_total) else 0);
|
||||
for (self.disks, 0..) |disk_info, i| {
|
||||
colors_array[2 + i] = getUsageColor(@divFloor(disk_info.used * 100, disk_info.total));
|
||||
}
|
||||
break :blk colors_array;
|
||||
},
|
||||
};
|
||||
|
||||
@ -135,6 +139,23 @@ pub const SystemInfo = struct {
|
||||
}));
|
||||
}
|
||||
|
||||
for (self.disks, 0..) |disk_info, i| {
|
||||
const disk_fmt = memory.formatSize(disk_info.used);
|
||||
const disk_total_fmt = memory.formatSize(disk_info.total);
|
||||
try info.append(try std.fmt.allocPrint(output_allocator, "{s}Disk {s}:{s} {d:.2} {s} / {d:.2} {s} ({s}{d}%{s})", .{
|
||||
colors.Color.bold,
|
||||
disk_info.mount_point,
|
||||
colors.Color.reset,
|
||||
disk_fmt.value,
|
||||
disk_fmt.unit,
|
||||
disk_total_fmt.value,
|
||||
disk_total_fmt.unit,
|
||||
calculations.usage_colors[2 + i],
|
||||
calculations.disk_percentages[i],
|
||||
colors.Color.reset,
|
||||
}));
|
||||
}
|
||||
|
||||
try info.appendSlice(&[_][]const u8{
|
||||
try std.fmt.allocPrint(output_allocator, "{s}Memory:{s} {d:.2} {s} / {d:.2} {s} ({s}{d}%{s})", .{
|
||||
colors.Color.bold, colors.Color.reset,
|
||||
@ -150,13 +171,6 @@ pub const SystemInfo = struct {
|
||||
calculations.usage_colors[1], calculations.swap_percentage,
|
||||
colors.Color.reset,
|
||||
}),
|
||||
try std.fmt.allocPrint(output_allocator, "{s}Disk (/):{s} {d:.2} {s} / {d:.2} {s} ({s}{d}%{s})", .{
|
||||
colors.Color.bold, colors.Color.reset,
|
||||
calculations.disk_fmt.value, calculations.disk_fmt.unit,
|
||||
calculations.disk_total_fmt.value, calculations.disk_total_fmt.unit,
|
||||
calculations.usage_colors[2], calculations.disk_percentage,
|
||||
colors.Color.reset,
|
||||
}),
|
||||
try std.fmt.allocPrint(output_allocator, "", .{}),
|
||||
try std.fmt.allocPrint(output_allocator, "\x1b[30m███\x1b[31m███\x1b[32m███\x1b[33m███\x1b[34m███\x1b[35m███\x1b[36m███\x1b[37m███{s}", .{
|
||||
colors.Color.reset,
|
||||
|
@ -1,10 +1,77 @@
|
||||
const std = @import("std");
|
||||
const os = std.os;
|
||||
const fs = std.fs;
|
||||
const time = std.time;
|
||||
|
||||
pub fn getDpkgCount() !u32 {
|
||||
const dpkg_path = "/var/lib/dpkg/status";
|
||||
|
||||
// Get cache directory
|
||||
const allocator = std.heap.page_allocator;
|
||||
const home = try std.process.getEnvVarOwned(allocator, "HOME");
|
||||
defer allocator.free(home);
|
||||
|
||||
const cache_dir = try fs.path.join(allocator, &.{ home, ".config", "pikafetch" });
|
||||
defer allocator.free(cache_dir);
|
||||
|
||||
// Ensure cache directory exists, ignore if it already does
|
||||
fs.makeDirAbsolute(cache_dir) catch |err| {
|
||||
if (err != error.PathAlreadyExists) return err;
|
||||
};
|
||||
|
||||
const cache_path = try fs.path.join(allocator, &.{ cache_dir, "dpkg_cache" });
|
||||
defer allocator.free(cache_path);
|
||||
|
||||
// Check if cache exists and is valid
|
||||
if (try isCacheValid(dpkg_path, cache_path)) {
|
||||
return try readFromCache(cache_path);
|
||||
}
|
||||
|
||||
// If cache is invalid or doesn't exist, count packages and update cache
|
||||
const count = try countPackages(dpkg_path);
|
||||
try updateCache(cache_path, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
fn isCacheValid(dpkg_path: []const u8, cache_path: []const u8) !bool {
|
||||
// Check if both files exist
|
||||
const dpkg_file = fs.openFileAbsolute(dpkg_path, .{}) catch return false;
|
||||
defer dpkg_file.close();
|
||||
|
||||
const cache_file = fs.openFileAbsolute(cache_path, .{}) catch return false;
|
||||
defer cache_file.close();
|
||||
|
||||
// Compare modification times
|
||||
const dpkg_stat = try dpkg_file.stat();
|
||||
const cache_stat = try cache_file.stat();
|
||||
|
||||
return cache_stat.mtime >= dpkg_stat.mtime;
|
||||
}
|
||||
|
||||
fn readFromCache(cache_path: []const u8) !u32 {
|
||||
const file = try fs.openFileAbsolute(cache_path, .{});
|
||||
defer file.close();
|
||||
|
||||
var buffer: [128]u8 = undefined;
|
||||
const bytes_read = try file.readAll(&buffer);
|
||||
|
||||
// Parse the cached count
|
||||
const content = buffer[0..bytes_read];
|
||||
const count_str = std.mem.trim(u8, content, &std.ascii.whitespace);
|
||||
return try std.fmt.parseInt(u32, count_str, 10);
|
||||
}
|
||||
|
||||
fn updateCache(cache_path: []const u8, count: u32) !void {
|
||||
const file = try fs.createFileAbsolute(cache_path, .{});
|
||||
defer file.close();
|
||||
|
||||
try std.fmt.format(file.writer(), "{d}", .{count});
|
||||
}
|
||||
|
||||
fn countPackages(dpkg_path: []const u8) !u32 {
|
||||
@setEvalBranchQuota(20000);
|
||||
|
||||
const file = try std.fs.openFileAbsolute("/var/lib/dpkg/status", .{});
|
||||
const file = try fs.openFileAbsolute(dpkg_path, .{});
|
||||
defer file.close();
|
||||
|
||||
const stat = try file.stat();
|
||||
|
Loading…
Reference in New Issue
Block a user