refactor: refactoring of the project structure

This commit is contained in:
utox39
2025-03-20 16:45:29 +01:00
parent 6a8f82dd41
commit 43d0752991
11 changed files with 813 additions and 799 deletions

121
src/linux/hardware.zig Normal file
View File

@@ -0,0 +1,121 @@
const std = @import("std");
const c_unistd = @cImport(@cInclude("unistd.h"));
pub const CpuInfo = struct {
cpu_name: []u8,
cpu_cores: i32,
cpu_max_freq: f32,
};
pub const RamInfo = struct {
ram_size: f64,
ram_usage: f64,
ram_usage_percentage: u8,
};
pub fn getCpuInfo(allocator: std.mem.Allocator) !CpuInfo {
const cpu_cores = c_unistd.sysconf(c_unistd._SC_NPROCESSORS_ONLN);
// Reads /proc/cpuinfo
const cpuinfo_path = "/proc/cpuinfo";
var file = try std.fs.cwd().openFile(cpuinfo_path, .{});
defer file.close();
const cpuinfo_data = try file.readToEndAlloc(allocator, std.math.maxInt(usize));
defer allocator.free(cpuinfo_data);
// Parsing /proc/cpuinfo
var model_name: ?[]const u8 = null;
var lines = std.mem.split(u8, cpuinfo_data, "\n");
while (lines.next()) |line| {
const trimmed = std.mem.trim(u8, line, " \t");
if (std.mem.startsWith(u8, trimmed, "model name") and model_name == null) {
var parts = std.mem.split(u8, trimmed, ":");
_ = parts.next(); // discards the key
if (parts.next()) |value| {
model_name = std.mem.trim(u8, value, " ");
break;
}
}
}
// Reads /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq
const cpuinfo_max_freq_path = "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
var file2 = try std.fs.cwd().openFile(cpuinfo_max_freq_path, .{});
defer file2.close();
const cpuinfo_max_freq_data = try file2.readToEndAlloc(allocator, std.math.maxInt(usize));
defer allocator.free(cpuinfo_max_freq_data);
// Parsing /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq
const trimmed = std.mem.trim(u8, cpuinfo_max_freq_data, " \n\r");
const cpu_max_freq_khz: f32 = try std.fmt.parseFloat(f32, trimmed);
const cpu_max_freq: f32 = cpu_max_freq_khz / 1_000_000;
return CpuInfo{
.cpu_name = try allocator.dupe(u8, model_name orelse "Unknown"),
.cpu_cores = @as(i32, @intCast(cpu_cores)),
.cpu_max_freq = cpu_max_freq,
};
}
pub fn getRamInfo(allocator: std.mem.Allocator) !RamInfo {
// Reads /proc/meminfo
const meminfo_path = "/proc/meminfo";
const file = try std.fs.cwd().openFile(meminfo_path, .{});
defer file.close();
const meminfo_data = try file.readToEndAlloc(allocator, std.math.maxInt(usize));
defer allocator.free(meminfo_data);
// Parsing /proc/meminfo
var total_mem: f64 = 0.0;
var free_mem: f64 = 0.0; // remove?
var available_mem: f64 = 0.0;
var total_mem_str: ?[]const u8 = null;
var free_mem_str: ?[]const u8 = null;
var available_mem_str: ?[]const u8 = null;
var lines = std.mem.split(u8, meminfo_data, "\n");
while (lines.next()) |line| {
const trimmed = std.mem.trim(u8, line, " \t");
if (std.mem.startsWith(u8, trimmed, "MemTotal")) {
var parts = std.mem.split(u8, trimmed, ":");
_ = parts.next(); // discards the key
if (parts.next()) |value| {
total_mem_str = std.mem.trim(u8, value[0..(value.len - 3)], " ");
total_mem = try std.fmt.parseFloat(f64, total_mem_str.?);
}
} else if (std.mem.startsWith(u8, trimmed, "MemFree")) {
var parts = std.mem.split(u8, trimmed, ":");
_ = parts.next(); // discards the key
if (parts.next()) |value| {
free_mem_str = std.mem.trim(u8, value[0..(value.len - 3)], " ");
free_mem = try std.fmt.parseFloat(f64, free_mem_str.?);
}
} else if (std.mem.startsWith(u8, trimmed, "MemAvailable")) {
var parts = std.mem.split(u8, trimmed, ":");
_ = parts.next(); // discards the key
if (parts.next()) |value| {
available_mem_str = std.mem.trim(u8, value[0..(value.len - 3)], " ");
available_mem = try std.fmt.parseFloat(f64, available_mem_str.?);
}
}
if ((total_mem_str != null) and (free_mem_str != null) and (available_mem_str != null)) {
break;
}
}
var used_mem = total_mem - available_mem;
// Converts KB in GB
total_mem /= (1024 * 1024);
used_mem /= (1024 * 1024);
const ram_usage_percentage: u8 = @as(u8, @intFromFloat((used_mem * 100) / total_mem));
return RamInfo{
.ram_size = total_mem,
.ram_usage = used_mem,
.ram_usage_percentage = ram_usage_percentage,
};
}

View File

@@ -1,283 +1,4 @@
const std = @import("std");
const c_sysinfo = @cImport(@cInclude("sys/sysinfo.h"));
const c_unistd = @cImport(@cInclude("unistd.h"));
const c_utsname = @cImport(@cInclude("sys/utsname.h"));
const c_ifaddrs = @cImport(@cInclude("ifaddrs.h"));
const c_inet = @cImport(@cInclude("arpa/inet.h"));
const c_net_if = @cImport(@cInclude("net/if.h"));
const c_netinet_in = @cImport(@cInclude("netinet/in.h"));
const c_socket = @cImport(@cInclude("sys/socket.h"));
/// Structure representing system uptime in days, hours, and minutes.
pub const SystemUptime = struct {
days: i8,
hours: i8,
minutes: i8,
};
pub const CpuInfo = struct {
cpu_name: []u8,
cpu_cores: i32,
cpu_max_freq: f32,
};
pub const RamInfo = struct {
ram_size: f64,
ram_usage: f64,
ram_usage_percentage: u8,
};
pub const KernelInfo = struct {
kernel_name: []u8,
kernel_release: []u8,
};
pub const NetInfo = struct {
interface_name: []u8,
ipv4_addr: []u8,
};
pub fn getUsername(allocator: std.mem.Allocator) ![]u8 {
const username = try std.process.getEnvVarOwned(allocator, "USER");
return username;
}
pub fn getHostname(allocator: std.mem.Allocator) ![]u8 {
var buf: [std.posix.HOST_NAME_MAX]u8 = undefined;
const hostnameEnv = try std.posix.gethostname(&buf);
const hostname = try allocator.dupe(u8, hostnameEnv);
return hostname;
}
/// Returns the system uptime.
///
/// Uses `sysinfo` to fetch the system uptime and calculates the elapsed time.
pub fn getSystemUptime() !SystemUptime {
const seconds_per_day: f64 = 86400.0;
const hours_per_day: f64 = 24.0;
const seconds_per_hour: f64 = 3600.0;
const seconds_per_minute: f64 = 60.0;
var info: c_sysinfo.struct_sysinfo = undefined;
if (c_sysinfo.sysinfo(&info) != 0) {
return error.SysinfoFailed;
}
const uptime_seconds: f64 = @as(f64, @floatFromInt(info.uptime));
var remainig_seconds: f64 = uptime_seconds;
const days: f64 = @floor(remainig_seconds / seconds_per_day);
remainig_seconds = (remainig_seconds / seconds_per_day) - days;
const hours = @floor(remainig_seconds * hours_per_day);
remainig_seconds = (remainig_seconds * hours_per_day) - hours;
const minutes = @floor((remainig_seconds * seconds_per_hour) / seconds_per_minute);
return SystemUptime{
.days = @as(i8, @intFromFloat(days)),
.hours = @as(i8, @intFromFloat(hours)),
.minutes = @as(i8, @intFromFloat(minutes)),
};
}
pub fn getShell(allocator: std.mem.Allocator) ![]u8 {
const shell = try std.process.getEnvVarOwned(allocator, "SHELL");
var child = std.process.Child.init(&[_][]const u8{ shell, "--version" }, allocator);
child.stdout_behavior = .Pipe;
child.stderr_behavior = .Pipe;
try child.spawn();
const output = try child.stdout.?.reader().readAllAlloc(allocator, 1024 * 1024);
_ = try child.wait();
return output;
}
pub fn getCpuInfo(allocator: std.mem.Allocator) !CpuInfo {
const cpu_cores = c_unistd.sysconf(c_unistd._SC_NPROCESSORS_ONLN);
// Reads /proc/cpuinfo
const cpuinfo_path = "/proc/cpuinfo";
var file = try std.fs.cwd().openFile(cpuinfo_path, .{});
defer file.close();
const cpuinfo_data = try file.readToEndAlloc(allocator, std.math.maxInt(usize));
defer allocator.free(cpuinfo_data);
// Parsing /proc/cpuinfo
var model_name: ?[]const u8 = null;
var lines = std.mem.split(u8, cpuinfo_data, "\n");
while (lines.next()) |line| {
const trimmed = std.mem.trim(u8, line, " \t");
if (std.mem.startsWith(u8, trimmed, "model name") and model_name == null) {
var parts = std.mem.split(u8, trimmed, ":");
_ = parts.next(); // discards the key
if (parts.next()) |value| {
model_name = std.mem.trim(u8, value, " ");
break;
}
}
}
// Reads /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq
const cpuinfo_max_freq_path = "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
var file2 = try std.fs.cwd().openFile(cpuinfo_max_freq_path, .{});
defer file2.close();
const cpuinfo_max_freq_data = try file2.readToEndAlloc(allocator, std.math.maxInt(usize));
defer allocator.free(cpuinfo_max_freq_data);
// Parsing /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq
const trimmed = std.mem.trim(u8, cpuinfo_max_freq_data, " \n\r");
const cpu_max_freq_khz: f32 = try std.fmt.parseFloat(f32, trimmed);
const cpu_max_freq: f32 = cpu_max_freq_khz / 1_000_000;
return CpuInfo{
.cpu_name = try allocator.dupe(u8, model_name orelse "Unknown"),
.cpu_cores = @as(i32, @intCast(cpu_cores)),
.cpu_max_freq = cpu_max_freq,
};
}
pub fn getRamInfo(allocator: std.mem.Allocator) !RamInfo {
// Reads /proc/meminfo
const meminfo_path = "/proc/meminfo";
const file = try std.fs.cwd().openFile(meminfo_path, .{});
defer file.close();
const meminfo_data = try file.readToEndAlloc(allocator, std.math.maxInt(usize));
defer allocator.free(meminfo_data);
// Parsing /proc/meminfo
var total_mem: f64 = 0.0;
var free_mem: f64 = 0.0; // remove?
var available_mem: f64 = 0.0;
var total_mem_str: ?[]const u8 = null;
var free_mem_str: ?[]const u8 = null;
var available_mem_str: ?[]const u8 = null;
var lines = std.mem.split(u8, meminfo_data, "\n");
while (lines.next()) |line| {
const trimmed = std.mem.trim(u8, line, " \t");
if (std.mem.startsWith(u8, trimmed, "MemTotal")) {
var parts = std.mem.split(u8, trimmed, ":");
_ = parts.next(); // discards the key
if (parts.next()) |value| {
total_mem_str = std.mem.trim(u8, value[0..(value.len - 3)], " ");
total_mem = try std.fmt.parseFloat(f64, total_mem_str.?);
}
} else if (std.mem.startsWith(u8, trimmed, "MemFree")) {
var parts = std.mem.split(u8, trimmed, ":");
_ = parts.next(); // discards the key
if (parts.next()) |value| {
free_mem_str = std.mem.trim(u8, value[0..(value.len - 3)], " ");
free_mem = try std.fmt.parseFloat(f64, free_mem_str.?);
}
} else if (std.mem.startsWith(u8, trimmed, "MemAvailable")) {
var parts = std.mem.split(u8, trimmed, ":");
_ = parts.next(); // discards the key
if (parts.next()) |value| {
available_mem_str = std.mem.trim(u8, value[0..(value.len - 3)], " ");
available_mem = try std.fmt.parseFloat(f64, available_mem_str.?);
}
}
if ((total_mem_str != null) and (free_mem_str != null) and (available_mem_str != null)) {
break;
}
}
var used_mem = total_mem - available_mem;
// Converts KB in GB
total_mem /= (1024 * 1024);
used_mem /= (1024 * 1024);
const ram_usage_percentage: u8 = @as(u8, @intFromFloat((used_mem * 100) / total_mem));
return RamInfo{
.ram_size = total_mem,
.ram_usage = used_mem,
.ram_usage_percentage = ram_usage_percentage,
};
}
pub fn getKernelInfo(allocator: std.mem.Allocator) !KernelInfo {
var uts: c_utsname.struct_utsname = undefined;
if (c_utsname.uname(&uts) != 0) {
return error.UnameFailed;
}
return KernelInfo{
.kernel_name = try allocator.dupe(u8, &uts.sysname),
.kernel_release = try allocator.dupe(u8, &uts.release),
};
}
pub fn getOsInfo(allocator: std.mem.Allocator) ![]u8 {
const os_release_path = "/etc/os-release";
const file = try std.fs.cwd().openFile(os_release_path, .{});
defer file.close();
const os_release_data = try file.readToEndAlloc(allocator, std.math.maxInt(usize));
defer allocator.free(os_release_data);
var pretty_name: ?[]const u8 = null;
var lines = std.mem.split(u8, os_release_data, "\n");
while (lines.next()) |line| {
if (std.mem.startsWith(u8, line, "PRETTY_NAME")) {
var parts = std.mem.split(u8, line, "=");
_ = parts.next(); // discard the key
if (parts.next()) |value| {
pretty_name = std.mem.trim(u8, value, "\"");
break;
}
}
}
return try allocator.dupe(u8, pretty_name orelse "Unknown");
}
pub fn getTerminalName(allocator: std.mem.Allocator) ![]u8 {
const term_progrm = try std.process.getEnvVarOwned(allocator, "TERM_PROGRAM");
return term_progrm;
}
pub fn getNetInfo(allocator: std.mem.Allocator) !std.ArrayList(NetInfo) {
var net_info_list = std.ArrayList(NetInfo).init(allocator);
var ifap: ?*c_ifaddrs.ifaddrs = null;
if (c_ifaddrs.getifaddrs(&ifap) != 0) {
return error.GetifaddrsFailed;
}
defer c_ifaddrs.freeifaddrs(ifap);
var cur: ?*c_ifaddrs.ifaddrs = ifap;
while (cur) |ifa| : (cur = ifa.ifa_next) {
if (ifa.ifa_addr) |addr| {
// Skips the loopback
if ((ifa.ifa_flags & c_net_if.IFF_LOOPBACK) != 0) continue;
const sockaddr_ptr = @as(*const c_socket.sockaddr, @ptrCast(@alignCast(addr)));
if (sockaddr_ptr.sa_family != c_inet.AF_INET) continue;
var addr_in = @as(*const c_netinet_in.sockaddr_in, @ptrCast(@alignCast(sockaddr_ptr)));
var ip_buf: [c_inet.INET_ADDRSTRLEN]u8 = undefined;
const ip_str = c_inet.inet_ntop(c_inet.AF_INET, &addr_in.sin_addr, &ip_buf, c_inet.INET_ADDRSTRLEN);
if (ip_str) |ip| {
try net_info_list.append(NetInfo{
.interface_name = try allocator.dupe(u8, std.mem.span(ifa.ifa_name)),
.ipv4_addr = try allocator.dupe(u8, std.mem.span(ip)),
});
}
}
}
return net_info_list;
}
pub const hardware = @import("./hardware.zig");
pub const network = @import("./network.zig");
pub const system = @import("./system.zig");
pub const user = @import("./user.zig");

45
src/linux/network.zig Normal file
View File

@@ -0,0 +1,45 @@
const std = @import("std");
const c_ifaddrs = @cImport(@cInclude("ifaddrs.h"));
const c_inet = @cImport(@cInclude("arpa/inet.h"));
const c_net_if = @cImport(@cInclude("net/if.h"));
const c_netinet_in = @cImport(@cInclude("netinet/in.h"));
const c_socket = @cImport(@cInclude("sys/socket.h"));
pub const NetInfo = struct {
interface_name: []u8,
ipv4_addr: []u8,
};
pub fn getNetInfo(allocator: std.mem.Allocator) !std.ArrayList(NetInfo) {
var net_info_list = std.ArrayList(NetInfo).init(allocator);
var ifap: ?*c_ifaddrs.ifaddrs = null;
if (c_ifaddrs.getifaddrs(&ifap) != 0) {
return error.GetifaddrsFailed;
}
defer c_ifaddrs.freeifaddrs(ifap);
var cur: ?*c_ifaddrs.ifaddrs = ifap;
while (cur) |ifa| : (cur = ifa.ifa_next) {
if (ifa.ifa_addr) |addr| {
// Skips the loopback
if ((ifa.ifa_flags & c_net_if.IFF_LOOPBACK) != 0) continue;
const sockaddr_ptr = @as(*const c_socket.sockaddr, @ptrCast(@alignCast(addr)));
if (sockaddr_ptr.sa_family != c_inet.AF_INET) continue;
var addr_in = @as(*const c_netinet_in.sockaddr_in, @ptrCast(@alignCast(sockaddr_ptr)));
var ip_buf: [c_inet.INET_ADDRSTRLEN]u8 = undefined;
const ip_str = c_inet.inet_ntop(c_inet.AF_INET, &addr_in.sin_addr, &ip_buf, c_inet.INET_ADDRSTRLEN);
if (ip_str) |ip| {
try net_info_list.append(NetInfo{
.interface_name = try allocator.dupe(u8, std.mem.span(ifa.ifa_name)),
.ipv4_addr = try allocator.dupe(u8, std.mem.span(ip)),
});
}
}
}
return net_info_list;
}

92
src/linux/system.zig Normal file
View File

@@ -0,0 +1,92 @@
const std = @import("std");
const c_sysinfo = @cImport(@cInclude("sys/sysinfo.h"));
const c_utsname = @cImport(@cInclude("sys/utsname.h"));
/// Structure representing system uptime in days, hours, and minutes.
pub const SystemUptime = struct {
days: i8,
hours: i8,
minutes: i8,
};
pub const KernelInfo = struct {
kernel_name: []u8,
kernel_release: []u8,
};
pub fn getHostname(allocator: std.mem.Allocator) ![]u8 {
var buf: [std.posix.HOST_NAME_MAX]u8 = undefined;
const hostnameEnv = try std.posix.gethostname(&buf);
const hostname = try allocator.dupe(u8, hostnameEnv);
return hostname;
}
/// Returns the system uptime.
///
/// Uses `sysinfo` to fetch the system uptime and calculates the elapsed time.
pub fn getSystemUptime() !SystemUptime {
const seconds_per_day: f64 = 86400.0;
const hours_per_day: f64 = 24.0;
const seconds_per_hour: f64 = 3600.0;
const seconds_per_minute: f64 = 60.0;
var info: c_sysinfo.struct_sysinfo = undefined;
if (c_sysinfo.sysinfo(&info) != 0) {
return error.SysinfoFailed;
}
const uptime_seconds: f64 = @as(f64, @floatFromInt(info.uptime));
var remainig_seconds: f64 = uptime_seconds;
const days: f64 = @floor(remainig_seconds / seconds_per_day);
remainig_seconds = (remainig_seconds / seconds_per_day) - days;
const hours = @floor(remainig_seconds * hours_per_day);
remainig_seconds = (remainig_seconds * hours_per_day) - hours;
const minutes = @floor((remainig_seconds * seconds_per_hour) / seconds_per_minute);
return SystemUptime{
.days = @as(i8, @intFromFloat(days)),
.hours = @as(i8, @intFromFloat(hours)),
.minutes = @as(i8, @intFromFloat(minutes)),
};
}
pub fn getKernelInfo(allocator: std.mem.Allocator) !KernelInfo {
var uts: c_utsname.struct_utsname = undefined;
if (c_utsname.uname(&uts) != 0) {
return error.UnameFailed;
}
return KernelInfo{
.kernel_name = try allocator.dupe(u8, &uts.sysname),
.kernel_release = try allocator.dupe(u8, &uts.release),
};
}
pub fn getOsInfo(allocator: std.mem.Allocator) ![]u8 {
const os_release_path = "/etc/os-release";
const file = try std.fs.cwd().openFile(os_release_path, .{});
defer file.close();
const os_release_data = try file.readToEndAlloc(allocator, std.math.maxInt(usize));
defer allocator.free(os_release_data);
var pretty_name: ?[]const u8 = null;
var lines = std.mem.split(u8, os_release_data, "\n");
while (lines.next()) |line| {
if (std.mem.startsWith(u8, line, "PRETTY_NAME")) {
var parts = std.mem.split(u8, line, "=");
_ = parts.next(); // discard the key
if (parts.next()) |value| {
pretty_name = std.mem.trim(u8, value, "\"");
break;
}
}
}
return try allocator.dupe(u8, pretty_name orelse "Unknown");
}

28
src/linux/user.zig Normal file
View File

@@ -0,0 +1,28 @@
const std = @import("std");
pub fn getUsername(allocator: std.mem.Allocator) ![]u8 {
const username = try std.process.getEnvVarOwned(allocator, "USER");
return username;
}
pub fn getShell(allocator: std.mem.Allocator) ![]u8 {
const shell = try std.process.getEnvVarOwned(allocator, "SHELL");
var child = std.process.Child.init(&[_][]const u8{ shell, "--version" }, allocator);
child.stdout_behavior = .Pipe;
child.stderr_behavior = .Pipe;
try child.spawn();
const output = try child.stdout.?.reader().readAllAlloc(allocator, 1024 * 1024);
_ = try child.wait();
return output;
}
pub fn getTerminalName(allocator: std.mem.Allocator) ![]u8 {
const term_progrm = try std.process.getEnvVarOwned(allocator, "TERM_PROGRAM");
return term_progrm;
}

302
src/macos/hardware.zig Normal file
View File

@@ -0,0 +1,302 @@
const std = @import("std");
const utils = @import("utils.zig");
const c_sysctl = @cImport(@cInclude("sys/sysctl.h"));
const c_iokit = @cImport(@cInclude("IOKit/IOKitLib.h"));
const c_cf = @cImport(@cInclude("CoreFoundation/CoreFoundation.h"));
const c_mach = @cImport(@cInclude("mach/mach.h"));
const c_statvfs = @cImport(@cInclude("sys/statvfs.h"));
pub const CpuInfo = struct {
cpu_name: []u8,
cpu_cores: i32,
cpu_max_freq: f64,
};
pub const GpuInfo = struct {
gpu_name: []u8,
gpu_cores: i32,
};
pub const RamInfo = struct {
ram_size: f64,
ram_usage: f64,
ram_usage_percentage: u8,
};
pub const SwapInfo = struct {
swap_size: f64,
swap_usage: f64,
swap_usage_percentage: u64,
};
pub const DiskInfo = struct {
disk_path: []const u8,
disk_size: f64,
disk_usage: f64,
disk_usage_percentage: u8,
};
pub fn getCpuInfo(allocator: std.mem.Allocator) !CpuInfo {
var size: usize = 0;
// First call to sysctlbyname to get the size of the string
if (c_sysctl.sysctlbyname("machdep.cpu.brand_string", null, &size, null, 0) != 0) {
return error.FailedToGetCpuNameSize;
}
const cpu_name: []u8 = try allocator.alloc(u8, size - 1);
errdefer allocator.free(cpu_name);
// Second call to sysctlbyname to get the CPU name
if (c_sysctl.sysctlbyname("machdep.cpu.brand_string", cpu_name.ptr, &size, null, 0) != 0) {
return error.FailedToGetCpuName;
}
// Call to sysctlbyname to get the cpu cores
var n_cpu: i32 = 0;
size = @sizeOf(i32);
if (c_sysctl.sysctlbyname("hw.ncpu", &n_cpu, &size, null, 0) != 0) {
return error.FailedToGetPhysicalCpuInfo;
}
// TODO: add cpu frequency for Intel
const cpu_freq_mhz = try getCpuFreqAppleSilicon();
const cpu_freq_ghz = @floor(cpu_freq_mhz) / 1000;
return CpuInfo{ .cpu_name = cpu_name, .cpu_cores = n_cpu, .cpu_max_freq = cpu_freq_ghz };
}
fn getCpuFreqAppleSilicon() !f64 {
// https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/cpu/cpu_apple.c
// Retrieve the matching service for "pmgr"
// https://developer.apple.com/documentation/iokit/1514535-ioservicegetmatchingservice
const service = c_iokit.IOServiceGetMatchingService(c_iokit.kIOMasterPortDefault, c_iokit.IOServiceNameMatching("pmgr"));
if (service == c_iokit.FALSE) return error.NoMatchingService;
defer _ = c_iokit.IOObjectRelease(service);
// Check that the service conforms to "AppleARMIODevice"
// https://developer.apple.com/documentation/iokit/1514505-ioobjectconformsto
if (c_iokit.IOObjectConformsTo(service, "AppleARMIODevice") == c_iokit.FALSE) {
return error.NotAppleARMIODevice;
}
// CFSTR is a macro and can't be translated by Zig
// The CFString is created "manually"
const vs5s_key = c_iokit.CFStringCreateWithCString(c_iokit.kCFAllocatorDefault, "voltage-states5-sram", c_iokit.kCFStringEncodingUTF8);
if (vs5s_key == null) {
return error.FailedToCreateCFKey;
}
defer c_iokit.CFRelease(vs5s_key);
// Retrieve the property from the registry entry
// https://developer.apple.com/documentation/iokit/1514293-ioregistryentrycreatecfproperty
const freq_property = c_iokit.IORegistryEntryCreateCFProperty(service, vs5s_key, c_iokit.kCFAllocatorDefault, 0);
if (freq_property == null) return error.PropertyNotFound;
defer c_iokit.CFRelease(freq_property);
// Ensure the property is a CFData object
if (c_iokit.CFGetTypeID(freq_property) != c_cf.CFDataGetTypeID())
return error.InvalidPropertyType;
const freq_data = @as(*const c_iokit.__CFData, @ptrCast(freq_property));
// Get the length of the CFData
const freq_data_length = c_iokit.CFDataGetLength(freq_data);
// voltage-states5-sram stores supported <frequency / voltage> pairs of pcores from the lowest to the highest
if (freq_data_length == 0 or @as(u32, @intCast(freq_data_length)) % (@sizeOf(u32) * 2) != 0)
return error.InvalidVoltageStates5SramLength;
// Get data pointer
const freq_data_ptr = c_iokit.CFDataGetBytePtr(freq_data);
if (freq_data_ptr == null)
return error.InvalidVoltageStates5SramData;
const freq_array = @as([*]const u32, @ptrCast(@alignCast(freq_data_ptr)));
// The first element contains the minimum freq
var p_max: u32 = freq_array[0];
const total_elements = @as(u32, @intCast(freq_data_length)) / @sizeOf(u32);
// Iterate on values, starting at index 2, skipping voltage (each pair is <frequency, voltage>)
var i: usize = 2;
while (i < total_elements) : (i += 2) {
const current = freq_array[i];
if (current == 0) break;
if (current > p_max) {
p_max = current;
}
}
// Assume that p_max is in Hz, M1~M3
if (p_max > 100_000_000) {
return @as(f64, @floatFromInt(p_max)) / 1_000 / 1_000;
} else { // Assume that p_max is in kHz, M4 and later
return @as(f64, @floatFromInt(p_max)) / 1_000;
}
}
pub fn getGpuInfo(allocator: std.mem.Allocator) !GpuInfo {
// TODO: add support for non-Apple Silicon Macs
var gpu_info = GpuInfo{
.gpu_name = try allocator.dupe(u8, "Unknown"),
.gpu_cores = 0,
};
// https://developer.apple.com/documentation/iokit/1514687-ioservicematching
const accel_matching_dict = c_iokit.IOServiceMatching("IOAccelerator");
if (accel_matching_dict == null) {
return error.MatchingDictionaryCreationFailed;
}
var iterator: c_iokit.io_iterator_t = undefined;
// https://developer.apple.com/documentation/iokit/1514494-ioservicegetmatchingservices
const result = c_iokit.IOServiceGetMatchingServices(c_iokit.kIOMasterPortDefault, accel_matching_dict, &iterator);
if (result != c_iokit.KERN_SUCCESS) {
return error.ServiceMatchingFailed;
}
defer _ = c_iokit.IOObjectRelease(iterator);
const service = c_iokit.IOIteratorNext(iterator);
if (service != 0) {
defer _ = c_iokit.IOObjectRelease(service);
var properties_ptr: c_iokit.CFMutableDictionaryRef = null;
const properties_ptr_ref: [*c]c_iokit.CFMutableDictionaryRef = &properties_ptr;
// https://developer.apple.com/documentation/iokit/1514310-ioregistryentrycreatecfpropertie
if (c_iokit.IORegistryEntryCreateCFProperties(service, properties_ptr_ref, c_iokit.kCFAllocatorDefault, 0) != c_iokit.KERN_SUCCESS) {
return gpu_info;
}
if (properties_ptr == null) {
return gpu_info;
}
defer c_iokit.CFRelease(properties_ptr);
var name_ref: c_iokit.CFTypeRef = undefined;
var cores_ref: c_iokit.CFTypeRef = undefined;
// CFSTR is a macro and can't be translated by Zig
// The CFString is created "manually"
const model_key = c_iokit.CFStringCreateWithCString(c_iokit.kCFAllocatorDefault, "model", c_iokit.kCFStringEncodingUTF8);
if (model_key == null) return gpu_info;
defer c_iokit.CFRelease(model_key);
if (c_iokit.CFDictionaryGetValueIfPresent(@as(c_iokit.CFDictionaryRef, @ptrCast(properties_ptr)), model_key, &name_ref) == c_iokit.TRUE) {
if (c_iokit.CFGetTypeID(name_ref) == c_iokit.CFStringGetTypeID()) {
const accel_name = utils.cfStringToZigString(allocator, @as(c_iokit.CFStringRef, @ptrCast(name_ref))) catch {
return gpu_info;
};
allocator.free(gpu_info.gpu_name);
gpu_info.gpu_name = accel_name;
}
}
// CFSTR is a macro and can't be translated by Zig
// The CFString is created "manually"
const gpu_core_count_key = c_iokit.CFStringCreateWithCString(c_iokit.kCFAllocatorDefault, "gpu-core-count", c_iokit.kCFStringEncodingUTF8);
if (gpu_core_count_key == null) return gpu_info;
defer c_iokit.CFRelease(gpu_core_count_key);
if (c_iokit.CFDictionaryGetValueIfPresent(@as(c_iokit.CFDictionaryRef, @ptrCast(properties_ptr)), gpu_core_count_key, &cores_ref) == c_iokit.TRUE) {
if (c_iokit.CFGetTypeID(cores_ref) == c_cf.CFNumberGetTypeID()) {
var cores_num: i32 = 0;
if (c_cf.CFNumberGetValue(@as(c_cf.CFNumberRef, @ptrCast(cores_ref)), c_cf.kCFNumberIntType, &cores_num) == c_cf.TRUE) {
gpu_info.gpu_cores = cores_num;
}
}
}
}
return gpu_info;
}
pub fn getRamInfo() !RamInfo {
// -- RAM SIZE --
var ram_size: u64 = 0;
var ram_size_len: usize = @sizeOf(u64);
var name = [_]c_int{ c_sysctl.CTL_HW, c_sysctl.HW_MEMSIZE };
if (c_sysctl.sysctl(&name, name.len, &ram_size, &ram_size_len, null, 0) != 0) {
return error.FailedToGetRamSize;
}
// Converts Bytes to Gigabytes
const ram_size_gb: f64 = @as(f64, @floatFromInt(ram_size)) / (1024 * 1024 * 1024);
// -- RAM USAGE --
var info: c_mach.vm_statistics64 = undefined;
var count: c_mach.mach_msg_type_number_t = @sizeOf(c_mach.vm_statistics64) / @sizeOf(c_mach.integer_t);
const host_port = c_mach.mach_host_self();
if (c_mach.host_statistics64(host_port, c_mach.HOST_VM_INFO64, @ptrCast(&info), &count) != c_mach.KERN_SUCCESS) {
return error.HostStatistics64Failed;
}
const page_size: u64 = std.heap.page_size_min;
// https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/memory/memory_apple.c
const ram_usage = (info.active_count + info.inactive_count + info.speculative_count + info.wire_count + info.compressor_page_count - info.purgeable_count - info.external_page_count) * page_size;
// Converts Bytes to Gigabytes
const ram_usage_gb: f64 = @as(f64, @floatFromInt(ram_usage)) / (1024 * 1024 * 1024);
const ram_usage_percentage: u8 = @as(u8, @intFromFloat((ram_usage_gb * 100) / ram_size_gb));
return RamInfo{
.ram_size = ram_size_gb,
.ram_usage = ram_usage_gb,
.ram_usage_percentage = ram_usage_percentage,
};
}
pub fn getSwapInfo() !?SwapInfo {
var swap: c_sysctl.struct_xsw_usage = undefined;
var size: usize = @sizeOf(c_sysctl.struct_xsw_usage);
if (c_sysctl.sysctlbyname("vm.swapusage", &swap, &size, null, 0) != 0) {
return error.FailedToGetSwapInfo;
}
const swap_size = @as(f64, @floatFromInt(swap.xsu_total / (1024 * 1024 * 1024)));
const swap_usage = @as(f64, @floatFromInt(swap.xsu_used / (1024 * 1024 * 1024)));
var swap_usage_percentage: u64 = 0;
if (@as(u64, swap.xsu_total) != 0) {
swap_usage_percentage = (@as(u64, swap.xsu_used) * 100) / @as(u64, swap.xsu_total);
} else {
return null;
}
return SwapInfo{
.swap_size = swap_size,
.swap_usage = swap_usage,
.swap_usage_percentage = swap_usage_percentage,
};
}
pub fn getDiskSize(disk_path: []const u8) !DiskInfo {
var stat: c_statvfs.struct_statvfs = undefined;
if (c_statvfs.statvfs(disk_path.ptr, &stat) != 0) {
return error.StatvfsFailed;
}
const total_size = stat.f_blocks * stat.f_frsize;
const free_size = stat.f_bavail * stat.f_frsize;
const used_size = total_size - free_size;
const used_size_percentage = (used_size * 100) / total_size;
return DiskInfo{
.disk_path = disk_path,
.disk_size = @as(f64, @floatFromInt(total_size)) / 1e9,
.disk_usage = @as(f64, @floatFromInt(used_size)) / 1e9,
.disk_usage_percentage = @as(u8, @intCast(used_size_percentage)),
};
}

View File

@@ -1,502 +1,4 @@
const std = @import("std");
const utils = @import("./utils.zig");
const c_libproc = @cImport(@cInclude("libproc.h"));
const c_sysctl = @cImport(@cInclude("sys/sysctl.h"));
const c_iokit = @cImport(@cInclude("IOKit/IOKitLib.h"));
const c_cf = @cImport(@cInclude("CoreFoundation/CoreFoundation.h"));
const c_mach = @cImport(@cInclude("mach/mach.h"));
const c_statvfs = @cImport(@cInclude("sys/statvfs.h"));
const c_ifaddrs = @cImport(@cInclude("ifaddrs.h"));
const c_inet = @cImport(@cInclude("arpa/inet.h"));
const c_net_if = @cImport(@cInclude("net/if.h"));
const c_netinet_in = @cImport(@cInclude("netinet/in.h"));
const c_socket = @cImport(@cInclude("sys/socket.h"));
/// Structure representing system uptime in days, hours, and minutes.
pub const SystemUptime = struct {
days: i8,
hours: i8,
minutes: i8,
};
pub const CpuInfo = struct {
cpu_name: []u8,
cpu_cores: i32,
cpu_max_freq: f64,
};
pub const GpuInfo = struct {
gpu_name: []u8,
gpu_cores: i32,
};
pub const KernelInfo = struct {
kernel_name: []u8,
kernel_release: []u8,
};
pub const RamInfo = struct {
ram_size: f64,
ram_usage: f64,
ram_usage_percentage: u8,
};
pub const DiskInfo = struct {
disk_path: []const u8,
disk_size: f64,
disk_usage: f64,
disk_usage_percentage: u8,
};
pub const SwapInfo = struct {
swap_size: f64,
swap_usage: f64,
swap_usage_percentage: u64,
};
pub const NetInfo = struct {
interface_name: []u8,
ipv4_addr: []u8,
};
/// Returns the current logged-in uesr's username.
/// Uses the environment variable `USER`.
/// The caller is responsible for freeing the allocated memory.
pub fn getUsername(allocator: std.mem.Allocator) ![]u8 {
const username = try std.process.getEnvVarOwned(allocator, "USER");
return username;
}
/// Returns the hostname.
pub fn getHostname(allocator: std.mem.Allocator) ![]u8 {
var buf: [std.posix.HOST_NAME_MAX]u8 = undefined;
const hostnameEnv = try std.posix.gethostname(&buf);
const hostname = try allocator.dupe(u8, hostnameEnv);
return hostname;
}
pub fn getLocale(allocator: std.mem.Allocator) ![]u8 {
const locale = try std.process.getEnvVarOwned(allocator, "LANG");
return locale;
}
/// Returns the system uptime.
///
/// Uses `sysctl` to fetch the system boot time and calculates the elapsed time.
pub fn getSystemUptime() !SystemUptime {
const seconds_per_day: f64 = 86400.0;
const hours_per_day: f64 = 24.0;
const seconds_per_hour: f64 = 3600.0;
const seconds_per_minute: f64 = 60.0;
var boot_time: c_libproc.struct_timeval = undefined;
var size: usize = @sizeOf(c_libproc.struct_timeval);
var uptime_seconds: f64 = 0.0;
var name = [_]c_int{ c_sysctl.CTL_KERN, c_sysctl.KERN_BOOTTIME };
if (c_sysctl.sysctl(&name, name.len, &boot_time, &size, null, 0) == 0) {
const boot_seconds = @as(f64, @floatFromInt(boot_time.tv_sec));
const now_seconds = @as(f64, @floatFromInt(std.time.timestamp()));
uptime_seconds = now_seconds - boot_seconds;
} else {
return error.UnableToGetSystemUptime;
}
var remainig_seconds: f64 = uptime_seconds;
const days: f64 = @floor(remainig_seconds / seconds_per_day);
remainig_seconds = (remainig_seconds / seconds_per_day) - days;
const hours = @floor(remainig_seconds * hours_per_day);
remainig_seconds = (remainig_seconds * hours_per_day) - hours;
const minutes = @floor((remainig_seconds * seconds_per_hour) / seconds_per_minute);
return SystemUptime{
.days = @as(i8, @intFromFloat(days)),
.hours = @as(i8, @intFromFloat(hours)),
.minutes = @as(i8, @intFromFloat(minutes)),
};
}
pub fn getShell(allocator: std.mem.Allocator) ![]u8 {
const shell = try std.process.getEnvVarOwned(allocator, "SHELL");
var child = std.process.Child.init(&[_][]const u8{ shell, "--version" }, allocator);
child.stdout_behavior = .Pipe;
child.stderr_behavior = .Pipe;
try child.spawn();
const output = try child.stdout.?.reader().readAllAlloc(allocator, 1024 * 1024);
_ = try child.wait();
return output;
}
pub fn getCpuInfo(allocator: std.mem.Allocator) !CpuInfo {
var size: usize = 0;
// First call to sysctlbyname to get the size of the string
if (c_sysctl.sysctlbyname("machdep.cpu.brand_string", null, &size, null, 0) != 0) {
return error.FailedToGetCpuNameSize;
}
const cpu_name: []u8 = try allocator.alloc(u8, size - 1);
errdefer allocator.free(cpu_name);
// Second call to sysctlbyname to get the CPU name
if (c_sysctl.sysctlbyname("machdep.cpu.brand_string", cpu_name.ptr, &size, null, 0) != 0) {
return error.FailedToGetCpuName;
}
// Call to sysctlbyname to get the cpu cores
var n_cpu: i32 = 0;
size = @sizeOf(i32);
if (c_sysctl.sysctlbyname("hw.ncpu", &n_cpu, &size, null, 0) != 0) {
return error.FailedToGetPhysicalCpuInfo;
}
// TODO: add cpu frequency for Intel
const cpu_freq_mhz = try getCpuFreqAppleSilicon();
const cpu_freq_ghz = @floor(cpu_freq_mhz) / 1000;
return CpuInfo{ .cpu_name = cpu_name, .cpu_cores = n_cpu, .cpu_max_freq = cpu_freq_ghz };
}
fn getCpuFreqAppleSilicon() !f64 {
// https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/cpu/cpu_apple.c
// Retrieve the matching service for "pmgr"
// https://developer.apple.com/documentation/iokit/1514535-ioservicegetmatchingservice
const service = c_iokit.IOServiceGetMatchingService(c_iokit.kIOMasterPortDefault, c_iokit.IOServiceNameMatching("pmgr"));
if (service == c_iokit.FALSE) return error.NoMatchingService;
defer _ = c_iokit.IOObjectRelease(service);
// Check that the service conforms to "AppleARMIODevice"
// https://developer.apple.com/documentation/iokit/1514505-ioobjectconformsto
if (c_iokit.IOObjectConformsTo(service, "AppleARMIODevice") == c_iokit.FALSE) {
return error.NotAppleARMIODevice;
}
// CFSTR is a macro and can't be translated by Zig
// The CFString is created "manually"
const vs5s_key = c_iokit.CFStringCreateWithCString(c_iokit.kCFAllocatorDefault, "voltage-states5-sram", c_iokit.kCFStringEncodingUTF8);
if (vs5s_key == null) {
return error.FailedToCreateCFKey;
}
defer c_iokit.CFRelease(vs5s_key);
// Retrieve the property from the registry entry
// https://developer.apple.com/documentation/iokit/1514293-ioregistryentrycreatecfproperty
const freq_property = c_iokit.IORegistryEntryCreateCFProperty(service, vs5s_key, c_iokit.kCFAllocatorDefault, 0);
if (freq_property == null) return error.PropertyNotFound;
defer c_iokit.CFRelease(freq_property);
// Ensure the property is a CFData object
if (c_iokit.CFGetTypeID(freq_property) != c_cf.CFDataGetTypeID())
return error.InvalidPropertyType;
const freq_data = @as(*const c_iokit.__CFData, @ptrCast(freq_property));
// Get the length of the CFData
const freq_data_length = c_iokit.CFDataGetLength(freq_data);
// voltage-states5-sram stores supported <frequency / voltage> pairs of pcores from the lowest to the highest
if (freq_data_length == 0 or @as(u32, @intCast(freq_data_length)) % (@sizeOf(u32) * 2) != 0)
return error.InvalidVoltageStates5SramLength;
// Get data pointer
const freq_data_ptr = c_iokit.CFDataGetBytePtr(freq_data);
if (freq_data_ptr == null)
return error.InvalidVoltageStates5SramData;
const freq_array = @as([*]const u32, @ptrCast(@alignCast(freq_data_ptr)));
// The first element contains the minimum freq
var p_max: u32 = freq_array[0];
const total_elements = @as(u32, @intCast(freq_data_length)) / @sizeOf(u32);
// Iterate on values, starting at index 2, skipping voltage (each pair is <frequency, voltage>)
var i: usize = 2;
while (i < total_elements) : (i += 2) {
const current = freq_array[i];
if (current == 0) break;
if (current > p_max) {
p_max = current;
}
}
// Assume that p_max is in Hz, M1~M3
if (p_max > 100_000_000) {
return @as(f64, @floatFromInt(p_max)) / 1_000 / 1_000;
} else { // Assume that p_max is in kHz, M4 and later
return @as(f64, @floatFromInt(p_max)) / 1_000;
}
}
/// Returns the gpu info.
pub fn getGpuInfo(allocator: std.mem.Allocator) !GpuInfo {
// TODO: add support for non-Apple Silicon Macs
var gpu_info = GpuInfo{
.gpu_name = try allocator.dupe(u8, "Unknown"),
.gpu_cores = 0,
};
// https://developer.apple.com/documentation/iokit/1514687-ioservicematching
const accel_matching_dict = c_iokit.IOServiceMatching("IOAccelerator");
if (accel_matching_dict == null) {
return error.MatchingDictionaryCreationFailed;
}
var iterator: c_iokit.io_iterator_t = undefined;
// https://developer.apple.com/documentation/iokit/1514494-ioservicegetmatchingservices
const result = c_iokit.IOServiceGetMatchingServices(c_iokit.kIOMasterPortDefault, accel_matching_dict, &iterator);
if (result != c_iokit.KERN_SUCCESS) {
return error.ServiceMatchingFailed;
}
defer _ = c_iokit.IOObjectRelease(iterator);
const service = c_iokit.IOIteratorNext(iterator);
if (service != 0) {
defer _ = c_iokit.IOObjectRelease(service);
var properties_ptr: c_iokit.CFMutableDictionaryRef = null;
const properties_ptr_ref: [*c]c_iokit.CFMutableDictionaryRef = &properties_ptr;
// https://developer.apple.com/documentation/iokit/1514310-ioregistryentrycreatecfpropertie
if (c_iokit.IORegistryEntryCreateCFProperties(service, properties_ptr_ref, c_iokit.kCFAllocatorDefault, 0) != c_iokit.KERN_SUCCESS) {
return gpu_info;
}
if (properties_ptr == null) {
return gpu_info;
}
defer c_iokit.CFRelease(properties_ptr);
var name_ref: c_iokit.CFTypeRef = undefined;
var cores_ref: c_iokit.CFTypeRef = undefined;
// CFSTR is a macro and can't be translated by Zig
// The CFString is created "manually"
const model_key = c_iokit.CFStringCreateWithCString(c_iokit.kCFAllocatorDefault, "model", c_iokit.kCFStringEncodingUTF8);
if (model_key == null) return gpu_info;
defer c_iokit.CFRelease(model_key);
if (c_iokit.CFDictionaryGetValueIfPresent(@as(c_iokit.CFDictionaryRef, @ptrCast(properties_ptr)), model_key, &name_ref) == c_iokit.TRUE) {
if (c_iokit.CFGetTypeID(name_ref) == c_iokit.CFStringGetTypeID()) {
const accel_name = utils.cfStringToZigString(allocator, @as(c_iokit.CFStringRef, @ptrCast(name_ref))) catch {
return gpu_info;
};
allocator.free(gpu_info.gpu_name);
gpu_info.gpu_name = accel_name;
}
}
// CFSTR is a macro and can't be translated by Zig
// The CFString is created "manually"
const gpu_core_count_key = c_iokit.CFStringCreateWithCString(c_iokit.kCFAllocatorDefault, "gpu-core-count", c_iokit.kCFStringEncodingUTF8);
if (gpu_core_count_key == null) return gpu_info;
defer c_iokit.CFRelease(gpu_core_count_key);
if (c_iokit.CFDictionaryGetValueIfPresent(@as(c_iokit.CFDictionaryRef, @ptrCast(properties_ptr)), gpu_core_count_key, &cores_ref) == c_iokit.TRUE) {
if (c_iokit.CFGetTypeID(cores_ref) == c_cf.CFNumberGetTypeID()) {
var cores_num: i32 = 0;
if (c_cf.CFNumberGetValue(@as(c_cf.CFNumberRef, @ptrCast(cores_ref)), c_cf.kCFNumberIntType, &cores_num) == c_cf.TRUE) {
gpu_info.gpu_cores = cores_num;
}
}
}
}
return gpu_info;
}
pub fn getRamInfo() !RamInfo {
// -- RAM SIZE --
var ram_size: u64 = 0;
var ram_size_len: usize = @sizeOf(u64);
var name = [_]c_int{ c_sysctl.CTL_HW, c_sysctl.HW_MEMSIZE };
if (c_sysctl.sysctl(&name, name.len, &ram_size, &ram_size_len, null, 0) != 0) {
return error.FailedToGetRamSize;
}
// Converts Bytes to Gigabytes
const ram_size_gb: f64 = @as(f64, @floatFromInt(ram_size)) / (1024 * 1024 * 1024);
// -- RAM USAGE --
var info: c_mach.vm_statistics64 = undefined;
var count: c_mach.mach_msg_type_number_t = @sizeOf(c_mach.vm_statistics64) / @sizeOf(c_mach.integer_t);
const host_port = c_mach.mach_host_self();
if (c_mach.host_statistics64(host_port, c_mach.HOST_VM_INFO64, @ptrCast(&info), &count) != c_mach.KERN_SUCCESS) {
return error.HostStatistics64Failed;
}
const page_size: u64 = std.heap.page_size_min;
// https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/memory/memory_apple.c
const ram_usage = (info.active_count + info.inactive_count + info.speculative_count + info.wire_count + info.compressor_page_count - info.purgeable_count - info.external_page_count) * page_size;
// Converts Bytes to Gigabytes
const ram_usage_gb: f64 = @as(f64, @floatFromInt(ram_usage)) / (1024 * 1024 * 1024);
const ram_usage_percentage: u8 = @as(u8, @intFromFloat((ram_usage_gb * 100) / ram_size_gb));
return RamInfo{
.ram_size = ram_size_gb,
.ram_usage = ram_usage_gb,
.ram_usage_percentage = ram_usage_percentage,
};
}
pub fn getDiskSize(disk_path: []const u8) !DiskInfo {
var stat: c_statvfs.struct_statvfs = undefined;
if (c_statvfs.statvfs(disk_path.ptr, &stat) != 0) {
return error.StatvfsFailed;
}
const total_size = stat.f_blocks * stat.f_frsize;
const free_size = stat.f_bavail * stat.f_frsize;
const used_size = total_size - free_size;
const used_size_percentage = (used_size * 100) / total_size;
return DiskInfo{
.disk_path = disk_path,
.disk_size = @as(f64, @floatFromInt(total_size)) / 1e9,
.disk_usage = @as(f64, @floatFromInt(used_size)) / 1e9,
.disk_usage_percentage = @as(u8, @intCast(used_size_percentage)),
};
}
pub fn getTerminalName(allocator: std.mem.Allocator) ![]u8 {
const term_progrm = try std.process.getEnvVarOwned(allocator, "TERM_PROGRAM");
return term_progrm;
}
pub fn getKernelInfo(allocator: std.mem.Allocator) !KernelInfo {
var size: usize = 0;
// --- KERNEL NAME ---
// First call to sysctlbyname to get the size of the string
if (c_sysctl.sysctlbyname("kern.ostype", null, &size, null, 0) != 0) {
return error.FailedToGetKernelNameSize;
}
const kernel_type: []u8 = try allocator.alloc(u8, size - 1);
errdefer allocator.free(kernel_type);
// Second call to sysctlbyname to get the kernel name
if (c_sysctl.sysctlbyname("kern.ostype", kernel_type.ptr, &size, null, 0) != 0) {
return error.FailedToGetKernelName;
}
// --- KERNEL RELEASE ---
// First call to sysctlbyname to get the size of the string
if (c_sysctl.sysctlbyname("kern.osrelease", null, &size, null, 0) != 0) {
return error.FailedToGetKernelReleaseSize;
}
const os_release: []u8 = try allocator.alloc(u8, size - 1);
errdefer allocator.free(os_release);
// Second call to sysctlbyname to get the kernel release
if (c_sysctl.sysctlbyname("kern.osrelease", os_release.ptr, &size, null, 0) != 0) {
return error.FailedToGetKernelRelease;
}
return KernelInfo{
.kernel_name = kernel_type,
.kernel_release = os_release,
};
}
pub fn getOsInfo(allocator: std.mem.Allocator) ![]u8 {
var size: usize = 0;
// First call to sysctlbyname to get the size of the string
if (c_sysctl.sysctlbyname("kern.osproductversion", null, &size, null, 0) != 0) {
return error.FailedToGetCpuNameSize;
}
const os_version: []u8 = try allocator.alloc(u8, size - 1);
defer allocator.free(os_version);
// Second call to sysctlbyname to get the os version
if (c_sysctl.sysctlbyname("kern.osproductversion", os_version.ptr, &size, null, 0) != 0) {
return error.FailedToGetOsVersion;
}
const os_info = try std.fmt.allocPrint(allocator, "macOS {s}", .{os_version});
return os_info;
}
pub fn getSwapInfo() !?SwapInfo {
var swap: c_sysctl.struct_xsw_usage = undefined;
var size: usize = @sizeOf(c_sysctl.struct_xsw_usage);
if (c_sysctl.sysctlbyname("vm.swapusage", &swap, &size, null, 0) != 0) {
return error.FailedToGetSwapInfo;
}
const swap_size = @as(f64, @floatFromInt(swap.xsu_total / (1024 * 1024 * 1024)));
const swap_usage = @as(f64, @floatFromInt(swap.xsu_used / (1024 * 1024 * 1024)));
var swap_usage_percentage: u64 = 0;
if (@as(u64, swap.xsu_total) != 0) {
swap_usage_percentage = (@as(u64, swap.xsu_used) * 100) / @as(u64, swap.xsu_total);
} else {
return null;
}
return SwapInfo{
.swap_size = swap_size,
.swap_usage = swap_usage,
.swap_usage_percentage = swap_usage_percentage,
};
}
pub fn getNetInfo(allocator: std.mem.Allocator) !std.ArrayList(NetInfo) {
var net_info_list = std.ArrayList(NetInfo).init(allocator);
var ifap: ?*c_ifaddrs.ifaddrs = null;
if (c_ifaddrs.getifaddrs(&ifap) != 0) {
return error.GetifaddrsFailed;
}
defer c_ifaddrs.freeifaddrs(ifap);
var cur: ?*c_ifaddrs.ifaddrs = ifap;
while (cur) |ifa| : (cur = ifa.ifa_next) {
if (ifa.ifa_addr) |addr| {
// Skips the loopback
if ((ifa.ifa_flags & c_net_if.IFF_LOOPBACK) != 0) continue;
const sockaddr_ptr = @as(*const c_socket.sockaddr, @ptrCast(@alignCast(addr)));
if (sockaddr_ptr.sa_family != c_inet.AF_INET) continue;
var addr_in = @as(*const c_netinet_in.sockaddr_in, @ptrCast(@alignCast(sockaddr_ptr)));
var ip_buf: [c_inet.INET_ADDRSTRLEN]u8 = undefined;
const ip_str = c_inet.inet_ntop(c_inet.AF_INET, &addr_in.sin_addr, &ip_buf, c_inet.INET_ADDRSTRLEN);
if (ip_str) |ip| {
try net_info_list.append(NetInfo{
.interface_name = try allocator.dupe(u8, std.mem.span(ifa.ifa_name)),
.ipv4_addr = try allocator.dupe(u8, std.mem.span(ip)),
});
}
}
}
return net_info_list;
}
pub const hardware = @import("./hardware.zig");
pub const network = @import("./network.zig");
pub const system = @import("./system.zig");
pub const user = @import("./user.zig");

45
src/macos/network.zig Normal file
View File

@@ -0,0 +1,45 @@
const std = @import("std");
const c_ifaddrs = @cImport(@cInclude("ifaddrs.h"));
const c_inet = @cImport(@cInclude("arpa/inet.h"));
const c_net_if = @cImport(@cInclude("net/if.h"));
const c_netinet_in = @cImport(@cInclude("netinet/in.h"));
const c_socket = @cImport(@cInclude("sys/socket.h"));
pub const NetInfo = struct {
interface_name: []u8,
ipv4_addr: []u8,
};
pub fn getNetInfo(allocator: std.mem.Allocator) !std.ArrayList(NetInfo) {
var net_info_list = std.ArrayList(NetInfo).init(allocator);
var ifap: ?*c_ifaddrs.ifaddrs = null;
if (c_ifaddrs.getifaddrs(&ifap) != 0) {
return error.GetifaddrsFailed;
}
defer c_ifaddrs.freeifaddrs(ifap);
var cur: ?*c_ifaddrs.ifaddrs = ifap;
while (cur) |ifa| : (cur = ifa.ifa_next) {
if (ifa.ifa_addr) |addr| {
// Skips the loopback
if ((ifa.ifa_flags & c_net_if.IFF_LOOPBACK) != 0) continue;
const sockaddr_ptr = @as(*const c_socket.sockaddr, @ptrCast(@alignCast(addr)));
if (sockaddr_ptr.sa_family != c_inet.AF_INET) continue;
var addr_in = @as(*const c_netinet_in.sockaddr_in, @ptrCast(@alignCast(sockaddr_ptr)));
var ip_buf: [c_inet.INET_ADDRSTRLEN]u8 = undefined;
const ip_str = c_inet.inet_ntop(c_inet.AF_INET, &addr_in.sin_addr, &ip_buf, c_inet.INET_ADDRSTRLEN);
if (ip_str) |ip| {
try net_info_list.append(NetInfo{
.interface_name = try allocator.dupe(u8, std.mem.span(ifa.ifa_name)),
.ipv4_addr = try allocator.dupe(u8, std.mem.span(ip)),
});
}
}
}
return net_info_list;
}

127
src/macos/system.zig Normal file
View File

@@ -0,0 +1,127 @@
const std = @import("std");
const c_sysctl = @cImport(@cInclude("sys/sysctl.h"));
const c_libproc = @cImport(@cInclude("libproc.h"));
/// Structure representing system uptime in days, hours, and minutes.
pub const SystemUptime = struct {
days: i8,
hours: i8,
minutes: i8,
};
pub const KernelInfo = struct {
kernel_name: []u8,
kernel_release: []u8,
};
// Returns the hostname.
pub fn getHostname(allocator: std.mem.Allocator) ![]u8 {
var buf: [std.posix.HOST_NAME_MAX]u8 = undefined;
const hostnameEnv = try std.posix.gethostname(&buf);
const hostname = try allocator.dupe(u8, hostnameEnv);
return hostname;
}
pub fn getLocale(allocator: std.mem.Allocator) ![]u8 {
const locale = try std.process.getEnvVarOwned(allocator, "LANG");
return locale;
}
/// Returns the system uptime.
///
/// Uses `sysctl` to fetch the system boot time and calculates the elapsed time.
pub fn getSystemUptime() !SystemUptime {
const seconds_per_day: f64 = 86400.0;
const hours_per_day: f64 = 24.0;
const seconds_per_hour: f64 = 3600.0;
const seconds_per_minute: f64 = 60.0;
var boot_time: c_libproc.struct_timeval = undefined;
var size: usize = @sizeOf(c_libproc.struct_timeval);
var uptime_seconds: f64 = 0.0;
var name = [_]c_int{ c_sysctl.CTL_KERN, c_sysctl.KERN_BOOTTIME };
if (c_sysctl.sysctl(&name, name.len, &boot_time, &size, null, 0) == 0) {
const boot_seconds = @as(f64, @floatFromInt(boot_time.tv_sec));
const now_seconds = @as(f64, @floatFromInt(std.time.timestamp()));
uptime_seconds = now_seconds - boot_seconds;
} else {
return error.UnableToGetSystemUptime;
}
var remainig_seconds: f64 = uptime_seconds;
const days: f64 = @floor(remainig_seconds / seconds_per_day);
remainig_seconds = (remainig_seconds / seconds_per_day) - days;
const hours = @floor(remainig_seconds * hours_per_day);
remainig_seconds = (remainig_seconds * hours_per_day) - hours;
const minutes = @floor((remainig_seconds * seconds_per_hour) / seconds_per_minute);
return SystemUptime{
.days = @as(i8, @intFromFloat(days)),
.hours = @as(i8, @intFromFloat(hours)),
.minutes = @as(i8, @intFromFloat(minutes)),
};
}
pub fn getKernelInfo(allocator: std.mem.Allocator) !KernelInfo {
var size: usize = 0;
// --- KERNEL NAME ---
// First call to sysctlbyname to get the size of the string
if (c_sysctl.sysctlbyname("kern.ostype", null, &size, null, 0) != 0) {
return error.FailedToGetKernelNameSize;
}
const kernel_type: []u8 = try allocator.alloc(u8, size - 1);
errdefer allocator.free(kernel_type);
// Second call to sysctlbyname to get the kernel name
if (c_sysctl.sysctlbyname("kern.ostype", kernel_type.ptr, &size, null, 0) != 0) {
return error.FailedToGetKernelName;
}
// --- KERNEL RELEASE ---
// First call to sysctlbyname to get the size of the string
if (c_sysctl.sysctlbyname("kern.osrelease", null, &size, null, 0) != 0) {
return error.FailedToGetKernelReleaseSize;
}
const os_release: []u8 = try allocator.alloc(u8, size - 1);
errdefer allocator.free(os_release);
// Second call to sysctlbyname to get the kernel release
if (c_sysctl.sysctlbyname("kern.osrelease", os_release.ptr, &size, null, 0) != 0) {
return error.FailedToGetKernelRelease;
}
return KernelInfo{
.kernel_name = kernel_type,
.kernel_release = os_release,
};
}
pub fn getOsInfo(allocator: std.mem.Allocator) ![]u8 {
var size: usize = 0;
// First call to sysctlbyname to get the size of the string
if (c_sysctl.sysctlbyname("kern.osproductversion", null, &size, null, 0) != 0) {
return error.FailedToGetCpuNameSize;
}
const os_version: []u8 = try allocator.alloc(u8, size - 1);
defer allocator.free(os_version);
// Second call to sysctlbyname to get the os version
if (c_sysctl.sysctlbyname("kern.osproductversion", os_version.ptr, &size, null, 0) != 0) {
return error.FailedToGetOsVersion;
}
const os_info = try std.fmt.allocPrint(allocator, "macOS {s}", .{os_version});
return os_info;
}

31
src/macos/user.zig Normal file
View File

@@ -0,0 +1,31 @@
const std = @import("std");
/// Returns the current logged-in uesr's username.
/// Uses the environment variable `USER`.
/// The caller is responsible for freeing the allocated memory.
pub fn getUsername(allocator: std.mem.Allocator) ![]u8 {
const username = try std.process.getEnvVarOwned(allocator, "USER");
return username;
}
pub fn getShell(allocator: std.mem.Allocator) ![]u8 {
const shell = try std.process.getEnvVarOwned(allocator, "SHELL");
var child = std.process.Child.init(&[_][]const u8{ shell, "--version" }, allocator);
child.stdout_behavior = .Pipe;
child.stderr_behavior = .Pipe;
try child.spawn();
const output = try child.stdout.?.reader().readAllAlloc(allocator, 1024 * 1024);
_ = try child.wait();
return output;
}
pub fn getTerminalName(allocator: std.mem.Allocator) ![]u8 {
const term_progrm = try std.process.getEnvVarOwned(allocator, "TERM_PROGRAM");
return term_progrm;
}

View File

@@ -9,56 +9,56 @@ pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const kernel_info = try detection.getKernelInfo(allocator);
const kernel_info = try detection.system.getKernelInfo(allocator);
try stdout.print("Kernel: {s} {s}\n", .{ kernel_info.kernel_name, kernel_info.kernel_release });
try bw.flush();
allocator.free(kernel_info.kernel_name);
allocator.free(kernel_info.kernel_release);
const os_info = try detection.getOsInfo(allocator);
const os_info = try detection.system.getOsInfo(allocator);
try stdout.print("OS: {s}\n", .{os_info});
try bw.flush();
allocator.free(os_info);
const username = try detection.getUsername(allocator);
const username = try detection.user.getUsername(allocator);
try stdout.print("User: {s}\n", .{username});
try bw.flush();
allocator.free(username);
const hostname = try detection.getHostname(allocator);
const hostname = try detection.system.getHostname(allocator);
try stdout.print("Hostname: {s}\n", .{hostname});
try bw.flush();
allocator.free(hostname);
const locale = try detection.getLocale(allocator);
const locale = try detection.system.getLocale(allocator);
try stdout.print("Locale: {s}\n", .{locale});
try bw.flush();
allocator.free(locale);
const uptime = try detection.getSystemUptime();
const uptime = try detection.system.getSystemUptime();
try stdout.print("Uptime: {} days, {} hours, {} minutes\n", .{ uptime.days, uptime.hours, uptime.minutes });
try bw.flush();
const shell = try detection.getShell(allocator);
const shell = try detection.user.getShell(allocator);
try stdout.print("Shell: {s}", .{shell});
try bw.flush();
allocator.free(shell);
const cpu_info = try detection.getCpuInfo(allocator);
const cpu_info = try detection.hardware.getCpuInfo(allocator);
try stdout.print("cpu: {s} ({}) @ {d:.2} GHz\n", .{ cpu_info.cpu_name, cpu_info.cpu_cores, cpu_info.cpu_max_freq });
try bw.flush();
allocator.free(cpu_info.cpu_name);
const gpu_info = try detection.getGpuInfo(allocator);
const gpu_info = try detection.hardware.getGpuInfo(allocator);
try stdout.print("gpu: {s} ({})\n", .{ gpu_info.gpu_name, gpu_info.gpu_cores });
try bw.flush();
allocator.free(gpu_info.gpu_name);
const ram_info = try detection.getRamInfo();
const ram_info = try detection.hardware.getRamInfo();
try stdout.print("ram: {d:.2} / {d:.2} GB ({}%)\n", .{ ram_info.ram_usage, ram_info.ram_size, ram_info.ram_usage_percentage });
try bw.flush();
const swap_info = try detection.getSwapInfo();
const swap_info = try detection.hardware.getSwapInfo();
if (swap_info) |s| {
try stdout.print("Swap: {d:.2} / {d:.2} GB ({}%)\n", .{ s.swap_usage, s.swap_size, s.swap_usage_percentage });
} else {
@@ -66,16 +66,16 @@ pub fn main() !void {
}
try bw.flush();
const diskInfo = try detection.getDiskSize("/");
const diskInfo = try detection.hardware.getDiskSize("/");
try stdout.print("disk ({s}): {d:.2} / {d:.2} GB ({}%)\n", .{ diskInfo.disk_path, diskInfo.disk_usage, diskInfo.disk_size, diskInfo.disk_usage_percentage });
try bw.flush();
const terminal_name = try detection.getTerminalName(allocator);
const terminal_name = try detection.user.getTerminalName(allocator);
try stdout.print("terminal: {s}\n", .{terminal_name});
try bw.flush();
allocator.free(terminal_name);
const net_info_list = try detection.getNetInfo(allocator);
const net_info_list = try detection.network.getNetInfo(allocator);
for (net_info_list.items) |n| {
try stdout.print("Local IP ({s}): {s}\n", .{ n.interface_name, n.ipv4_addr });
try bw.flush();