virtualization-rs 0.1.2

virtualization-rs provides the API of the Apple Virtualization.framework in Rust language.
Documentation
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;
        }
    }
}