This repository has been archived on 2025-03-31. You can view files and clone it, but cannot push or open issues or pull requests.
idrac-zig/src/main.zig
2025-03-30 17:29:57 -04:00

225 lines
6.9 KiB
Zig

// Depends on having IPMItool installed
const std = @import("std");
const IPMIHOST = "192.168.1.233";
const IPMIUSER = "root";
const IPMIPASS = "<change>";
// Build IPMI command
fn executeIpmiCommand(full_command: []const u8) !void {
const allocator = std.heap.page_allocator;
var args = std.ArrayList([]const u8).init(allocator);
defer args.deinit();
try args.appendSlice(&.{
"ipmitool",
"-I",
"lanplus",
"-H",
IPMIHOST,
"-U",
IPMIUSER,
"-P",
IPMIPASS,
});
var cmd_iter = std.mem.splitSequence(u8, full_command, " ");
while (cmd_iter.next()) |part| {
if (part.len > 0) try args.append(part);
}
const result = try std.process.Child.run(.{
.allocator = allocator,
.argv = args.items,
.max_output_bytes = 1024 * 1024,
});
std.debug.print("{s}\n", .{result.stdout});
if (result.stderr.len > 0) {
std.debug.print("{s}\n", .{result.stderr});
}
if (result.term != .Exited or result.term.Exited != 0) {
return error.CommandFailed;
}
}
// Get server temperature
fn getServerTemperature() !f32 {
const allocator = std.heap.page_allocator;
const result = try std.process.Child.run(.{
.allocator = allocator,
.argv = &.{ "ipmitool", "-I", "lanplus", "-H", IPMIHOST, "-U", IPMIUSER, "-P", IPMIPASS, "sdr", "type", "temperature" },
.max_output_bytes = 1024 * 1024,
});
var temps = std.ArrayList(f32).init(allocator);
defer temps.deinit();
var lines = std.mem.splitSequence(u8, result.stdout, "\n");
while (lines.next()) |line| {
var cols = std.mem.splitSequence(u8, line, "|");
while (cols.next()) |col| {
if (std.mem.indexOf(u8, col, "degrees C")) |_| {
var tokens = std.mem.tokenizeAny(u8, col, " ");
while (tokens.next()) |t| {
if (std.fmt.parseFloat(f32, t)) |temp| {
try temps.append(temp);
} else |_| {}
}
}
}
}
if (temps.items.len == 0) return error.NoData;
var sum: f32 = 0;
for (temps.items) |t| sum += t;
return sum / @as(f32, @floatFromInt(temps.items.len));
}
// Power controls
fn powerOn() !void {
std.debug.print("Powering on the server...\n", .{});
try executeIpmiCommand("chassis power on");
}
fn powerOff() !void {
std.debug.print("Powering off the server...\n", .{});
try executeIpmiCommand("chassis power off");
std.debug.print("Enabling dynamic fan control...\n", .{});
try executeIpmiCommand("raw 0x30 0x30 0x01 0x01");
}
// Fan controls
fn setFanSpeed(speed: u8) !void {
std.debug.print("Setting fan speed to {d}%\n", .{speed});
const cmd = try std.fmt.allocPrint(std.heap.page_allocator, "raw 0x30 0x30 0x02 0xff 0x{x:0>2}", .{speed});
defer std.heap.page_allocator.free(cmd);
try executeIpmiCommand(cmd);
}
fn enableDynamicFanControl() !void {
std.debug.print("Enabling dynamic fan control...\n", .{});
try executeIpmiCommand("raw 0x30 0x30 0x01 0x01");
}
fn disableDynamicFanControl() !void {
std.debug.print("Disabling dynamic fan control...\n", .{});
try executeIpmiCommand("raw 0x30 0x30 0x01 0x00");
}
// Fan speed logic
fn adjustFanSpeedBasedOnTemp() !void {
const temp = try getServerTemperature();
std.debug.print("Current temperature: {d:.1} degrees C\n", .{temp});
const warning_threshold: f32 = 75.0;
const critical_threshold: f32 = 90.0;
var fan_speed: u8 = 40;
if (temp >= critical_threshold) {
std.debug.print("Temperature critical, fans at 100%\n", .{});
fan_speed = 100;
} else if (temp >= warning_threshold) {
std.debug.print("Temperature high, fans at 80%\n", .{});
fan_speed = 80;
} else if (temp >= 40.0) {
std.debug.print("Temperature moderate, fans at 60%\n", .{});
fan_speed = 60;
} else if (temp >= 30.0) {
std.debug.print("Temperature low, fans at 50%\n", .{});
fan_speed = 50;
} else {
std.debug.print("Temperature very low, fans at 40%\n", .{});
}
try setFanSpeed(fan_speed);
}
// Main function
pub fn main() !void {
const args = try std.process.argsAlloc(std.heap.page_allocator);
defer std.process.argsFree(std.heap.page_allocator, args);
var power: ?[]const u8 = null;
var fan: ?u8 = null;
var temp: bool = false;
var dynamic: ?[]const u8 = null;
var i: usize = 1;
while (i < args.len) : (i += 1) {
if (std.mem.eql(u8, args[i], "-p") or std.mem.eql(u8, args[i], "--power")) {
i += 1;
if (i < args.len) {
power = args[i];
} else {
std.debug.print("Missing argument for -p/--power\n", .{});
return error.InvalidArgs;
}
} else if (std.mem.eql(u8, args[i], "-f") or std.mem.eql(u8, args[i], "--fan")) {
i += 1;
if (i < args.len) {
fan = std.fmt.parseInt(u8, args[i], 10) catch {
std.debug.print("Invalid fan speed\n", .{});
return error.InvalidArgs;
};
if (fan.? > 100) {
std.debug.print("Fan speed must be 0-100\n", .{});
return error.InvalidArgs;
}
} else {
std.debug.print("Missing argument for -f/--fan\n", .{});
return error.InvalidArgs;
}
} else if (std.mem.eql(u8, args[i], "-t") or std.mem.eql(u8, args[i], "--temp")) {
temp = true;
} else if (std.mem.eql(u8, args[i], "-d") or std.mem.eql(u8, args[i], "--dynamic")) {
i += 1;
if (i < args.len) {
dynamic = args[i];
} else {
std.debug.print("Missing argument for -d/--dynamic\n", .{});
return error.InvalidArgs;
}
} else {
std.debug.print("Unknown argument: {s}\n", .{args[i]});
return error.InvalidArgs;
}
}
if (power) |pwr| {
if (std.mem.eql(u8, pwr, "on")) {
try powerOn();
} else if (std.mem.eql(u8, pwr, "off")) {
try powerOff();
} else {
std.debug.print("Invalid power option: {s}\n", .{pwr});
return error.InvalidArgs;
}
}
if (fan) |spd| {
try setFanSpeed(spd);
}
if (temp) {
try adjustFanSpeedBasedOnTemp();
}
if (dynamic) |dyn| {
if (std.mem.eql(u8, dyn, "on")) {
try enableDynamicFanControl();
} else if (std.mem.eql(u8, dyn, "off")) {
try disableDynamicFanControl();
} else {
std.debug.print("Invalid dynamic option: {s}\n", .{dyn});
return error.InvalidArgs;
}
}
if (args.len == 1) {
std.debug.print("Usage: {s} [-p on|off] [-f 0-100] [-t] [-d on|off]\n", .{args[0]});
}
}