zig libvirt: add String struct for handling null-terminated strings

This commit is contained in:
Jeeves 2024-06-25 10:09:22 -06:00
parent 8ae3069450
commit 30fcde4b5b
2 changed files with 62 additions and 32 deletions

View file

@ -12,7 +12,7 @@ pub const c = @cImport({
fn handleError() VirError { fn handleError() VirError {
const err = c.virGetLastError(); const err = c.virGetLastError();
std.debug.print("err: {any}\n", .{err}); // std.debug.print("err: {any}\n", .{err});
return switch (err.*.code) { return switch (err.*.code) {
c.VIR_ERR_OK => VirError.OK, c.VIR_ERR_OK => VirError.OK,
c.VIR_ERR_INTERNAL_ERROR => VirError.InternalError, c.VIR_ERR_INTERNAL_ERROR => VirError.InternalError,
@ -239,6 +239,27 @@ pub const VirError = error{
NoNetworkMetadata, NoNetworkMetadata,
}; };
// Helper struct for allocating null-terminated strings
pub const String = struct {
allocator: mem.Allocator,
slice: []u8,
str: [:0]const u8,
pub fn init(allocator: mem.Allocator, in_str: []const u8) !String {
const str = try allocator.alloc(u8, in_str.len + 1);
@memcpy(str[0..in_str.len], in_str);
str[str.len - 1] = 0;
return .{
.allocator = allocator,
.slice = str,
.str = @ptrCast(str),
};
}
pub fn deinit(self: *const String) void {
self.allocator.free(self.slice);
}
};
fn string(str: [*c]const u8) []const u8 { fn string(str: [*c]const u8) []const u8 {
return mem.span(str); return mem.span(str);
} }
@ -348,12 +369,12 @@ pub const Connection = struct {
); );
} }
pub fn createPool(self: *const Connection, xml: []const u8, flags: []const Pool.CreateFlags) !Pool { pub fn createPool(self: *const Connection, xml: String, flags: []const Pool.CreateFlags) !Pool {
const pool = c.virStoragePoolCreateXML(self.ptr, @ptrCast(xml), intFromFlags(Pool.CreateFlags, flags)); const pool = c.virStoragePoolCreateXML(self.ptr, @ptrCast(xml.str), intFromFlags(Pool.CreateFlags, flags));
return if (pool) |p| Pool.init(p, self.allocator) else handleError(); return if (pool) |p| Pool.init(p, self.allocator) else handleError();
} }
pub fn definePoolFlags(self: *const Connection, xml: []const u8, flags: []const Pool.DefineFlags) !Pool { pub fn definePoolFlags(self: *const Connection, xml: String, flags: []const Pool.DefineFlags) !Pool {
const pool = c.virStoragePoolCreateXML(self.ptr, @ptrCast(xml), intFromFlags(Pool.DefineFlags, flags)); const pool = c.virStoragePoolCreateXML(self.ptr, @ptrCast(xml.str), intFromFlags(Pool.DefineFlags, flags));
return if (pool) |p| Pool.init(p, self.allocator) else handleError(); return if (pool) |p| Pool.init(p, self.allocator) else handleError();
} }
@ -376,16 +397,16 @@ pub const Connection = struct {
); );
} }
pub fn createDomain(self: *const Connection, xml: []const u8, flags: []const Domain.CreateFlags) !Domain { pub fn createDomain(self: *const Connection, xml: String, flags: []const Domain.CreateFlags) !Domain {
const domain = c.virDomainCreateXML(self.ptr, @ptrCast(xml), intFromFlags(Domain.CreateFlags, flags)); const domain = c.virDomainCreateXML(self.ptr, @ptrCast(xml.str), intFromFlags(Domain.CreateFlags, flags));
return if (domain) |d| Domain.init(d, self.allocator) else handleError(); return if (domain) |d| Domain.init(d, self.allocator) else handleError();
} }
pub fn defineDomain(self: *const Connection, xml: []const u8) !Domain { pub fn defineDomain(self: *const Connection, xml: String) !Domain {
const domain = c.virDomainDefineXML(self.ptr, @ptrCast(xml)); const domain = c.virDomainDefineXML(self.ptr, @ptrCast(xml.str));
return if (domain) |d| Domain.init(d, self.allocator) else handleError(); return if (domain) |d| Domain.init(d, self.allocator) else handleError();
} }
pub fn defineDomainFlags(self: *const Connection, xml: []const u8, flags: []const Domain.DefineFlags) !Domain { pub fn defineDomainFlags(self: *const Connection, xml: String, flags: []const Domain.DefineFlags) !Domain {
const domain = c.virDomainDefineXMLFlags(self.ptr, @ptrCast(xml), intFromFlags(Domain.DefineFlags, flags)); const domain = c.virDomainDefineXMLFlags(self.ptr, @ptrCast(xml.str), intFromFlags(Domain.DefineFlags, flags));
return if (domain) |d| Domain.init(d, self.allocator) else handleError(); return if (domain) |d| Domain.init(d, self.allocator) else handleError();
} }
}; };
@ -413,8 +434,13 @@ pub const Pool = struct {
// return if (str.len == 0) handleError() else try self.arena.allocator().dupe(u8, str); // return if (str.len == 0) handleError() else try self.arena.allocator().dupe(u8, str);
} }
pub fn createVolume(self: *const Pool, xml: []const u8, flags: []const Volume.CreateFlags) !Volume { pub fn createVolume(self: *const Pool, xml: String, flags: []const Volume.CreateFlags) !Volume {
const volume = c.virStorageVolCreateXML(self.ptr, @ptrCast(xml), intFromFlags(Volume.CreateFlags, flags)); const volume = c.virStorageVolCreateXML(self.ptr, @ptrCast(xml.str), intFromFlags(Volume.CreateFlags, flags));
return if (volume) |v| Volume.init(v, self.arena.allocator()) else handleError();
}
pub fn lookupVolumeByName(self: *const Pool, name: []const u8) !Volume {
const volume = c.virStorageVolLookupByName(self.ptr, @ptrCast(name));
return if (volume) |v| Volume.init(v, self.arena.allocator()) else handleError(); return if (volume) |v| Volume.init(v, self.arena.allocator()) else handleError();
} }

View file

@ -6,9 +6,9 @@ const fs = std.fs;
const libvirt = @import("libvirt.zig"); const libvirt = @import("libvirt.zig");
pub fn main() !void { pub fn main() !void {
var gpa = heap.GeneralPurposeAllocator(.{}){}; var arena = heap.ArenaAllocator.init(heap.raw_c_allocator);
defer _ = gpa.deinit(); defer arena.deinit();
const allocator = gpa.allocator(); const allocator = arena.allocator();
const connection = try libvirt.Connection.connect("qemu:///system", allocator); const connection = try libvirt.Connection.connect("qemu:///system", allocator);
defer connection.close(); defer connection.close();
@ -20,7 +20,7 @@ pub fn main() !void {
nix.cwd_dir = flake_dir; nix.cwd_dir = flake_dir;
_ = try nix.spawnAndWait(); _ = try nix.spawnAndWait();
nix.argv = &[_][]const u8{ "nix", "build", ".#beforeInstall", "-o", "beforeInstall" }; nix.argv = &[_][]const u8{ "nix", "build", ".#beforeInstall", "-o", "beforeInstall" };
// _ = try nix.spawnAndWait(); _ = try nix.spawnAndWait();
nix.argv = &[_][]const u8{ "nix", "build", ".#afterInstall", "-o", "afterInstall" }; nix.argv = &[_][]const u8{ "nix", "build", ".#afterInstall", "-o", "afterInstall" };
// _ = try nix.spawnAndWait(); // _ = try nix.spawnAndWait();
nix.argv = &[_][]const u8{ "nix", "build", ".#beforeBoot", "-o", "beforeBoot" }; nix.argv = &[_][]const u8{ "nix", "build", ".#beforeBoot", "-o", "beforeBoot" };
@ -32,7 +32,11 @@ pub fn main() !void {
defer volume_def.close(); defer volume_def.close();
const volume_xml = try volume_def.readToEndAlloc(allocator, 1024 * 1024); const volume_xml = try volume_def.readToEndAlloc(allocator, 1024 * 1024);
defer allocator.free(volume_xml); defer allocator.free(volume_xml);
// const volume = try connection.defineVolume(volume_xml);
const domain_beforeinstall_def = try flake_dir.openFile("beforeInstall", .{});
defer domain_beforeinstall_def.close();
const domain_beforeinstall_xml = try domain_beforeinstall_def.readToEndAlloc(allocator, 1024 * 1024 * 1024);
defer allocator.free(domain_beforeinstall_xml);
// -------------- // --------------
@ -68,22 +72,22 @@ pub fn main() !void {
const name = try pool.getName(); const name = try pool.getName();
// std.debug.print("name: {s}\n", .{name}); // std.debug.print("name: {s}\n", .{name});
if (mem.eql(u8, name, "default")) { if (mem.eql(u8, name, "default")) {
// std.debug.print("xml: {s}\n\n", .{volume_xml}); const vol_str = try libvirt.String.init(allocator, volume_xml);
const xml_str = defer vol_str.deinit();
\\<volume> const volume = pool.createVolume(vol_str, &[_]libvirt.Pool.Volume.CreateFlags{}) catch |err| blk: {
\\ <name>wintest.qcow2</name> if (err == libvirt.VirError.StorageVolExist) {
\\ <capacity unit='GB'>16</capacity> break :blk try pool.lookupVolumeByName("wintest.qcow2");
\\ <target> } else return err;
\\ <format type='qcow2'/> };
\\ </target> _ = volume;
\\</volume> break;
;
const volume = try pool.createVolume(xml_str, &[_]libvirt.Pool.Volume.CreateFlags{
libvirt.Pool.Volume.CreateFlags.Validate,
});
std.debug.print("pool: {any}\n", .{volume});
} }
} }
const dom_str = try libvirt.String.init(allocator, domain_beforeinstall_xml);
defer dom_str.deinit();
const domain = try connection.createDomain(dom_str, &[_]libvirt.Domain.CreateFlags{});
_ = domain;
} }
pub const DomainSpec = struct { pub const DomainSpec = struct {