use block::{Block, ConcreteBlock};
use libc::sleep;
use objc::rc::StrongPtr;
use std::fs::canonicalize;
use virtualization_rs::{
base::{dispatch_async, dispatch_queue_create, Id, NSError, NSFileHandle, NIL},
virtualization::{
boot_loader::VZLinuxBootLoaderBuilder,
entropy_device::VZVirtioEntropyDeviceConfiguration,
memory_device::VZVirtioTraditionalMemoryBalloonDeviceConfiguration,
network_device::{
VZMACAddress, VZNATNetworkDeviceAttachment, VZVirtioNetworkDeviceConfiguration,
},
serial_port::{
VZFileHandleSerialPortAttachmentBuilder, VZVirtioConsoleDeviceSerialPortConfiguration,
},
storage_device::{
VZDiskImageStorageDeviceAttachmentBuilder, VZVirtioBlockDeviceConfiguration,
},
virtual_machine::{VZVirtualMachine, VZVirtualMachineConfigurationBuilder},
},
};
use std::path::PathBuf;
use structopt::StructOpt;
#[derive(StructOpt, Debug)]
#[structopt(name = "simplevm")]
struct Opt {
#[structopt(long, parse(from_os_str))]
kernel: PathBuf,
#[structopt(short, long, parse(from_os_str))]
initrd: PathBuf,
#[structopt(short, long, default_value = "console=hvc0")]
command_line: String,
#[structopt(short, long, parse(from_os_str))]
disk: PathBuf,
#[structopt(short, long, default_value = "4")]
cpu: usize,
#[structopt(short, long, default_value = "2147483648")]
memory_size: usize,
}
fn main() {
let opt = Opt::from_args();
let cpu_count = opt.cpu;
let memory_size = opt.memory_size;
let command_line = opt.command_line;
let kernel = opt.kernel;
let disk = opt.disk;
let initrd = opt.initrd;
if !VZVirtualMachine::supported() {
println!("not supported");
return;
}
let boot_loader = VZLinuxBootLoaderBuilder::new()
.kernel_url(
canonicalize(&kernel)
.unwrap()
.into_os_string()
.into_string()
.unwrap(),
)
.initial_ramdisk_url(
canonicalize(&initrd)
.unwrap()
.into_os_string()
.into_string()
.unwrap(),
)
.command_line(command_line)
.build();
let file_handle_for_reading = NSFileHandle::file_handle_with_standard_input();
let file_handle_for_writing = NSFileHandle::file_handle_with_standard_output();
let attachement = VZFileHandleSerialPortAttachmentBuilder::new()
.file_handle_for_reading(file_handle_for_reading)
.file_handle_for_writing(file_handle_for_writing)
.build();
let serial = VZVirtioConsoleDeviceSerialPortConfiguration::new(attachement);
let entropy = VZVirtioEntropyDeviceConfiguration::new();
let memory_balloon = VZVirtioTraditionalMemoryBalloonDeviceConfiguration::new();
let block_attachment = match VZDiskImageStorageDeviceAttachmentBuilder::new()
.path(
canonicalize(&disk)
.unwrap()
.into_os_string()
.into_string()
.unwrap(),
)
.build()
{
Ok(x) => x,
Err(err) => {
err.dump();
return;
}
};
let block_device = VZVirtioBlockDeviceConfiguration::new(block_attachment);
let network_attachment = VZNATNetworkDeviceAttachment::new();
let mut network_device = VZVirtioNetworkDeviceConfiguration::new(network_attachment);
network_device.set_mac_address(VZMACAddress::random_locally_administered_address());
let conf = VZVirtualMachineConfigurationBuilder::new()
.boot_loader(boot_loader)
.cpu_count(cpu_count)
.memory_size(memory_size)
.entropy_devices(vec![entropy])
.memory_balloon_devices(vec![memory_balloon])
.network_devices(vec![network_device])
.serial_ports(vec![serial])
.storage_devices(vec![block_device])
.build();
match conf.validate_with_error() {
Ok(_) => {
let label = std::ffi::CString::new("second").unwrap();
let queue = unsafe { dispatch_queue_create(label.as_ptr(), NIL) };
let vm = VZVirtualMachine::new(conf, queue);
let dispatch_block = ConcreteBlock::new(move || {
let completion_handler = ConcreteBlock::new(|err: Id| {
if err != NIL {
let error = unsafe { NSError(StrongPtr::new(err)) };
error.dump();
}
});
let completion_handler = completion_handler.copy();
let completion_handler: &Block<(Id,), ()> = &completion_handler;
vm.start_with_completion_handler(completion_handler);
});
let dispatch_block = dispatch_block.copy();
let dispatch_block: &Block<(), ()> = &dispatch_block;
unsafe {
dispatch_async(queue, dispatch_block);
}
loop {
unsafe {
sleep(100);
}
}
}
Err(e) => {
e.dump();
return;
}
}
}