<commit_before>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

extern crate backtrace;
extern crate libc;

extern crate api_server;
#[macro_use]
extern crate logger;
extern crate mmds;
extern crate seccomp;
extern crate utils;
extern crate vmm;

use backtrace::Backtrace;

use std::fs;
use std::io;
use std::panic;
use std::path::PathBuf;
use std::process;
use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError};
use std::sync::{Arc, RwLock};
use std::thread;

use api_server::{ApiServer, Error, VmmRequest, VmmResponse};
use logger::{Metric, LOGGER, METRICS};
use mmds::MMDS;
use seccomp::{BpfInstructionSlice, BpfProgram, SeccompLevel};
use utils::arg_parser::{App, ArgInfo};
use utils::eventfd::EventFd;
use utils::terminal::Terminal;
use utils::validators::validate_instance_id;
use vmm::default_syscalls::get_seccomp_filter;
use vmm::signal_handler::register_signal_handlers;
use vmm::vmm_config::instance_info::{InstanceInfo, InstanceState};
use vmm::{EventLoopExitReason, Vmm};

const DEFAULT_API_SOCK_PATH: &str = "/tmp/firecracker.socket";
const DEFAULT_INSTANCE_ID: &str = "anonymous-instance";
const FIRECRACKER_VERSION: &str = env!("CARGO_PKG_VERSION");

fn main() {
    LOGGER
        .preinit(Some(DEFAULT_INSTANCE_ID.to_string()))
        .expect("Failed to register logger");

    if let Err(e) = register_signal_handlers() {
        error!("Failed to register signal handlers: {}", e);
        process::exit(i32::from(vmm::FC_EXIT_CODE_GENERIC_ERROR));
    }

    // We need this so that we can reset terminal to canonical mode if panic occurs.
    let stdin = io::stdin();

    // Start firecracker by setting up a panic hook, which will be called before
    // terminating as we're building with panic = "abort".
    // It's worth noting that the abort is caused by sending a SIG_ABORT signal to the process.
    panic::set_hook(Box::new(move |info| {
        // We're currently using the closure parameter, which is a &PanicInfo, for printing the
        // origin of the panic, including the payload passed to panic! and the source code location
        // from which the panic originated.
        error!("Firecracker {}", info);
        if let Err(e) = stdin.lock().set_canon_mode() {
            error!(
                "Failure while trying to reset stdin to canonical mode: {}",
                e
            );
        }

        METRICS.vmm.panic_count.inc();
        let bt = Backtrace::new();
        error!("{:?}", bt);

        // Log the metrics before aborting.
        if let Err(e) = LOGGER.log_metrics() {
            error!("Failed to log metrics while panicking: {}", e);
        }
    }));

    let mut app = App::new()
        .name("Firecracker")
        .version(FIRECRACKER_VERSION)
        .header("Launch a microVM.")
        .arg(
            ArgInfo::new("api-sock")
                .takes_value(true)
                .default_value(DEFAULT_API_SOCK_PATH)
                .help("Path to unix domain socket used by the API"),
        )
        .arg(
            ArgInfo::new("id")
                .takes_value(true)
                .default_value(DEFAULT_INSTANCE_ID)
                .help("MicroVM unique identifier"),
        )
        .arg(
            ArgInfo::new("seccomp-level")
                .takes_value(true)
                .default_value("2")
                .help(
                    "Level of seccomp filtering that will be passed to executed path as \
                    argument.\n
                        - Level 0: No filtering.\n
                        - Level 1: Seccomp filtering by syscall number.\n
                        - Level 2: Seccomp filtering by syscall number and argument values.\n
                    ",
                ),
        )
        .arg(
            ArgInfo::new("start-time-us")
                .takes_value(true),
        )
        .arg(
            ArgInfo::new("start-time-cpu-us")
                .takes_value(true),
        )
        .arg(
            ArgInfo::new("config-file")
                .takes_value(true)
                .help("Path to a file that contains the microVM configuration in JSON format."),
        )
        .arg(
            ArgInfo::new("no-api")
                .takes_value(false)
                .requires("config-file")
                .help("Optional parameter which allows starting and using a microVM without an active API socket.")
        );

    let arguments = match app.parse_cmdline_args() {
        Err(err) => {
            error!(
                "Arguments parsing error: {} \n\n\
                 For more information try --help.",
                err
            );
            process::exit(i32::from(vmm::FC_EXIT_CODE_ARG_PARSING));
        }
        _ => {
            if let Some(help) = app.arguments().value_as_bool("help") {
                if help {
                    println!("{}", app.format_help());
                    process::exit(i32::from(vmm::FC_EXIT_CODE_OK));
                }
            }
            app.arguments()
        }
    };

    let bind_path = arguments
        .value_as_string("api-sock")
        .map(PathBuf::from)
        .expect("Missing argument: api_sock");

    // It's safe to unwrap here because the field's been provided with a default value.
    let instance_id = arguments.value_as_string("id").unwrap();
    validate_instance_id(instance_id.as_str()).expect("Invalid instance ID");

    // It's safe to unwrap here because the field's been provided with a default value.
    let seccomp_level = arguments.value_as_string("seccomp-level").unwrap();
    let seccomp_filter = get_seccomp_filter(
        SeccompLevel::from_string(seccomp_level).unwrap_or_else(|err| {
            panic!("Invalid value for seccomp-level: {}", err);
        }),
    )
    .unwrap_or_else(|err| {
        panic!("Could not create seccomp filter: {}", err);
    });

    let start_time_us = arguments.value_as_string("start-time-us").map(|s| {
        s.parse::<u64>()
            .expect("'start-time-us' parameter expected to be of 'u64' type.")
    });

    let start_time_cpu_us = arguments.value_as_string("start-time-cpu-us").map(|s| {
        s.parse::<u64>()
            .expect("'start-time-cpu_us' parameter expected to be of 'u64' type.")
    });

    let vmm_config_json = arguments
        .value_as_string("config-file")
        .map(fs::read_to_string)
        .map(|x| x.expect("Unable to open or read from the configuration file"));

    let no_api = arguments.value_as_bool("no-api").unwrap_or(false);

    let api_shared_info = Arc::new(RwLock::new(InstanceInfo {
        state: InstanceState::Uninitialized,
        id: instance_id.clone(),
        vmm_version: FIRECRACKER_VERSION.to_string(),
    }));

    let request_event_fd = EventFd::new(libc::EFD_NONBLOCK)
        .map_err(Error::Eventfd)
        .expect("Cannot create API Eventfd.");
    let (to_vmm, from_api) = channel();
    let (to_api, from_vmm) = channel();

    // Api enabled.
    if !no_api {
        // MMDS only supported with API.
        let mmds_info = MMDS.clone();
        let vmm_shared_info = api_shared_info.clone();
        let to_vmm_event_fd = request_event_fd.try_clone().unwrap();
        let api_server_filter = seccomp_filter.clone();

        thread::Builder::new()
            .name("fc_api".to_owned())
            .spawn(move || {
                match ApiServer::new(
                    mmds_info,
                    vmm_shared_info,
                    to_vmm,
                    from_vmm,
                    to_vmm_event_fd,
                )
                .expect("Cannot create API server")
                .bind_and_run(
                    bind_path,
                    start_time_us,
                    start_time_cpu_us,
                    api_server_filter,
                ) {
                    Ok(_) => (),
                    Err(Error::Io(inner)) => match inner.kind() {
                        io::ErrorKind::AddrInUse => {
                            panic!("Failed to open the API socket: {:?}", Error::Io(inner))
                        }
                        _ => panic!(
                            "Failed to communicate with the API socket: {:?}",
                            Error::Io(inner)
                        ),
                    },
                    Err(eventfd_err @ Error::Eventfd(_)) => {
                        panic!("Failed to open the API socket: {:?}", eventfd_err)
                    }
                }
            })
            .expect("API thread spawn failed.");
    }

    start_vmm(
        api_shared_info,
        request_event_fd,
        from_api,
        to_api,
        seccomp_filter,
        vmm_config_json,
    );
}

/// Creates and starts a vmm.
///
/// # Arguments
///
/// * `api_shared_info` - A parameter for storing information on the VMM (e.g the current state).
/// * `api_event_fd` - An event fd used for receiving API associated events.
/// * `from_api` - The receiver end point of the communication channel.
/// * `seccomp_filter` - The seccomp filter used. Filters are loaded before executing
///                     guest code. The caller is responsible for providing a correct filter.
/// * `config_json` - Optional parameter that can be used to configure the guest machine without
///                   using the API socket.
fn start_vmm(
    api_shared_info: Arc<RwLock<InstanceInfo>>,
    api_event_fd: EventFd,
    from_api: Receiver<VmmRequest>,
    to_api: Sender<VmmResponse>,
    seccomp_filter: BpfProgram,
    config_json: Option<String>,
) {
    // If this fails, consider it fatal. Use expect().
    let mut vmm = Vmm::new(api_shared_info, &api_event_fd).expect("Cannot create VMM");
    let vmm_seccomp_filter = seccomp_filter.clone();
    let vcpu_seccomp_filter = seccomp_filter.clone();

    if let Some(json) = config_json {
        vmm.configure_from_json(json).unwrap_or_else(|err| {
            error!(
                "Setting configuration for VMM from one single json failed: {}",
                err
            );
            process::exit(i32::from(vmm::FC_EXIT_CODE_BAD_CONFIGURATION));
        });
        vmm.start_microvm(vmm_seccomp_filter.clone(), vcpu_seccomp_filter.clone())
            .unwrap_or_else(|err| {
                error!(
                    "Starting microvm that was configured from one single json failed: {}",
                    err
                );
                process::exit(i32::from(vmm::FC_EXIT_CODE_UNEXPECTED_ERROR));
            });
        info!("Successfully started microvm that was configured from one single json");
    }

    let exit_code = loop {
        match vmm.run_event_loop() {
            Err(e) => {
                error!("Abruptly exited VMM control loop: {:?}", e);
                break vmm::FC_EXIT_CODE_GENERIC_ERROR;
            }
            Ok(exit_reason) => match exit_reason {
                EventLoopExitReason::Break => {
                    info!("Gracefully terminated VMM control loop");
                    break vmm::FC_EXIT_CODE_OK;
                }
                EventLoopExitReason::ControlAction => {
                    if let Err(exit_code) = vmm_control_event(
                        &mut vmm,
                        &api_event_fd,
                        &from_api,
                        &to_api,
                        &vmm_seccomp_filter[..],
                        &vcpu_seccomp_filter[..],
                    ) {
                        break exit_code;
                    }
                }
            },
        };
    };

    vmm.stop(i32::from(exit_code));
}

/// Handles the control event.
/// Receives and runs the Vmm action and sends back a response.
/// Provides program exit codes on errors.
fn vmm_control_event(
    vmm: &mut Vmm,
    api_event_fd: &EventFd,
    from_api: &Receiver<VmmRequest>,
    to_api: &Sender<VmmResponse>,
    vmm_seccomp_filter: &BpfInstructionSlice,
    vcpu_seccomp_filter: &BpfInstructionSlice,
) -> Result<(), u8> {
    api_event_fd.read().map_err(|e| {
        error!("VMM: Failed to read the API event_fd: {}", e);
        vmm::FC_EXIT_CODE_GENERIC_ERROR
    })?;

    match from_api.try_recv() {
        Ok(vmm_request) => {
            use api_server::VmmAction::*;
            let action_request = *vmm_request;
            let response = match action_request {
                ConfigureBootSource(boot_source_body) => vmm
                    .configure_boot_source(boot_source_body)
                    .map(|_| api_server::VmmData::Empty),
                ConfigureLogger(logger_description) => vmm
                    .init_logger(logger_description)
                    .map(|_| api_server::VmmData::Empty),
                FlushMetrics => vmm.flush_metrics().map(|_| api_server::VmmData::Empty),
                GetVmConfiguration => Ok(api_server::VmmData::MachineConfiguration(
                    vmm.vm_config().clone(),
                )),
                InsertBlockDevice(block_device_config) => vmm
                    .insert_block_device(block_device_config)
                    .map(|_| api_server::VmmData::Empty),
                InsertNetworkDevice(netif_body) => vmm
                    .insert_net_device(netif_body)
                    .map(|_| api_server::VmmData::Empty),
                SetVsockDevice(vsock_cfg) => vmm
                    .set_vsock_device(vsock_cfg)
                    .map(|_| api_server::VmmData::Empty),
                RescanBlockDevice(drive_id) => vmm
                    .rescan_block_device(&drive_id)
                    .map(|_| api_server::VmmData::Empty),
                StartMicroVm => vmm
                    .start_microvm(
                        vmm_seccomp_filter.to_owned(),
                        vcpu_seccomp_filter.to_owned(),
                    )
                    .map(|_| api_server::VmmData::Empty),
                #[cfg(target_arch = "x86_64")]
                SendCtrlAltDel => vmm.send_ctrl_alt_del().map(|_| api_server::VmmData::Empty),
                SetVmConfiguration(machine_config_body) => vmm
                    .set_vm_configuration(machine_config_body)
                    .map(|_| api_server::VmmData::Empty),
                UpdateBlockDevicePath(drive_id, path_on_host) => vmm
                    .set_block_device_path(drive_id, path_on_host)
                    .map(|_| api_server::VmmData::Empty),
                UpdateNetworkInterface(netif_update) => vmm
                    .update_net_device(netif_update)
                    .map(|_| api_server::VmmData::Empty),
            };
            // Run the requested action and send back the result.
            to_api
                .send(Box::new(response))
                .map_err(|_| ())
                .expect("one-shot channel closed");
        }
        Err(TryRecvError::Empty) => {
            warn!("Got a spurious notification from api thread");
        }
        Err(TryRecvError::Disconnected) => {
            panic!("The channel's sending half was disconnected. Cannot receive data.");
        }
    };
    Ok(())
}
<commit_msg>firecracker: change default api socket path<commit_after>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

extern crate backtrace;
extern crate libc;

extern crate api_server;
#[macro_use]
extern crate logger;
extern crate mmds;
extern crate seccomp;
extern crate utils;
extern crate vmm;

use backtrace::Backtrace;

use std::fs;
use std::io;
use std::panic;
use std::path::PathBuf;
use std::process;
use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError};
use std::sync::{Arc, RwLock};
use std::thread;

use api_server::{ApiServer, Error, VmmRequest, VmmResponse};
use logger::{Metric, LOGGER, METRICS};
use mmds::MMDS;
use seccomp::{BpfInstructionSlice, BpfProgram, SeccompLevel};
use utils::arg_parser::{App, ArgInfo};
use utils::eventfd::EventFd;
use utils::terminal::Terminal;
use utils::validators::validate_instance_id;
use vmm::default_syscalls::get_seccomp_filter;
use vmm::signal_handler::register_signal_handlers;
use vmm::vmm_config::instance_info::{InstanceInfo, InstanceState};
use vmm::{EventLoopExitReason, Vmm};

// The reason we place default API socket under /run is that API socket is a
// runtime file.
// see https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch03s15.html for more information.
const DEFAULT_API_SOCK_PATH: &str = "/run/firecracker.socket";
const DEFAULT_INSTANCE_ID: &str = "anonymous-instance";
const FIRECRACKER_VERSION: &str = env!("CARGO_PKG_VERSION");

fn main() {
    LOGGER
        .preinit(Some(DEFAULT_INSTANCE_ID.to_string()))
        .expect("Failed to register logger");

    if let Err(e) = register_signal_handlers() {
        error!("Failed to register signal handlers: {}", e);
        process::exit(i32::from(vmm::FC_EXIT_CODE_GENERIC_ERROR));
    }

    // We need this so that we can reset terminal to canonical mode if panic occurs.
    let stdin = io::stdin();

    // Start firecracker by setting up a panic hook, which will be called before
    // terminating as we're building with panic = "abort".
    // It's worth noting that the abort is caused by sending a SIG_ABORT signal to the process.
    panic::set_hook(Box::new(move |info| {
        // We're currently using the closure parameter, which is a &PanicInfo, for printing the
        // origin of the panic, including the payload passed to panic! and the source code location
        // from which the panic originated.
        error!("Firecracker {}", info);
        if let Err(e) = stdin.lock().set_canon_mode() {
            error!(
                "Failure while trying to reset stdin to canonical mode: {}",
                e
            );
        }

        METRICS.vmm.panic_count.inc();
        let bt = Backtrace::new();
        error!("{:?}", bt);

        // Log the metrics before aborting.
        if let Err(e) = LOGGER.log_metrics() {
            error!("Failed to log metrics while panicking: {}", e);
        }
    }));

    let mut app = App::new()
        .name("Firecracker")
        .version(FIRECRACKER_VERSION)
        .header("Launch a microVM.")
        .arg(
            ArgInfo::new("api-sock")
                .takes_value(true)
                .default_value(DEFAULT_API_SOCK_PATH)
                .help("Path to unix domain socket used by the API"),
        )
        .arg(
            ArgInfo::new("id")
                .takes_value(true)
                .default_value(DEFAULT_INSTANCE_ID)
                .help("MicroVM unique identifier"),
        )
        .arg(
            ArgInfo::new("seccomp-level")
                .takes_value(true)
                .default_value("2")
                .help(
                    "Level of seccomp filtering that will be passed to executed path as \
                    argument.\n
                        - Level 0: No filtering.\n
                        - Level 1: Seccomp filtering by syscall number.\n
                        - Level 2: Seccomp filtering by syscall number and argument values.\n
                    ",
                ),
        )
        .arg(
            ArgInfo::new("start-time-us")
                .takes_value(true),
        )
        .arg(
            ArgInfo::new("start-time-cpu-us")
                .takes_value(true),
        )
        .arg(
            ArgInfo::new("config-file")
                .takes_value(true)
                .help("Path to a file that contains the microVM configuration in JSON format."),
        )
        .arg(
            ArgInfo::new("no-api")
                .takes_value(false)
                .requires("config-file")
                .help("Optional parameter which allows starting and using a microVM without an active API socket.")
        );

    let arguments = match app.parse_cmdline_args() {
        Err(err) => {
            error!(
                "Arguments parsing error: {} \n\n\
                 For more information try --help.",
                err
            );
            process::exit(i32::from(vmm::FC_EXIT_CODE_ARG_PARSING));
        }
        _ => {
            if let Some(help) = app.arguments().value_as_bool("help") {
                if help {
                    println!("{}", app.format_help());
                    process::exit(i32::from(vmm::FC_EXIT_CODE_OK));
                }
            }
            app.arguments()
        }
    };

    let bind_path = arguments
        .value_as_string("api-sock")
        .map(PathBuf::from)
        .expect("Missing argument: api_sock");

    // It's safe to unwrap here because the field's been provided with a default value.
    let instance_id = arguments.value_as_string("id").unwrap();
    validate_instance_id(instance_id.as_str()).expect("Invalid instance ID");

    // It's safe to unwrap here because the field's been provided with a default value.
    let seccomp_level = arguments.value_as_string("seccomp-level").unwrap();
    let seccomp_filter = get_seccomp_filter(
        SeccompLevel::from_string(seccomp_level).unwrap_or_else(|err| {
            panic!("Invalid value for seccomp-level: {}", err);
        }),
    )
    .unwrap_or_else(|err| {
        panic!("Could not create seccomp filter: {}", err);
    });

    let start_time_us = arguments.value_as_string("start-time-us").map(|s| {
        s.parse::<u64>()
            .expect("'start-time-us' parameter expected to be of 'u64' type.")
    });

    let start_time_cpu_us = arguments.value_as_string("start-time-cpu-us").map(|s| {
        s.parse::<u64>()
            .expect("'start-time-cpu_us' parameter expected to be of 'u64' type.")
    });

    let vmm_config_json = arguments
        .value_as_string("config-file")
        .map(fs::read_to_string)
        .map(|x| x.expect("Unable to open or read from the configuration file"));

    let no_api = arguments.value_as_bool("no-api").unwrap_or(false);

    let api_shared_info = Arc::new(RwLock::new(InstanceInfo {
        state: InstanceState::Uninitialized,
        id: instance_id.clone(),
        vmm_version: FIRECRACKER_VERSION.to_string(),
    }));

    let request_event_fd = EventFd::new(libc::EFD_NONBLOCK)
        .map_err(Error::Eventfd)
        .expect("Cannot create API Eventfd.");
    let (to_vmm, from_api) = channel();
    let (to_api, from_vmm) = channel();

    // Api enabled.
    if !no_api {
        // MMDS only supported with API.
        let mmds_info = MMDS.clone();
        let vmm_shared_info = api_shared_info.clone();
        let to_vmm_event_fd = request_event_fd.try_clone().unwrap();
        let api_server_filter = seccomp_filter.clone();

        thread::Builder::new()
            .name("fc_api".to_owned())
            .spawn(move || {
                match ApiServer::new(
                    mmds_info,
                    vmm_shared_info,
                    to_vmm,
                    from_vmm,
                    to_vmm_event_fd,
                )
                .expect("Cannot create API server")
                .bind_and_run(
                    bind_path,
                    start_time_us,
                    start_time_cpu_us,
                    api_server_filter,
                ) {
                    Ok(_) => (),
                    Err(Error::Io(inner)) => match inner.kind() {
                        io::ErrorKind::AddrInUse => {
                            panic!("Failed to open the API socket: {:?}", Error::Io(inner))
                        }
                        _ => panic!(
                            "Failed to communicate with the API socket: {:?}",
                            Error::Io(inner)
                        ),
                    },
                    Err(eventfd_err @ Error::Eventfd(_)) => {
                        panic!("Failed to open the API socket: {:?}", eventfd_err)
                    }
                }
            })
            .expect("API thread spawn failed.");
    }

    start_vmm(
        api_shared_info,
        request_event_fd,
        from_api,
        to_api,
        seccomp_filter,
        vmm_config_json,
    );
}

/// Creates and starts a vmm.
///
/// # Arguments
///
/// * `api_shared_info` - A parameter for storing information on the VMM (e.g the current state).
/// * `api_event_fd` - An event fd used for receiving API associated events.
/// * `from_api` - The receiver end point of the communication channel.
/// * `seccomp_filter` - The seccomp filter used. Filters are loaded before executing
///                     guest code. The caller is responsible for providing a correct filter.
/// * `config_json` - Optional parameter that can be used to configure the guest machine without
///                   using the API socket.
fn start_vmm(
    api_shared_info: Arc<RwLock<InstanceInfo>>,
    api_event_fd: EventFd,
    from_api: Receiver<VmmRequest>,
    to_api: Sender<VmmResponse>,
    seccomp_filter: BpfProgram,
    config_json: Option<String>,
) {
    // If this fails, consider it fatal. Use expect().
    let mut vmm = Vmm::new(api_shared_info, &api_event_fd).expect("Cannot create VMM");
    let vmm_seccomp_filter = seccomp_filter.clone();
    let vcpu_seccomp_filter = seccomp_filter.clone();

    if let Some(json) = config_json {
        vmm.configure_from_json(json).unwrap_or_else(|err| {
            error!(
                "Setting configuration for VMM from one single json failed: {}",
                err
            );
            process::exit(i32::from(vmm::FC_EXIT_CODE_BAD_CONFIGURATION));
        });
        vmm.start_microvm(vmm_seccomp_filter.clone(), vcpu_seccomp_filter.clone())
            .unwrap_or_else(|err| {
                error!(
                    "Starting microvm that was configured from one single json failed: {}",
                    err
                );
                process::exit(i32::from(vmm::FC_EXIT_CODE_UNEXPECTED_ERROR));
            });
        info!("Successfully started microvm that was configured from one single json");
    }

    let exit_code = loop {
        match vmm.run_event_loop() {
            Err(e) => {
                error!("Abruptly exited VMM control loop: {:?}", e);
                break vmm::FC_EXIT_CODE_GENERIC_ERROR;
            }
            Ok(exit_reason) => match exit_reason {
                EventLoopExitReason::Break => {
                    info!("Gracefully terminated VMM control loop");
                    break vmm::FC_EXIT_CODE_OK;
                }
                EventLoopExitReason::ControlAction => {
                    if let Err(exit_code) = vmm_control_event(
                        &mut vmm,
                        &api_event_fd,
                        &from_api,
                        &to_api,
                        &vmm_seccomp_filter[..],
                        &vcpu_seccomp_filter[..],
                    ) {
                        break exit_code;
                    }
                }
            },
        };
    };

    vmm.stop(i32::from(exit_code));
}

/// Handles the control event.
/// Receives and runs the Vmm action and sends back a response.
/// Provides program exit codes on errors.
fn vmm_control_event(
    vmm: &mut Vmm,
    api_event_fd: &EventFd,
    from_api: &Receiver<VmmRequest>,
    to_api: &Sender<VmmResponse>,
    vmm_seccomp_filter: &BpfInstructionSlice,
    vcpu_seccomp_filter: &BpfInstructionSlice,
) -> Result<(), u8> {
    api_event_fd.read().map_err(|e| {
        error!("VMM: Failed to read the API event_fd: {}", e);
        vmm::FC_EXIT_CODE_GENERIC_ERROR
    })?;

    match from_api.try_recv() {
        Ok(vmm_request) => {
            use api_server::VmmAction::*;
            let action_request = *vmm_request;
            let response = match action_request {
                ConfigureBootSource(boot_source_body) => vmm
                    .configure_boot_source(boot_source_body)
                    .map(|_| api_server::VmmData::Empty),
                ConfigureLogger(logger_description) => vmm
                    .init_logger(logger_description)
                    .map(|_| api_server::VmmData::Empty),
                FlushMetrics => vmm.flush_metrics().map(|_| api_server::VmmData::Empty),
                GetVmConfiguration => Ok(api_server::VmmData::MachineConfiguration(
                    vmm.vm_config().clone(),
                )),
                InsertBlockDevice(block_device_config) => vmm
                    .insert_block_device(block_device_config)
                    .map(|_| api_server::VmmData::Empty),
                InsertNetworkDevice(netif_body) => vmm
                    .insert_net_device(netif_body)
                    .map(|_| api_server::VmmData::Empty),
                SetVsockDevice(vsock_cfg) => vmm
                    .set_vsock_device(vsock_cfg)
                    .map(|_| api_server::VmmData::Empty),
                RescanBlockDevice(drive_id) => vmm
                    .rescan_block_device(&drive_id)
                    .map(|_| api_server::VmmData::Empty),
                StartMicroVm => vmm
                    .start_microvm(
                        vmm_seccomp_filter.to_owned(),
                        vcpu_seccomp_filter.to_owned(),
                    )
                    .map(|_| api_server::VmmData::Empty),
                #[cfg(target_arch = "x86_64")]
                SendCtrlAltDel => vmm.send_ctrl_alt_del().map(|_| api_server::VmmData::Empty),
                SetVmConfiguration(machine_config_body) => vmm
                    .set_vm_configuration(machine_config_body)
                    .map(|_| api_server::VmmData::Empty),
                UpdateBlockDevicePath(drive_id, path_on_host) => vmm
                    .set_block_device_path(drive_id, path_on_host)
                    .map(|_| api_server::VmmData::Empty),
                UpdateNetworkInterface(netif_update) => vmm
                    .update_net_device(netif_update)
                    .map(|_| api_server::VmmData::Empty),
            };
            // Run the requested action and send back the result.
            to_api
                .send(Box::new(response))
                .map_err(|_| ())
                .expect("one-shot channel closed");
        }
        Err(TryRecvError::Empty) => {
            warn!("Got a spurious notification from api thread");
        }
        Err(TryRecvError::Disconnected) => {
            panic!("The channel's sending half was disconnected. Cannot receive data.");
        }
    };
    Ok(())
}
<|endoftext|>


<commit_before>use fuse::{ReplyAttr, ReplyData, ReplyEntry, ReplyDirectory, Request};
use libc::ENOENT;

use fs::constants;
use fs::GoodDataFS;
use fs::helpers::create_inode_file_attributes;
use object;

mod feature_flags;
mod ldm;
mod metadata;
mod project_json;
mod user_permissions;
mod user_roles;

use super::item;
use super::project;

use super::super::inode;

use std::path::Path;

pub const PROJECT_FILES: [item::ProjectItem; 4] =
    [feature_flags::ITEM, project_json::ITEM, user_permissions::ITEM, user_roles::ITEM];

pub const PROJECT_DIRS: [item::ProjectItem; 2] = [ldm::ITEM, metadata::ITEM];

pub const PROJECT_ITEMS: [item::ProjectItem; 6] = [feature_flags::ITEM,
                                                   project_json::ITEM,
                                                   user_permissions::ITEM,
                                                   user_roles::ITEM,
                                                   ldm::ITEM,
                                                   metadata::ITEM];

/// Gets project from inode
pub fn project_from_inode<Type: Into<inode::Inode>>(fs: &GoodDataFS, ino: Type) -> object::Project {
    let inode = ino.into();
    let pid = (inode.project - 1) as usize;

    fs.client().projects().as_ref().unwrap()[pid].clone()
}

pub fn getattr(fs: &mut GoodDataFS, req: &Request, ino: u64, reply: ReplyAttr) {
    let inode = inode::Inode::deserialize(ino);
    println!("fs::project::getattr() {} - {:?}", ino, inode);

    if inode.project > 0 {
        match inode.category {
            x if x == constants::Category::Internal as u8 => {
                let reserved = constants::ReservedFile::from(inode.reserved);
                match reserved {
                    constants::ReservedFile::FeatureFlagsJson => {
                        (feature_flags::ITEM.getattr)(fs, req, ino, reply)
                    }
                    constants::ReservedFile::ProjectJson => {
                        (project_json::ITEM.getattr)(fs, req, ino, reply)
                    }
                    constants::ReservedFile::PermissionsJson => {
                        (user_permissions::ITEM.getattr)(fs, req, ino, reply)
                    }
                    constants::ReservedFile::RolesJson => {
                        (user_roles::ITEM.getattr)(fs, req, ino, reply)
                    }
                    _ => reply.error(ENOENT),
                }
            }
            x if x == constants::Category::Ldm as u8 => (ldm::ITEM.getattr)(fs, req, ino, reply),
            x if x == constants::Category::Metadata as u8 => {
                (metadata::ITEM.getattr)(fs, req, ino, reply)
            }
            x if x == constants::Category::MetadataAttributes as u8 => {
                (metadata::attributes::ITEM.getattr)(fs, req, ino, reply)
            }
            x if x == constants::Category::MetadataFacts as u8 => {
                (metadata::facts::ITEM.getattr)(fs, req, ino, reply)
            }
            x if x == constants::Category::MetadataMetrics as u8 => {
                (metadata::metrics::ITEM.getattr)(fs, req, ino, reply)
            }
            x if x == constants::Category::MetadataReports as u8 => {
                if inode.reserved == constants::ReservedFile::KeepMe as u8 {
                    (metadata::reports::ITEM.getattr)(fs, req, ino, reply)
                } else if inode.reserved == 0 {
                    // JSON REPORT
                    let project: &object::Project = &project_from_inode(fs, ino);

                    let report = &project.reports(&mut fs.client.connector)
                        .objects
                        .items[inode.item as usize];

                    let json: String = report.clone().into();
                    let attr = create_inode_file_attributes(ino,
                                                            json.len() as u64,
                                                            constants::DEFAULT_CREATE_TIME);
                    reply.attr(&constants::DEFAULT_TTL, &attr);
                    println!("Getting attributes (report.json) {:?}", inode);
                } else {
                    println!("N A S T A L   H A P R ! ! !");
                    reply.error(ENOENT);
                }
            }
            x if x == constants::Category::MetadataReportDefinition as u8 => {
                (metadata::report_definitions::ITEM.getattr)(fs, req, ino, reply)
            }
            _ => {
                println!("fs::project::getattr() - not found!");
            }
        }
    } else {
        println!("GoodDataFS::getattr() - Not found inode {:?}", ino);
        reply.error(ENOENT);
    }
}

pub fn lookup(fs: &mut GoodDataFS, req: &Request, parent: u64, name: &Path, reply: ReplyEntry) {
    let inode = inode::Inode::deserialize(parent);

    match name.to_str() {
        Some(constants::FEATURE_FLAGS_JSON_FILENAME) => {
            (feature_flags::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::PROJECT_JSON_FILENAME) => {
            (project_json::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::PROJECT_LDM_DIR) => (ldm::ITEM.lookup)(fs, req, parent, name, reply),
        Some(constants::PROJECT_METADATA_DIR) => {
            (metadata::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::PROJECT_METADATA_ATTRIBUTES_DIR) => {
            (metadata::attributes::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::PROJECT_METADATA_FACTS_DIR) => {
            (metadata::facts::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::PROJECT_METADATA_METRICS_DIR) => {
            (metadata::metrics::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::PROJECT_METADATA_REPORTS_DIR) => {
            (metadata::reports::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::PROJECT_METADATA_REPORT_DEFINITIONS_DIR) => {
            (metadata::report_definitions::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::USER_PERMISSIONS_JSON_FILENAME) => {
            (user_permissions::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::USER_ROLES_JSON_FILENAME) => {
            (user_roles::ITEM.lookup)(fs, req, parent, name, reply)
        }
        _ => {
            if inode.category == constants::Category::MetadataReports as u8 &&
               inode.reserved == constants::ReservedFile::KeepMe as u8 {
                let identifier = name.to_str().unwrap().replace(".json", "");
                println!("fs::project::lookup() - Looking up parent {} - {:?}, name: {:?}, \
                          identifier: {:?}",
                         parent,
                         inode,
                         name,
                         identifier);

                let project: &object::Project = &project_from_inode(fs, parent);

                let (index, report) = project.reports(&mut fs.client.connector)
                    .find_by_identifier(&identifier);
                println!("{:?}", report);

                if !report.is_some() {
                    reply.error(ENOENT);
                    return;
                }

                let inode = inode::Inode {
                    project: inode.project,
                    category: constants::Category::MetadataReports as u8,
                    item: index,
                    reserved: 0,
                };
                let json: String = report.unwrap().into();
                let attr = create_inode_file_attributes(inode::Inode::serialize(&inode),
                                                        json.len() as u64,
                                                        constants::DEFAULT_CREATE_TIME);
                reply.entry(&constants::DEFAULT_TTL, &attr, 0);
            } else {
                reply.error(ENOENT)
            }
        }
    }
}

pub fn read(fs: &mut GoodDataFS,
            req: &Request,
            ino: u64,
            fh: u64,
            offset: u64,
            size: u32,
            reply: ReplyData) {
    let inode = inode::Inode::deserialize(ino);
    let reserved = constants::ReservedFile::from(inode.reserved);
    match reserved {
        constants::ReservedFile::FeatureFlagsJson => {
            (feature_flags::ITEM.read)(fs, inode, reply, offset, size)
        }
        constants::ReservedFile::ProjectJson => {
            (project_json::ITEM.read)(fs, inode, reply, offset, size)
        }
        constants::ReservedFile::PermissionsJson => {
            (user_permissions::ITEM.read)(fs, inode, reply, offset, size)
        }
        constants::ReservedFile::RolesJson => {
            (user_roles::ITEM.read)(fs, inode, reply, offset, size);
        }
        _ => {
            if inode.category >= constants::Category::Metadata as u8 &&
               inode.category <= constants::Category::MetadataLast as u8 {
                metadata::read(fs, inode, reply, offset, size);
            } else {
                reply.error(ENOENT);
            }
        }
    }
}

pub fn readdir(fs: &mut GoodDataFS,
               req: &Request,
               ino: u64,
               fh: u64,
               in_offset: u64,
               mut reply: ReplyDirectory) {
    let mut offset = in_offset;

    let inode = inode::Inode::deserialize(ino);
    match inode.category {
        x if x == constants::Category::Internal as u8 => {
            let projectid = inode.project - 1;

            // Iterate over all project::ITEMS
            if offset + 1 < PROJECT_ITEMS.len() as u64 {
                for item in PROJECT_ITEMS.into_iter().skip(offset as usize) {
                    item.readdir(projectid, &offset, &mut reply);
                    offset += 1;
                }
            }

            reply.ok();
        }
        x if x == constants::Category::Ldm as u8 => {
            project::ldm::readdir(fs, req, ino, fh, in_offset, reply)
        }
        x if x == constants::Category::Metadata as u8 => {
            project::metadata::readdir(fs, req, ino, fh, in_offset, reply)
        }
        x if x == constants::Category::MetadataReports as u8 => {
            project::metadata::reports::readdir(fs, req, ino, fh, in_offset, reply)
        }
        _ => {
            println!("fs::project::readdir() - Unknow Category {}",
                     inode.category);
        }
    }
}
<commit_msg>Remove spam<commit_after>use fuse::{ReplyAttr, ReplyData, ReplyEntry, ReplyDirectory, Request};
use libc::ENOENT;

use fs::constants;
use fs::GoodDataFS;
use fs::helpers::create_inode_file_attributes;
use object;

mod feature_flags;
mod ldm;
mod metadata;
mod project_json;
mod user_permissions;
mod user_roles;

use super::item;
use super::project;

use super::super::inode;

use std::path::Path;

pub const PROJECT_FILES: [item::ProjectItem; 4] =
    [feature_flags::ITEM, project_json::ITEM, user_permissions::ITEM, user_roles::ITEM];

pub const PROJECT_DIRS: [item::ProjectItem; 2] = [ldm::ITEM, metadata::ITEM];

pub const PROJECT_ITEMS: [item::ProjectItem; 6] = [feature_flags::ITEM,
                                                   project_json::ITEM,
                                                   user_permissions::ITEM,
                                                   user_roles::ITEM,
                                                   ldm::ITEM,
                                                   metadata::ITEM];

/// Gets project from inode
pub fn project_from_inode<Type: Into<inode::Inode>>(fs: &GoodDataFS, ino: Type) -> object::Project {
    let inode = ino.into();
    let pid = (inode.project - 1) as usize;

    fs.client().projects().as_ref().unwrap()[pid].clone()
}

pub fn getattr(fs: &mut GoodDataFS, req: &Request, ino: u64, reply: ReplyAttr) {
    let inode = inode::Inode::deserialize(ino);

    if inode.project > 0 {
        match inode.category {
            x if x == constants::Category::Internal as u8 => {
                let reserved = constants::ReservedFile::from(inode.reserved);
                match reserved {
                    constants::ReservedFile::FeatureFlagsJson => {
                        (feature_flags::ITEM.getattr)(fs, req, ino, reply)
                    }
                    constants::ReservedFile::ProjectJson => {
                        (project_json::ITEM.getattr)(fs, req, ino, reply)
                    }
                    constants::ReservedFile::PermissionsJson => {
                        (user_permissions::ITEM.getattr)(fs, req, ino, reply)
                    }
                    constants::ReservedFile::RolesJson => {
                        (user_roles::ITEM.getattr)(fs, req, ino, reply)
                    }
                    _ => reply.error(ENOENT),
                }
            }
            x if x == constants::Category::Ldm as u8 => (ldm::ITEM.getattr)(fs, req, ino, reply),
            x if x == constants::Category::Metadata as u8 => {
                (metadata::ITEM.getattr)(fs, req, ino, reply)
            }
            x if x == constants::Category::MetadataAttributes as u8 => {
                (metadata::attributes::ITEM.getattr)(fs, req, ino, reply)
            }
            x if x == constants::Category::MetadataFacts as u8 => {
                (metadata::facts::ITEM.getattr)(fs, req, ino, reply)
            }
            x if x == constants::Category::MetadataMetrics as u8 => {
                (metadata::metrics::ITEM.getattr)(fs, req, ino, reply)
            }
            x if x == constants::Category::MetadataReports as u8 => {
                if inode.reserved == constants::ReservedFile::KeepMe as u8 {
                    (metadata::reports::ITEM.getattr)(fs, req, ino, reply)
                } else if inode.reserved == 0 {
                    // JSON REPORT
                    let project: &object::Project = &project_from_inode(fs, ino);

                    let report = &project.reports(&mut fs.client.connector)
                        .objects
                        .items[inode.item as usize];

                    let json: String = report.clone().into();
                    let attr = create_inode_file_attributes(ino,
                                                            json.len() as u64,
                                                            constants::DEFAULT_CREATE_TIME);
                    reply.attr(&constants::DEFAULT_TTL, &attr);
                } else {
                    println!("N A S T A L   H A P R ! ! !");
                    reply.error(ENOENT);
                }
            }
            x if x == constants::Category::MetadataReportDefinition as u8 => {
                (metadata::report_definitions::ITEM.getattr)(fs, req, ino, reply)
            }
            _ => {
                println!("fs::project::getattr() - not found!");
            }
        }
    } else {
        println!("GoodDataFS::getattr() - Not found inode {:?}", ino);
        reply.error(ENOENT);
    }
}

pub fn lookup(fs: &mut GoodDataFS, req: &Request, parent: u64, name: &Path, reply: ReplyEntry) {
    let inode = inode::Inode::deserialize(parent);

    match name.to_str() {
        Some(constants::FEATURE_FLAGS_JSON_FILENAME) => {
            (feature_flags::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::PROJECT_JSON_FILENAME) => {
            (project_json::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::PROJECT_LDM_DIR) => (ldm::ITEM.lookup)(fs, req, parent, name, reply),
        Some(constants::PROJECT_METADATA_DIR) => {
            (metadata::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::PROJECT_METADATA_ATTRIBUTES_DIR) => {
            (metadata::attributes::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::PROJECT_METADATA_FACTS_DIR) => {
            (metadata::facts::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::PROJECT_METADATA_METRICS_DIR) => {
            (metadata::metrics::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::PROJECT_METADATA_REPORTS_DIR) => {
            (metadata::reports::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::PROJECT_METADATA_REPORT_DEFINITIONS_DIR) => {
            (metadata::report_definitions::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::USER_PERMISSIONS_JSON_FILENAME) => {
            (user_permissions::ITEM.lookup)(fs, req, parent, name, reply)
        }
        Some(constants::USER_ROLES_JSON_FILENAME) => {
            (user_roles::ITEM.lookup)(fs, req, parent, name, reply)
        }
        _ => {
            if inode.category == constants::Category::MetadataReports as u8 &&
               inode.reserved == constants::ReservedFile::KeepMe as u8 {
                let identifier = name.to_str().unwrap().replace(".json", "");
                println!("fs::project::lookup() - Looking up parent {} - {:?}, name: {:?}, \
                          identifier: {:?}",
                         parent,
                         inode,
                         name,
                         identifier);

                let project: &object::Project = &project_from_inode(fs, parent);

                let (index, report) = project.reports(&mut fs.client.connector)
                    .find_by_identifier(&identifier);
                println!("{:?}", report);

                if !report.is_some() {
                    reply.error(ENOENT);
                    return;
                }

                let inode = inode::Inode {
                    project: inode.project,
                    category: constants::Category::MetadataReports as u8,
                    item: index,
                    reserved: 0,
                };
                let json: String = report.unwrap().into();
                let attr = create_inode_file_attributes(inode::Inode::serialize(&inode),
                                                        json.len() as u64,
                                                        constants::DEFAULT_CREATE_TIME);
                reply.entry(&constants::DEFAULT_TTL, &attr, 0);
            } else {
                reply.error(ENOENT)
            }
        }
    }
}

pub fn read(fs: &mut GoodDataFS,
            req: &Request,
            ino: u64,
            fh: u64,
            offset: u64,
            size: u32,
            reply: ReplyData) {
    let inode = inode::Inode::deserialize(ino);
    let reserved = constants::ReservedFile::from(inode.reserved);
    match reserved {
        constants::ReservedFile::FeatureFlagsJson => {
            (feature_flags::ITEM.read)(fs, inode, reply, offset, size)
        }
        constants::ReservedFile::ProjectJson => {
            (project_json::ITEM.read)(fs, inode, reply, offset, size)
        }
        constants::ReservedFile::PermissionsJson => {
            (user_permissions::ITEM.read)(fs, inode, reply, offset, size)
        }
        constants::ReservedFile::RolesJson => {
            (user_roles::ITEM.read)(fs, inode, reply, offset, size);
        }
        _ => {
            if inode.category >= constants::Category::Metadata as u8 &&
               inode.category <= constants::Category::MetadataLast as u8 {
                metadata::read(fs, inode, reply, offset, size);
            } else {
                reply.error(ENOENT);
            }
        }
    }
}

pub fn readdir(fs: &mut GoodDataFS,
               req: &Request,
               ino: u64,
               fh: u64,
               in_offset: u64,
               mut reply: ReplyDirectory) {
    let mut offset = in_offset;

    let inode = inode::Inode::deserialize(ino);
    match inode.category {
        x if x == constants::Category::Internal as u8 => {
            let projectid = inode.project - 1;

            // Iterate over all project::ITEMS
            if offset + 1 < PROJECT_ITEMS.len() as u64 {
                for item in PROJECT_ITEMS.into_iter().skip(offset as usize) {
                    item.readdir(projectid, &offset, &mut reply);
                    offset += 1;
                }
            }

            reply.ok();
        }
        x if x == constants::Category::Ldm as u8 => {
            project::ldm::readdir(fs, req, ino, fh, in_offset, reply)
        }
        x if x == constants::Category::Metadata as u8 => {
            project::metadata::readdir(fs, req, ino, fh, in_offset, reply)
        }
        x if x == constants::Category::MetadataReports as u8 => {
            project::metadata::reports::readdir(fs, req, ino, fh, in_offset, reply)
        }
        _ => {
            println!("fs::project::readdir() - Unknow Category {}",
                     inode.category);
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

///
/// Example cUrl request:
/// curl -X PUT -d '[[[{"id":"setter:talk@link.mozilla.org"}], {"String": "hello world"}]]' http://localhost:3000/api/v1/channels/set
///

use foxbox_taxonomy::adapter::*;
use foxbox_taxonomy::manager::AdapterManager;
use foxbox_taxonomy::api::{ Error, InternalError, User };
use foxbox_taxonomy::services::{ AdapterId, Channel, ChannelKind, Id, Service, ServiceId };
use foxbox_taxonomy::values::{ Type, Value };
use std::collections::HashMap;
use std::sync::Arc;

pub mod engine;
pub use self::engine::TtsEngine;

// eSpeak is the only engine supported for now.
mod espeak;
use self::espeak::EspeakEngine;

static ADAPTER_ID: &'static str = "espeak_adapter@link.mozilla.org";
static ADAPTER_NAME: &'static str = "eSpeak adapter";
static ADAPTER_VENDOR: &'static str = "team@link.mozilla.org";
static ADAPTER_VERSION: [u32;4] = [0, 0, 0, 0];

pub struct TtsAdapter<T> {
    talk_setter_id: Id<Channel>,
    engine: T
}

impl<T: TtsEngine> Adapter for TtsAdapter<T> {
    fn id(&self) -> Id<AdapterId> {
        adapter_id!(ADAPTER_ID)
    }

    fn name(&self) -> &str {
        ADAPTER_NAME
    }

    fn vendor(&self) -> &str {
        ADAPTER_VENDOR
    }

    fn version(&self) -> &[u32;4] {
        &ADAPTER_VERSION
    }

    fn fetch_values(&self, mut set: Vec<Id<Channel>>, _: User) -> ResultMap<Id<Channel>, Option<Value>, Error> {
        set.drain(..).map(|id| {
            (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
        }).collect()
    }

    fn send_values(&self, mut values: HashMap<Id<Channel>, Value>, _: User) -> ResultMap<Id<Channel>, (), Error> {
        use core::ops::Deref;

        values.drain().map(|(id, value)| {
            if id == self.talk_setter_id {
                if let Value::String(text) = value {
                    self.engine.say(text.deref());
                    return (id, Ok(()));
                }
            }
            (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
        }).collect()
    }
}

pub fn init(adapt: &Arc<AdapterManager>) -> Result<(), Error> {
    let engine = EspeakEngine { };
    if !engine.init() {
        warn!("eSpeak initialization failed!");
        return Err(Error::InternalError(InternalError::GenericError("eSpeak initialization failed!".to_owned())));
    }

    let talk_setter_id = Id::new("setter:talk@link.mozilla.org");
    try!(adapt.add_adapter(Arc::new(TtsAdapter {
        talk_setter_id: talk_setter_id.clone(),
        engine: engine
    })));
    let service_id = service_id!("espeak@link.mozilla.org");
    let adapter_id = adapter_id!(ADAPTER_ID);
    try!(adapt.add_service(Service::empty(&service_id, &adapter_id)));
    try!(adapt.add_channel(Channel {
        kind: ChannelKind::Extension {
            vendor: Id::new(ADAPTER_VENDOR),
            adapter: Id::new(ADAPTER_NAME),
            kind: Id::new("Sentence"),
            typ: Type::String,
        },
        supports_send: true,
        .. Channel::empty(&talk_setter_id, &service_id, &adapter_id)
    }));
    Ok(())
}
<commit_msg>TTS: Decentralizing ChannelKind<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

///
/// Example cUrl request:
/// curl -X PUT -d '[[[{"id":"setter:talk@link.mozilla.org"}], {"String": "hello world"}]]' http://localhost:3000/api/v1/channels/set
///

use foxbox_taxonomy::adapter::*;
use foxbox_taxonomy::manager::AdapterManager;
use foxbox_taxonomy::api::{ Error, InternalError, User };
use foxbox_taxonomy::channel::*;
use foxbox_taxonomy::services::{ AdapterId, Id, Service, ServiceId };
use foxbox_taxonomy::util::Maybe;
use foxbox_taxonomy::values::{ Type, Value };
use std::collections::HashMap;
use std::sync::Arc;

pub mod engine;
pub use self::engine::TtsEngine;

// eSpeak is the only engine supported for now.
mod espeak;
use self::espeak::EspeakEngine;

static ADAPTER_ID: &'static str = "espeak_adapter@link.mozilla.org";
static ADAPTER_NAME: &'static str = "eSpeak adapter";
static ADAPTER_VENDOR: &'static str = "team@link.mozilla.org";
static ADAPTER_VERSION: [u32;4] = [0, 0, 0, 0];

pub struct TtsAdapter<T> {
    talk_setter_id: Id<Channel>,
    engine: T
}

impl<T: TtsEngine> Adapter for TtsAdapter<T> {
    fn id(&self) -> Id<AdapterId> {
        adapter_id!(ADAPTER_ID)
    }

    fn name(&self) -> &str {
        ADAPTER_NAME
    }

    fn vendor(&self) -> &str {
        ADAPTER_VENDOR
    }

    fn version(&self) -> &[u32;4] {
        &ADAPTER_VERSION
    }

    fn fetch_values(&self, mut set: Vec<Id<Channel>>, _: User) -> ResultMap<Id<Channel>, Option<Value>, Error> {
        set.drain(..).map(|id| {
            (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
        }).collect()
    }

    fn send_values(&self, mut values: HashMap<Id<Channel>, Value>, _: User) -> ResultMap<Id<Channel>, (), Error> {
        use core::ops::Deref;

        values.drain().map(|(id, value)| {
            if id == self.talk_setter_id {
                if let Value::String(text) = value {
                    self.engine.say(text.deref());
                    return (id, Ok(()));
                }
            }
            (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
        }).collect()
    }
}

pub fn init(adapt: &Arc<AdapterManager>) -> Result<(), Error> {
    let engine = EspeakEngine { };
    if !engine.init() {
        warn!("eSpeak initialization failed!");
        return Err(Error::InternalError(InternalError::GenericError("eSpeak initialization failed!".to_owned())));
    }

    let talk_setter_id = Id::new("setter:talk@link.mozilla.org");
    try!(adapt.add_adapter(Arc::new(TtsAdapter {
        talk_setter_id: talk_setter_id.clone(),
        engine: engine
    })));
    let service_id = service_id!("espeak@link.mozilla.org");
    let adapter_id = adapter_id!(ADAPTER_ID);
    try!(adapt.add_service(Service::empty(&service_id, &adapter_id)));
    try!(adapt.add_channel(Channel {
        feature: Id::new("speak/sentence"),
        supports_send: Some(Signature::accepts(Maybe::Required(Type::String))),
        id: talk_setter_id,
        service: service_id,
        adapter: adapter_id,
        .. Channel::default()
    }));
    Ok(())
}
<|endoftext|>


<commit_before>// Copyright (c) 2019 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Encapsulate a pattern where we spawn a task on a separate thread,
//! but need to represent the failure to spawn that thread, as well as
//! the return value of that thread's computation as a single Future.
//!
//! It's essentially a tailored version `futures::FutureResult`,
//! except that it polls its `Ok` variant.

use crate::error::SupError;
use futures::{sync::oneshot, Future, Poll};
use std::io;

pub enum SpawnedFuture<T> {
    Ok(oneshot::Receiver<T>),
    Err(Option<io::Error>),
}

impl<T> From<oneshot::Receiver<T>> for SpawnedFuture<T> {
    fn from(r: oneshot::Receiver<T>) -> SpawnedFuture<T> {
        SpawnedFuture::Ok(r)
    }
}

impl<T> From<io::Error> for SpawnedFuture<T> {
    fn from(e: io::Error) -> SpawnedFuture<T> {
        SpawnedFuture::Err(Some(e))
    }
}

impl<T> Future for SpawnedFuture<T> {
    type Item = T;
    type Error = SupError;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        match self {
            SpawnedFuture::Ok(r) => r.poll().map_err(Into::into),
            SpawnedFuture::Err(e) => Err(e
                .take()
                .expect("Cannot poll SpawnedFuture::Err twice")
                .into()),
        }
    }
}
<commit_msg>[PR Feedback] Simplify SpawnedFuture future<commit_after>// Copyright (c) 2019 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Encapsulate a pattern where we spawn a task on a separate thread,
//! but need to represent the failure to spawn that thread, as well as
//! the return value of that thread's computation as a single Future.
//!
//! It's essentially a tailored version `futures::FutureResult`,
//! except that it polls its `Ok` variant.

use crate::error::SupError;
use futures::{sync::oneshot, Future, Poll};
use std::{io, result};

pub struct SpawnedFuture<T>(result::Result<oneshot::Receiver<T>, Option<io::Error>>);

impl<T> From<oneshot::Receiver<T>> for SpawnedFuture<T> {
    fn from(r: oneshot::Receiver<T>) -> SpawnedFuture<T> {
        SpawnedFuture(Ok(r))
    }
}

impl<T> From<io::Error> for SpawnedFuture<T> {
    fn from(e: io::Error) -> SpawnedFuture<T> {
        SpawnedFuture(Err(Some(e)))
    }
}

impl<T> Future for SpawnedFuture<T> {
    type Item = T;
    type Error = SupError;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        match self {
            SpawnedFuture(Ok(r)) => r.poll().map_err(Into::into),
            SpawnedFuture(Err(e)) => Err(e
                .take()
                .expect("Cannot poll SpawnedFuture::Err twice")
                .into()),
        }
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code, unused_variables)]

use std::collections::HashMap;
use std::fmt;

#[derive(Debug, PartialEq)]
pub struct Source {
    nick: String,
    ident: Option<String>,
    host: Option<String>,
    is_server: bool,
}

//  <prefix>   ::= <nick> [ '!' <ident>  [ '@' <host> ] ]
impl Source {
    fn parse(s: &str) -> Option<Source> {
        if s.is_empty() {
            return None;
        }

        let (nick, rest) = if let Some(index) = s.find('!') {
            (&s[..index], Some(&s[index + 1..]))
        } else {
            (s, None)
        };

        let (ident, host) = rest
            .map(|r| {
                if let Some(index) = r.find('@') {
                    (Some(&r[..index]), Some(&r[index + 1..]))
                } else {
                    (rest, None)
                }
            })
            .unwrap_or((None, None));

        Some(Source {
            nick: nick.trim_start_matches(':').to_string(),
            ident: ident.map(str::to_string),
            host: host.map(str::to_string),
            is_server: nick.starts_with(':'),
        })
    }
}

#[derive(Debug, PartialEq)]
pub enum MessageKind {
    Away,
    Capability,
    Error,
    Invite,
    Join,
    Kick,
    Mode,
    Nick,
    Notice,
    Numeric(u16),
    Part,
    Pass,
    Ping,
    Pong,
    Privmsg,
    Quit,
    Topic,
    User,
    Unknown,
    Wallops,
}

impl MessageKind {
    pub fn from(msg: &RawMessage) -> MessageKind {
        if let Ok(code) = msg.command.parse::<u16>() {
            return MessageKind::Numeric(code);
        }

        match msg.command.as_str() {
            "CAP" => Self::Capability,
            "JOIN" => Self::Join,
            "KICK" => Self::Kick,
            "NOTICE" => Self::Notice,
            "PART" => Self::Part,
            "PASS" => Self::Pass,
            "PING" => Self::Ping,
            "PONG" => Self::Pong,
            "PRIVMSG" => Self::Privmsg,
            "USER" => Self::User,

            // TODO: Map remaining types
            _ => Self::Unknown,
        }
    }
}

type MessageTags = HashMap<String, MessageTag>;

#[derive(Debug, PartialEq)]
pub struct MessageTag {
    key: String,
    value: Option<String>,
}

#[derive(Debug, PartialEq)]
pub struct RawMessage {
    pub source: Option<Source>,
    pub command: String,
    params: Vec<String>,

    pub timestamp: u64, // TODO: datetime
    pub tags: MessageTags,
}

/// Split `msg` at first space character or end of word if no spaces remain.
fn split_message(msg: &str) -> Option<(&str, &str)> {
    if msg.is_empty() {
        return None;
    }

    match msg.find(' ') {
        Some(index) => Some((&msg[..index], &msg[index + 1..])),

        None => Some((msg, "")),
    }
}

impl RawMessage {
    pub fn new(command: &str, params: &[String]) -> RawMessage {
        RawMessage {
            command: command.to_string(),
            params: params.to_vec(),

            source: None,
            timestamp: 0,
            tags: HashMap::new(),
        }
    }

    /// Expects that any trailing CR, LF have been removed.
    ///
    ///  <message>  ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
    ///  <prefix>   ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
    ///  <command>  ::= <letter> { <letter> } | <number> <number> <number>
    ///  <SPACE>    ::= ' ' { ' ' }
    ///  <params>   ::= <SPACE> [ ':' <trailing> | <middle> <params> ]
    ///  <middle>   ::= <Any *non-empty* sequence of octets not including SPACE
    ///                 or NUL or CR or LF, the first of which may not be ':'>
    ///  <trailing> ::= <Any, possibly *empty*, sequence of octets not including
    ///                   NUL or CR or LF>
    pub fn parse(line: &str) -> Option<Self> {
        let mut line: &str = line;
        let mut msg: Self;

        let tags = if line.starts_with('@') {
            let (tags, rest) = split_message(line)?;
            line = rest;
            // TODO: parse tags
            HashMap::new()
        } else {
            HashMap::new()
        };

        let nick = if line.starts_with(':') {
            let (nick, rest) = split_message(line)?;
            line = rest;
            Some(Source::parse(nick)?)
        } else {
            None
        };

        let (cmd, mut line) = split_message(line)?;
        // line = rest;

        let mut params: Vec<String> = Vec::new();
        while let Some((param, rest)) = split_message(line) {
            // Trailing param, this will be the end, regardless of
            // remaining whitespace.
            if param.starts_with(':') {
                params.push(line[1..].to_string());
                break;
            } else {
                params.push(param.to_string());
            }

            line = rest;
        }

        Some(Self {
            source: nick,
            command: cmd.to_string(),
            params,
            tags,

            // TODO: pull this out of the tags, if possible
            timestamp: 0,
        })
    }

    pub fn param(&self, i: usize) -> Option<&str> {
        self.params.get(i).map(String::as_ref)
    }
}

impl fmt::Display for RawMessage {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        unimplemented!()
    }
}
/// IRC capabilities we support
#[derive(Hash, PartialEq, Eq)]
pub enum Capability {
    ServerTime,
}

impl Capability {
    pub fn from(s: &str) -> Option<Self> {
        match s {
            "server-time" => Some(Self::ServerTime),
            _ => None,
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_parse_source() {
        assert_eq!(
            Source::parse("nick!~ident@host").unwrap(),
            Source {
                nick: "nick".to_string(),
                ident: Some("~ident".to_string()),
                host: Some("host".to_string()),
                is_server: false,
            }
        );

        assert_eq!(
            Source::parse("nick!~user").unwrap(),
            Source {
                nick: "nick".to_string(),
                ident: Some("~user".to_string()),
                host: None,
                is_server: false,
            }
        );

        assert_eq!(
            Source::parse("nick").unwrap(),
            Source {
                nick: "nick".to_string(),
                ident: None,
                host: None,
                is_server: false,
            }
        );

        assert_eq!(
            Source::parse(":irc.server!~ident@host").unwrap(),
            Source {
                nick: "irc.server".to_string(),
                ident: Some("~ident".to_string()),
                host: Some("host".to_string()),
                is_server: true,
            }
        );
    }

    #[test]
    fn test_parse_messages() {
        let line = "@foo=bar :server 001 abc :hello, world";
        let msg = RawMessage::parse(line).expect("failed to parse");

        assert_eq!(msg.command, "001");
        assert_eq!(msg.source.unwrap().nick, "server");
        assert_eq!(msg.params, vec!["abc", "hello, world"]);
        // TODO: wire up assert
    }

    #[test]
    fn test_split_message() {
        assert_eq!(split_message("foo bar baz"), Some(("foo", "bar baz")));
        assert_eq!(split_message("foo bar"), Some(("foo", "bar")));
        assert_eq!(split_message("foo"), Some(("foo", "")));
        assert_eq!(split_message(""), None);
        assert_eq!(split_message("trailing "), Some(("trailing", "")));
        assert_eq!(split_message(" leading"), Some(("", "leading")));
    }
}
<commit_msg>Add fmt::Display implementation for RawMessage<commit_after>#![allow(dead_code, unused_variables)]

use std::collections::HashMap;
use std::fmt;

#[derive(Debug, PartialEq)]
pub struct Source {
    nick: String,
    ident: Option<String>,
    host: Option<String>,
    is_server: bool,
}

impl fmt::Display for Source {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if self.is_server {
            write!(f, ":")?;
        }

        write!(f, "{}", self.nick)?;

        if let Some(ref ident) = self.ident {
            write!(f, "!{}", ident)?;
        }
        if let Some(ref host) = self.host {
            write!(f, "@{}", host)?;
        }

        Ok(())
    }
}

//  <prefix>   ::= <nick> [ '!' <ident>  [ '@' <host> ] ]
impl Source {
    fn parse(s: &str) -> Option<Source> {
        if s.is_empty() {
            return None;
        }

        let (nick, rest) = if let Some(index) = s.find('!') {
            (&s[..index], Some(&s[index + 1..]))
        } else {
            (s, None)
        };

        let (ident, host) = rest
            .map(|r| {
                if let Some(index) = r.find('@') {
                    (Some(&r[..index]), Some(&r[index + 1..]))
                } else {
                    (rest, None)
                }
            })
            .unwrap_or((None, None));

        Some(Source {
            nick: nick.trim_start_matches(':').to_string(),
            ident: ident.map(str::to_string),
            host: host.map(str::to_string),
            is_server: nick.starts_with(':'),
        })
    }
}

#[derive(Debug, PartialEq)]
pub enum MessageKind {
    Away,
    Capability,
    Error,
    Invite,
    Join,
    Kick,
    Mode,
    Nick,
    Notice,
    Numeric(u16),
    Part,
    Pass,
    Ping,
    Pong,
    Privmsg,
    Quit,
    Topic,
    User,
    Unknown,
    Wallops,
}

impl MessageKind {
    pub fn from(msg: &RawMessage) -> MessageKind {
        if let Ok(code) = msg.command.parse::<u16>() {
            return MessageKind::Numeric(code);
        }

        match msg.command.as_str() {
            "CAP" => Self::Capability,
            "JOIN" => Self::Join,
            "KICK" => Self::Kick,
            "NOTICE" => Self::Notice,
            "PART" => Self::Part,
            "PASS" => Self::Pass,
            "PING" => Self::Ping,
            "PONG" => Self::Pong,
            "PRIVMSG" => Self::Privmsg,
            "USER" => Self::User,

            // TODO: Map remaining types
            _ => Self::Unknown,
        }
    }
}

type MessageTags = HashMap<String, MessageTag>;

#[derive(Debug, PartialEq)]
pub struct MessageTag {
    key: String,
    value: Option<String>,
}

#[derive(Debug, PartialEq)]
pub struct RawMessage {
    pub source: Option<Source>,
    pub command: String,
    params: Vec<String>,

    pub timestamp: u64, // TODO: datetime
    pub tags: MessageTags,
}

/// Split `msg` at first space character or end of word if no spaces remain.
fn split_message(msg: &str) -> Option<(&str, &str)> {
    if msg.is_empty() {
        return None;
    }

    match msg.find(' ') {
        Some(index) => Some((&msg[..index], &msg[index + 1..])),
        None => Some((msg, "")),
    }
}

impl RawMessage {
    pub fn new(command: &str, params: &[String]) -> RawMessage {
        RawMessage {
            command: command.to_string(),
            params: params.to_vec(),

            source: None,
            timestamp: 0,
            tags: HashMap::new(),
        }
    }

    /// Expects that any trailing CR, LF have been removed.
    ///
    ///  <message>  ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
    ///  <prefix>   ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
    ///  <command>  ::= <letter> { <letter> } | <number> <number> <number>
    ///  <SPACE>    ::= ' ' { ' ' }
    ///  <params>   ::= <SPACE> [ ':' <trailing> | <middle> <params> ]
    ///  <middle>   ::= <Any *non-empty* sequence of octets not including SPACE
    ///                 or NUL or CR or LF, the first of which may not be ':'>
    ///  <trailing> ::= <Any, possibly *empty*, sequence of octets not including
    ///                   NUL or CR or LF>
    pub fn parse(line: &str) -> Option<Self> {
        let mut line: &str = line;
        let mut msg: Self;

        let tags = if line.starts_with('@') {
            let (tags, rest) = split_message(line)?;
            line = rest;
            // TODO: parse tags
            HashMap::new()
        } else {
            HashMap::new()
        };

        let nick = if line.starts_with(':') {
            let (nick, rest) = split_message(line)?;
            line = rest;
            Some(Source::parse(nick)?)
        } else {
            None
        };

        let (cmd, mut line) = split_message(line)?;
        // line = rest;

        let mut params: Vec<String> = Vec::new();
        while let Some((param, rest)) = split_message(line) {
            // Trailing param, this will be the end, regardless of
            // remaining whitespace.
            if param.starts_with(':') {
                params.push(line[1..].to_string());
                break;
            } else {
                params.push(param.to_string());
            }

            line = rest;
        }

        Some(Self {
            source: nick,
            command: cmd.to_string(),
            params,
            tags,

            // TODO: pull this out of the tags, if possible
            timestamp: 0,
        })
    }

    pub fn param(&self, i: usize) -> Option<&str> {
        self.params.get(i).map(String::as_ref)
    }
}

impl fmt::Display for RawMessage {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // TOOD: tags

        if let Some(source) = &self.source {
            write!(f, "{} ", source)?;
        }

        write!(f, "{}", self.command)?;

        for (i, param) in self.params.iter().enumerate() {
            // Last is a special case
            if param.contains(' ') {
                write!(f, " :{}", param)?;
            } else {
                write!(f, " {}", param)?;
            }
        }

        Ok(())
    }
}

/// IRC capabilities we support
#[derive(Hash, PartialEq, Eq)]
pub enum Capability {
    ServerTime,
}

impl Capability {
    pub fn from(s: &str) -> Option<Self> {
        match s {
            "server-time" => Some(Self::ServerTime),
            _ => None,
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_parse_source() {
        assert_eq!(
            Source::parse("nick!~ident@host").unwrap(),
            Source {
                nick: "nick".to_string(),
                ident: Some("~ident".to_string()),
                host: Some("host".to_string()),
                is_server: false,
            }
        );

        assert_eq!(
            Source::parse("nick!~user").unwrap(),
            Source {
                nick: "nick".to_string(),
                ident: Some("~user".to_string()),
                host: None,
                is_server: false,
            }
        );

        assert_eq!(
            Source::parse("nick").unwrap(),
            Source {
                nick: "nick".to_string(),
                ident: None,
                host: None,
                is_server: false,
            }
        );

        assert_eq!(
            Source::parse(":irc.server!~ident@host").unwrap(),
            Source {
                nick: "irc.server".to_string(),
                ident: Some("~ident".to_string()),
                host: Some("host".to_string()),
                is_server: true,
            }
        );
    }

    #[test]
    fn test_display_source() {
        let cases = vec![
            "nick",
            "nick!~ident",
            "nick!~ident@host",
            ":irc.server!~ident@host",
        ];

        for case in cases {
            let round_trip = format!("{}", Source::parse(case).unwrap());
            assert_eq!(round_trip, case);
        }
    }

    #[test]
    fn test_parse_messages() {
        let line = "@foo=bar :server 001 abc :hello, world";
        let msg = RawMessage::parse(line).expect("failed to parse");

        assert_eq!(msg.command, "001");
        assert_eq!(msg.source.unwrap().nick, "server");
        assert_eq!(msg.params, vec!["abc", "hello, world"]);
        // TODO: wire up assert
    }

    #[test]
    fn test_split_message() {
        assert_eq!(split_message("foo bar baz"), Some(("foo", "bar baz")));
        assert_eq!(split_message("foo bar"), Some(("foo", "bar")));
        assert_eq!(split_message("foo"), Some(("foo", "")));
        assert_eq!(split_message(""), None);
        assert_eq!(split_message("trailing "), Some(("trailing", "")));
        assert_eq!(split_message(" leading"), Some(("", "leading")));
    }
}
<|endoftext|>


<commit_before>// This example uses get_sock() to find out which file descriptors c-ares
// wants us to listen on, and uses epoll() to satisfy those requirements.
extern crate c_ares;
extern crate nix;

use nix::sys::epoll::{
    epoll_create,
    epoll_ctl,
    epoll_wait,
    EpollEvent,
    EpollEventKind,
    EpollOp,
    EPOLLIN,
    EPOLLOUT,
};
use std::collections::HashSet;
use std::error::Error;
use std::os::unix::io;

fn print_a_results(result: Result<c_ares::AResults, c_ares::AresError>) {
    match result {
        Err(e) => {
            println!("A lookup failed with error '{}'", e.description());
        }
        Ok(a_results) => {
            println!("Successful A lookup...");
            println!("Hostname: {}", a_results.hostname());
            for a_result in &a_results {
                println!("{:}", a_result.ipv4_addr());
            }
        }
    }
}

fn print_aaaa_results(result: Result<c_ares::AAAAResults, c_ares::AresError>) {
    match result {
        Err(e) => {
            println!("AAAA lookup failed with error '{}'", e.description());
        }
        Ok(aaaa_results) => {
            println!("Successful AAAA lookup...");
            println!("Hostname: {}", aaaa_results.hostname());
            for aaaa_result in &aaaa_results {
                println!("{:}", aaaa_result.ipv6_addr());
            }
        }
    }
}

fn print_srv_results(result: Result<c_ares::SRVResults, c_ares::AresError>) {
    match result {
        Err(e) => {
            println!("SRV lookup failed with error '{}'", e.description());
        }
        Ok(srv_results) => {
            println!("Successful SRV lookup...");
            for srv_result in &srv_results {
                println!("host: {} (port: {}), priority: {} weight: {}",
                         srv_result.host(),
                         srv_result.port(),
                         srv_result.priority(),
                         srv_result.weight());
            }
        }
    }
}

fn main() {
    // Create the c_ares::Channel.
    let mut options = c_ares::Options::new();
    options
        .set_flags(c_ares::flags::STAYOPEN)
        .set_timeout(500)
        .set_tries(3);
    let mut ares_channel = c_ares::Channel::new(options)
        .ok()
        .expect("Failed to create channel");

    // Set up some queries.
    ares_channel.query_a("apple.com", move |result| {
        println!("");
        print_a_results(result);
    });

    ares_channel.query_aaaa("google.com", move |result| {
        println!("");
        print_aaaa_results(result);
    });

    ares_channel.query_srv("_xmpp-server._tcp.gmail.com", move |result| {
        println!("");
        print_srv_results(result);
    });

    // Create an epoll file descriptor so that we can listen for events.
    let epoll = epoll_create().ok().expect("Failed to create epoll");
    let mut tracked_fds = HashSet::<io::RawFd>::new();
    loop {
        // Ask c-ares what file descriptors we should be listening on, and map
        // those requests onto the epoll file descriptor.
        let mut active = false;
        for (fd, readable, writable) in &ares_channel.get_sock() {
            let mut interest = EpollEventKind::empty();
            if readable { interest = interest | EPOLLIN; }
            if writable { interest = interest | EPOLLOUT; }
            let event = EpollEvent {
                events: interest,
                data: fd as u64,
            };
            let op = if tracked_fds.insert(fd) {
                EpollOp::EpollCtlAdd
            } else {
                EpollOp::EpollCtlMod
            };
            epoll_ctl(epoll, op, fd, &event).ok().expect("epoll_ctl failed");
            active = true;
        }
        if !active { break }

        // Wait for something to happen.
        let empty_event = EpollEvent {
            events: EpollEventKind::empty(),
            data: 0,
        };
        let mut events = [empty_event; 2];
        let results = epoll_wait(epoll, &mut events, 500)
            .ok()
            .expect("epoll_wait failed");

        // Process whatever happened.
        match results {
            0 => {
                // No events - must be a timeout.  Tell c-ares about it.
                ares_channel.process_fd(
                    c_ares::INVALID_FD,
                    c_ares::INVALID_FD);
            },
            n => {
                // Sockets became readable or writable.  Tell c-ares about it.
                for i in 0..n {
                    let event = events[i];
                    let active_fd = event.data as io::RawFd;
                    let readable_fd = if (event.events & EPOLLIN).is_empty() {
                        c_ares::INVALID_FD
                    } else {
                        active_fd
                    };
                    let writable_fd = if (event.events & EPOLLOUT).is_empty() {
                        c_ares::INVALID_FD
                    } else {
                        active_fd
                    };
                    ares_channel.process_fd(readable_fd, writable_fd);
                }
            }
        }
    }
}
<commit_msg>Slight prettification of example<commit_after>// This example uses get_sock() to find out which file descriptors c-ares
// wants us to listen on, and uses epoll() to satisfy those requirements.
extern crate c_ares;
extern crate nix;

use nix::sys::epoll::{
    epoll_create,
    epoll_ctl,
    epoll_wait,
    EpollEvent,
    EpollEventKind,
    EpollOp,
    EPOLLIN,
    EPOLLOUT,
};
use std::collections::HashSet;
use std::error::Error;
use std::os::unix::io;

fn print_a_results(result: Result<c_ares::AResults, c_ares::AresError>) {
    match result {
        Err(e) => {
            println!("A lookup failed with error '{}'", e.description());
        }
        Ok(a_results) => {
            println!("Successful A lookup...");
            println!("Hostname: {}", a_results.hostname());
            for a_result in &a_results {
                println!("{:}", a_result.ipv4_addr());
            }
        }
    }
}

fn print_aaaa_results(result: Result<c_ares::AAAAResults, c_ares::AresError>) {
    match result {
        Err(e) => {
            println!("AAAA lookup failed with error '{}'", e.description());
        }
        Ok(aaaa_results) => {
            println!("Successful AAAA lookup...");
            println!("Hostname: {}", aaaa_results.hostname());
            for aaaa_result in &aaaa_results {
                println!("{:}", aaaa_result.ipv6_addr());
            }
        }
    }
}

fn print_srv_results(result: Result<c_ares::SRVResults, c_ares::AresError>) {
    match result {
        Err(e) => {
            println!("SRV lookup failed with error '{}'", e.description());
        }
        Ok(srv_results) => {
            println!("Successful SRV lookup...");
            for srv_result in &srv_results {
                println!("host: {} (port: {}), priority: {} weight: {}",
                         srv_result.host(),
                         srv_result.port(),
                         srv_result.priority(),
                         srv_result.weight());
            }
        }
    }
}

fn main() {
    // Create the c_ares::Channel.
    let mut options = c_ares::Options::new();
    options
        .set_flags(c_ares::flags::STAYOPEN)
        .set_timeout(500)
        .set_tries(3);
    let mut ares_channel = c_ares::Channel::new(options)
        .ok()
        .expect("Failed to create channel");

    // Set up some queries.
    ares_channel.query_a("apple.com", move |result| {
        println!("");
        print_a_results(result);
    });

    ares_channel.query_aaaa("google.com", move |result| {
        println!("");
        print_aaaa_results(result);
    });

    ares_channel.query_srv("_xmpp-server._tcp.gmail.com", move |result| {
        println!("");
        print_srv_results(result);
    });

    // Create an epoll file descriptor so that we can listen for events.
    let epoll = epoll_create().ok().expect("Failed to create epoll");
    let mut tracked_fds = HashSet::<io::RawFd>::new();
    loop {
        // Ask c-ares what file descriptors we should be listening on, and map
        // those requests onto the epoll file descriptor.
        let mut active = false;
        for (fd, readable, writable) in &ares_channel.get_sock() {
            let mut interest = EpollEventKind::empty();
            if readable { interest = interest | EPOLLIN; }
            if writable { interest = interest | EPOLLOUT; }
            let event = EpollEvent {
                events: interest,
                data: fd as u64,
            };
            let op = if tracked_fds.insert(fd) {
                EpollOp::EpollCtlAdd
            } else {
                EpollOp::EpollCtlMod
            };
            epoll_ctl(epoll, op, fd, &event).ok().expect("epoll_ctl failed");
            active = true;
        }
        if !active { break }

        // Wait for something to happen.
        let empty_event = EpollEvent {
            events: EpollEventKind::empty(),
            data: 0,
        };
        let mut events = [empty_event; 2];
        let results = epoll_wait(epoll, &mut events, 500)
            .ok()
            .expect("epoll_wait failed");

        // Process whatever happened.
        match results {
            0 => {
                // No events - must be a timeout.  Tell c-ares about it.
                ares_channel.process_fd(
                    c_ares::INVALID_FD,
                    c_ares::INVALID_FD);
            },
            n => {
                // Sockets became readable or writable.  Tell c-ares about it.
                for event in &events[0..n] {
                    let active_fd = event.data as io::RawFd;
                    let readable_fd = if (event.events & EPOLLIN).is_empty() {
                        c_ares::INVALID_FD
                    } else {
                        active_fd
                    };
                    let writable_fd = if (event.events & EPOLLOUT).is_empty() {
                        c_ares::INVALID_FD
                    } else {
                        active_fd
                    };
                    ares_channel.process_fd(readable_fd, writable_fd);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use hyperx::{
    header::{Formatter as HeaderFormatter, Header, RawLike},
    Error as HeaderError, Result as HyperResult,
};
use std::{
    fmt::{Display, Formatter as FmtFormatter, Result as FmtResult},
    str::{from_utf8, FromStr},
};

header! { (ContentId, "Content-ID") => [String] }

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ContentTransferEncoding {
    SevenBit,
    QuotedPrintable,
    Base64,
    // 8BITMIME
    EightBit,
    Binary,
}

impl Default for ContentTransferEncoding {
    fn default() -> Self {
        ContentTransferEncoding::SevenBit
    }
}

impl Display for ContentTransferEncoding {
    fn fmt(&self, f: &mut FmtFormatter<'_>) -> FmtResult {
        use self::ContentTransferEncoding::*;
        f.write_str(match *self {
            SevenBit => "7bit",
            QuotedPrintable => "quoted-printable",
            Base64 => "base64",
            EightBit => "8bit",
            Binary => "binary",
        })
    }
}

impl FromStr for ContentTransferEncoding {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        use self::ContentTransferEncoding::*;
        match s {
            "7bit" => Ok(SevenBit),
            "quoted-printable" => Ok(QuotedPrintable),
            "base64" => Ok(Base64),
            "8bit" => Ok(EightBit),
            "binary" => Ok(Binary),
            _ => Err(s.into()),
        }
    }
}

impl Header for ContentTransferEncoding {
    fn header_name() -> &'static str {
        "Content-Transfer-Encoding"
    }

    // FIXME HeaderError->HeaderError, same for result
    fn parse_header<'a, T>(raw: &'a T) -> HyperResult<Self>
    where
        T: RawLike<'a>,
        Self: Sized,
    {
        raw.one()
            .ok_or(HeaderError::Header)
            .and_then(|r| from_utf8(r).map_err(|_| HeaderError::Header))
            .and_then(|s| {
                s.parse::<ContentTransferEncoding>()
                    .map_err(|_| HeaderError::Header)
            })
    }

    fn fmt_header(&self, f: &mut HeaderFormatter<'_, '_>) -> FmtResult {
        f.fmt_line(&format!("{}", self))
    }
}

#[cfg(test)]
mod test {
    use super::ContentTransferEncoding;
    use hyperx::header::Headers;

    #[test]
    fn format_content_transfer_encoding() {
        let mut headers = Headers::new();

        headers.set(ContentTransferEncoding::SevenBit);

        assert_eq!(
            format!("{}", headers),
            "Content-Transfer-Encoding: 7bit\r\n"
        );

        headers.set(ContentTransferEncoding::Base64);

        assert_eq!(
            format!("{}", headers),
            "Content-Transfer-Encoding: base64\r\n"
        );
    }

    #[test]
    fn parse_content_transfer_encoding() {
        let mut headers = Headers::new();

        headers.set_raw("Content-Transfer-Encoding", "7bit");

        assert_eq!(
            headers.get::<ContentTransferEncoding>(),
            Some(&ContentTransferEncoding::SevenBit)
        );

        headers.set_raw("Content-Transfer-Encoding", "base64");

        assert_eq!(
            headers.get::<ContentTransferEncoding>(),
            Some(&ContentTransferEncoding::Base64)
        );
    }
}
<commit_msg>Return base64 as the Default Content-Transfer-Encoding<commit_after>use hyperx::{
    header::{Formatter as HeaderFormatter, Header, RawLike},
    Error as HeaderError, Result as HyperResult,
};
use std::{
    fmt::{Display, Formatter as FmtFormatter, Result as FmtResult},
    str::{from_utf8, FromStr},
};

header! { (ContentId, "Content-ID") => [String] }

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ContentTransferEncoding {
    SevenBit,
    QuotedPrintable,
    Base64,
    // 8BITMIME
    EightBit,
    Binary,
}

impl Default for ContentTransferEncoding {
    fn default() -> Self {
        ContentTransferEncoding::Base64
    }
}

impl Display for ContentTransferEncoding {
    fn fmt(&self, f: &mut FmtFormatter<'_>) -> FmtResult {
        use self::ContentTransferEncoding::*;
        f.write_str(match *self {
            SevenBit => "7bit",
            QuotedPrintable => "quoted-printable",
            Base64 => "base64",
            EightBit => "8bit",
            Binary => "binary",
        })
    }
}

impl FromStr for ContentTransferEncoding {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        use self::ContentTransferEncoding::*;
        match s {
            "7bit" => Ok(SevenBit),
            "quoted-printable" => Ok(QuotedPrintable),
            "base64" => Ok(Base64),
            "8bit" => Ok(EightBit),
            "binary" => Ok(Binary),
            _ => Err(s.into()),
        }
    }
}

impl Header for ContentTransferEncoding {
    fn header_name() -> &'static str {
        "Content-Transfer-Encoding"
    }

    // FIXME HeaderError->HeaderError, same for result
    fn parse_header<'a, T>(raw: &'a T) -> HyperResult<Self>
    where
        T: RawLike<'a>,
        Self: Sized,
    {
        raw.one()
            .ok_or(HeaderError::Header)
            .and_then(|r| from_utf8(r).map_err(|_| HeaderError::Header))
            .and_then(|s| {
                s.parse::<ContentTransferEncoding>()
                    .map_err(|_| HeaderError::Header)
            })
    }

    fn fmt_header(&self, f: &mut HeaderFormatter<'_, '_>) -> FmtResult {
        f.fmt_line(&format!("{}", self))
    }
}

#[cfg(test)]
mod test {
    use super::ContentTransferEncoding;
    use hyperx::header::Headers;

    #[test]
    fn format_content_transfer_encoding() {
        let mut headers = Headers::new();

        headers.set(ContentTransferEncoding::SevenBit);

        assert_eq!(
            format!("{}", headers),
            "Content-Transfer-Encoding: 7bit\r\n"
        );

        headers.set(ContentTransferEncoding::Base64);

        assert_eq!(
            format!("{}", headers),
            "Content-Transfer-Encoding: base64\r\n"
        );
    }

    #[test]
    fn parse_content_transfer_encoding() {
        let mut headers = Headers::new();

        headers.set_raw("Content-Transfer-Encoding", "7bit");

        assert_eq!(
            headers.get::<ContentTransferEncoding>(),
            Some(&ContentTransferEncoding::SevenBit)
        );

        headers.set_raw("Content-Transfer-Encoding", "base64");

        assert_eq!(
            headers.get::<ContentTransferEncoding>(),
            Some(&ContentTransferEncoding::Base64)
        );
    }
}
<|endoftext|>