<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 https://mozilla.org/MPL/2.0/. */

//! Liberally derived from the [Firefox JS implementation]
//! (http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/webconsole.js).
//! Mediates interaction between the remote web console and equivalent functionality (object
//! inspection, JS evaluation, autocompletion) in Servo.

use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
use crate::actors::browsing_context::BrowsingContextActor;
use crate::actors::object::ObjectActor;
use crate::actors::worker::WorkerActor;
use crate::protocol::JsonPacketStream;
use crate::UniqueId;
use devtools_traits::CachedConsoleMessage;
use devtools_traits::ConsoleMessage;
use devtools_traits::EvaluateJSReply::{ActorValue, BooleanValue, StringValue};
use devtools_traits::EvaluateJSReply::{NullValue, NumberValue, VoidValue};
use devtools_traits::{
    CachedConsoleMessageTypes, ConsoleAPI, DevtoolScriptControlMsg, LogLevel, PageError,
};
use ipc_channel::ipc::{self, IpcSender};
use msg::constellation_msg::TEST_PIPELINE_ID;
use serde_json::{self, Map, Number, Value};
use std::cell::{RefCell, RefMut};
use std::collections::HashMap;
use std::net::TcpStream;
use time::precise_time_ns;
use uuid::Uuid;

trait EncodableConsoleMessage {
    fn encode(&self) -> serde_json::Result<String>;
}

impl EncodableConsoleMessage for CachedConsoleMessage {
    fn encode(&self) -> serde_json::Result<String> {
        match *self {
            CachedConsoleMessage::PageError(ref a) => serde_json::to_string(a),
            CachedConsoleMessage::ConsoleAPI(ref a) => serde_json::to_string(a),
        }
    }
}

#[derive(Serialize)]
struct StartedListenersTraits;

#[derive(Serialize)]
struct StartedListenersReply {
    from: String,
    nativeConsoleAPI: bool,
    startedListeners: Vec<String>,
    traits: StartedListenersTraits,
}

#[derive(Serialize)]
struct GetCachedMessagesReply {
    from: String,
    messages: Vec<Map<String, Value>>,
}

#[derive(Serialize)]
struct StopListenersReply {
    from: String,
    stoppedListeners: Vec<String>,
}

#[derive(Serialize)]
struct AutocompleteReply {
    from: String,
    matches: Vec<String>,
    matchProp: String,
}

#[derive(Serialize)]
struct EvaluateJSReply {
    from: String,
    input: String,
    result: Value,
    timestamp: u64,
    exception: Value,
    exceptionMessage: Value,
    helperResult: Value,
}

#[derive(Serialize)]
struct EvaluateJSEvent {
    from: String,
    r#type: String,
    input: String,
    result: Value,
    timestamp: u64,
    resultID: String,
    exception: Value,
    exceptionMessage: Value,
    helperResult: Value,
}

#[derive(Serialize)]
struct EvaluateJSAsyncReply {
    from: String,
    resultID: String,
}

#[derive(Serialize)]
struct SetPreferencesReply {
    from: String,
    updated: Vec<String>,
}

pub(crate) enum Root {
    BrowsingContext(String),
    DedicatedWorker(String),
}

pub(crate) struct ConsoleActor {
    pub name: String,
    pub root: Root,
    pub cached_events: RefCell<HashMap<UniqueId, Vec<CachedConsoleMessage>>>,
}

impl ConsoleActor {
    fn script_chan<'a>(
        &self,
        registry: &'a ActorRegistry,
    ) -> &'a IpcSender<DevtoolScriptControlMsg> {
        match &self.root {
            Root::BrowsingContext(bc) => &registry.find::<BrowsingContextActor>(bc).script_chan,
            Root::DedicatedWorker(worker) => &registry.find::<WorkerActor>(worker).script_chan,
        }
    }

    fn streams_mut<'a>(&self, registry: &'a ActorRegistry) -> RefMut<'a, Vec<TcpStream>> {
        match &self.root {
            Root::BrowsingContext(bc) => registry
                .find::<BrowsingContextActor>(bc)
                .streams
                .borrow_mut(),
            Root::DedicatedWorker(worker) => {
                registry.find::<WorkerActor>(worker).streams.borrow_mut()
            },
        }
    }

    fn current_unique_id(&self, registry: &ActorRegistry) -> UniqueId {
        match &self.root {
            Root::BrowsingContext(bc) => UniqueId::Pipeline(
                registry
                    .find::<BrowsingContextActor>(bc)
                    .active_pipeline
                    .get(),
            ),
            Root::DedicatedWorker(w) => UniqueId::Worker(registry.find::<WorkerActor>(w).id),
        }
    }

    fn evaluateJS(
        &self,
        registry: &ActorRegistry,
        msg: &Map<String, Value>,
    ) -> Result<EvaluateJSReply, ()> {
        let input = msg.get("text").unwrap().as_str().unwrap().to_owned();
        let (chan, port) = ipc::channel().unwrap();
        // FIXME: redesign messages so we don't have to fake pipeline ids when
        //        communicating with workers.
        let pipeline = match self.current_unique_id(registry) {
            UniqueId::Pipeline(p) => p,
            UniqueId::Worker(_) => TEST_PIPELINE_ID,
        };
        self.script_chan(registry)
            .send(DevtoolScriptControlMsg::EvaluateJS(
                pipeline,
                input.clone(),
                chan,
            ))
            .unwrap();

        //TODO: extract conversion into protocol module or some other useful place
        let result = match port.recv().map_err(|_| ())? {
            VoidValue => {
                let mut m = Map::new();
                m.insert("type".to_owned(), Value::String("undefined".to_owned()));
                Value::Object(m)
            },
            NullValue => {
                let mut m = Map::new();
                m.insert("type".to_owned(), Value::String("null".to_owned()));
                Value::Object(m)
            },
            BooleanValue(val) => Value::Bool(val),
            NumberValue(val) => {
                if val.is_nan() {
                    let mut m = Map::new();
                    m.insert("type".to_owned(), Value::String("NaN".to_owned()));
                    Value::Object(m)
                } else if val.is_infinite() {
                    let mut m = Map::new();
                    if val < 0. {
                        m.insert("type".to_owned(), Value::String("-Infinity".to_owned()));
                    } else {
                        m.insert("type".to_owned(), Value::String("Infinity".to_owned()));
                    }
                    Value::Object(m)
                } else if val == 0. && val.is_sign_negative() {
                    let mut m = Map::new();
                    m.insert("type".to_owned(), Value::String("-0".to_owned()));
                    Value::Object(m)
                } else {
                    Value::Number(Number::from_f64(val).unwrap())
                }
            },
            StringValue(s) => Value::String(s),
            ActorValue { class, uuid } => {
                //TODO: make initial ActorValue message include these properties?
                let mut m = Map::new();
                let actor = ObjectActor::new(registry, uuid);

                m.insert("type".to_owned(), Value::String("object".to_owned()));
                m.insert("class".to_owned(), Value::String(class));
                m.insert("actor".to_owned(), Value::String(actor));
                m.insert("extensible".to_owned(), Value::Bool(true));
                m.insert("frozen".to_owned(), Value::Bool(false));
                m.insert("sealed".to_owned(), Value::Bool(false));
                Value::Object(m)
            },
        };

        //TODO: catch and return exception values from JS evaluation
        let reply = EvaluateJSReply {
            from: self.name(),
            input: input,
            result: result,
            timestamp: 0,
            exception: Value::Null,
            exceptionMessage: Value::Null,
            helperResult: Value::Null,
        };
        std::result::Result::Ok(reply)
    }

    pub(crate) fn handle_page_error(
        &self,
        page_error: PageError,
        id: UniqueId,
        registry: &ActorRegistry,
    ) {
        self.cached_events
            .borrow_mut()
            .entry(id.clone())
            .or_insert(vec![])
            .push(CachedConsoleMessage::PageError(page_error.clone()));
        if id == self.current_unique_id(registry) {
            let msg = PageErrorMsg {
                from: self.name(),
                type_: "pageError".to_owned(),
                pageError: page_error,
            };
            for stream in &mut *self.streams_mut(registry) {
                stream.write_json_packet(&msg);
            }
        }
    }

    pub(crate) fn handle_console_api(
        &self,
        console_message: ConsoleMessage,
        id: UniqueId,
        registry: &ActorRegistry,
    ) {
        let level = match console_message.logLevel {
            LogLevel::Debug => "debug",
            LogLevel::Info => "info",
            LogLevel::Warn => "warn",
            LogLevel::Error => "error",
            _ => "log",
        }
        .to_owned();
        self.cached_events
            .borrow_mut()
            .entry(id.clone())
            .or_insert(vec![])
            .push(CachedConsoleMessage::ConsoleAPI(ConsoleAPI {
                type_: "ConsoleAPI".to_owned(),
                level: level.clone(),
                filename: console_message.filename.clone(),
                lineNumber: console_message.lineNumber as u32,
                functionName: "".to_string(), //TODO
                timeStamp: precise_time_ns(),
                private: false,
                arguments: vec![console_message.message.clone()],
            }));
        if id == self.current_unique_id(registry) {
            let msg = ConsoleAPICall {
                from: self.name(),
                type_: "consoleAPICall".to_owned(),
                message: ConsoleMsg {
                    level: level,
                    timeStamp: precise_time_ns(),
                    arguments: vec![console_message.message],
                    filename: console_message.filename,
                    lineNumber: console_message.lineNumber,
                    columnNumber: console_message.columnNumber,
                },
            };
            for stream in &mut *self.streams_mut(registry) {
                stream.write_json_packet(&msg);
            }
        }
    }
}

impl Actor for ConsoleActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(
        &self,
        registry: &ActorRegistry,
        msg_type: &str,
        msg: &Map<String, Value>,
        stream: &mut TcpStream,
    ) -> Result<ActorMessageStatus, ()> {
        Ok(match msg_type {
            "clearMessagesCache" => {
                self.cached_events
                    .borrow_mut()
                    .remove(&self.current_unique_id(registry));
                ActorMessageStatus::Processed
            },

            "getCachedMessages" => {
                let str_types = msg
                    .get("messageTypes")
                    .unwrap()
                    .as_array()
                    .unwrap()
                    .into_iter()
                    .map(|json_type| json_type.as_str().unwrap());
                let mut message_types = CachedConsoleMessageTypes::empty();
                for str_type in str_types {
                    match str_type {
                        "PageError" => message_types.insert(CachedConsoleMessageTypes::PAGE_ERROR),
                        "ConsoleAPI" => {
                            message_types.insert(CachedConsoleMessageTypes::CONSOLE_API)
                        },
                        s => debug!("unrecognized message type requested: \"{}\"", s),
                    };
                }
                let mut messages = vec![];
                for event in self
                    .cached_events
                    .borrow()
                    .get(&self.current_unique_id(registry))
                    .unwrap_or(&vec![])
                    .iter()
                {
                    let include = match event {
                        CachedConsoleMessage::PageError(_)
                            if message_types.contains(CachedConsoleMessageTypes::PAGE_ERROR) =>
                        {
                            true
                        },
                        CachedConsoleMessage::ConsoleAPI(_)
                            if message_types.contains(CachedConsoleMessageTypes::CONSOLE_API) =>
                        {
                            true
                        },
                        _ => false,
                    };
                    if include {
                        let json_string = event.encode().unwrap();
                        let json = serde_json::from_str::<Value>(&json_string).unwrap();
                        messages.push(json.as_object().unwrap().to_owned())
                    }
                }

                let msg = GetCachedMessagesReply {
                    from: self.name(),
                    messages: messages,
                };
                stream.write_json_packet(&msg);
                ActorMessageStatus::Processed
            },

            "startListeners" => {
                //TODO: actually implement listener filters that support starting/stopping
                let listeners = msg.get("listeners").unwrap().as_array().unwrap().to_owned();
                let msg = StartedListenersReply {
                    from: self.name(),
                    nativeConsoleAPI: true,
                    startedListeners: listeners
                        .into_iter()
                        .map(|s| s.as_str().unwrap().to_owned())
                        .collect(),
                    traits: StartedListenersTraits,
                };
                stream.write_json_packet(&msg);
                ActorMessageStatus::Processed
            },

            "stopListeners" => {
                //TODO: actually implement listener filters that support starting/stopping
                let msg = StopListenersReply {
                    from: self.name(),
                    stoppedListeners: msg
                        .get("listeners")
                        .unwrap()
                        .as_array()
                        .unwrap_or(&vec![])
                        .iter()
                        .map(|listener| listener.as_str().unwrap().to_owned())
                        .collect(),
                };
                stream.write_json_packet(&msg);
                ActorMessageStatus::Processed
            },

            //TODO: implement autocompletion like onAutocomplete in
            //      http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/webconsole.js
            "autocomplete" => {
                let msg = AutocompleteReply {
                    from: self.name(),
                    matches: vec![],
                    matchProp: "".to_owned(),
                };
                stream.write_json_packet(&msg);
                ActorMessageStatus::Processed
            },

            "evaluateJS" => {
                let msg = self.evaluateJS(&registry, &msg);
                stream.write_json_packet(&msg);
                ActorMessageStatus::Processed
            },

            "evaluateJSAsync" => {
                let resultID = Uuid::new_v4().to_string();
                let early_reply = EvaluateJSAsyncReply {
                    from: self.name(),
                    resultID: resultID.clone(),
                };
                // Emit an eager reply so that the client starts listening
                // for an async event with the resultID
                stream.write_json_packet(&early_reply);
                let reply = self.evaluateJS(&registry, &msg).unwrap();
                let msg = EvaluateJSEvent {
                    from: self.name(),
                    r#type: "evaluationResult".to_owned(),
                    input: reply.input,
                    result: reply.result,
                    timestamp: reply.timestamp,
                    resultID: resultID,
                    exception: reply.exception,
                    exceptionMessage: reply.exceptionMessage,
                    helperResult: reply.helperResult,
                };
                // Send the data from evaluateJS along with a resultID
                stream.write_json_packet(&msg);
                ActorMessageStatus::Processed
            },

            "setPreferences" => {
                let msg = SetPreferencesReply {
                    from: self.name(),
                    updated: vec![],
                };
                stream.write_json_packet(&msg);
                ActorMessageStatus::Processed
            },

            _ => ActorMessageStatus::Ignored,
        })
    }
}

#[derive(Serialize)]
struct ConsoleAPICall {
    from: String,
    #[serde(rename = "type")]
    type_: String,
    message: ConsoleMsg,
}

#[derive(Serialize)]
struct ConsoleMsg {
    level: String,
    timeStamp: u64,
    arguments: Vec<String>,
    filename: String,
    lineNumber: usize,
    columnNumber: usize,
}

#[derive(Serialize)]
struct PageErrorMsg {
    from: String,
    #[serde(rename = "type")]
    type_: String,
    pageError: PageError,
}
<commit_msg>Ignore eager JS evaluation in devtools.<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 https://mozilla.org/MPL/2.0/. */

//! Liberally derived from the [Firefox JS implementation]
//! (http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/webconsole.js).
//! Mediates interaction between the remote web console and equivalent functionality (object
//! inspection, JS evaluation, autocompletion) in Servo.

use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
use crate::actors::browsing_context::BrowsingContextActor;
use crate::actors::object::ObjectActor;
use crate::actors::worker::WorkerActor;
use crate::protocol::JsonPacketStream;
use crate::UniqueId;
use devtools_traits::CachedConsoleMessage;
use devtools_traits::ConsoleMessage;
use devtools_traits::EvaluateJSReply::{ActorValue, BooleanValue, StringValue};
use devtools_traits::EvaluateJSReply::{NullValue, NumberValue, VoidValue};
use devtools_traits::{
    CachedConsoleMessageTypes, ConsoleAPI, DevtoolScriptControlMsg, LogLevel, PageError,
};
use ipc_channel::ipc::{self, IpcSender};
use msg::constellation_msg::TEST_PIPELINE_ID;
use serde_json::{self, Map, Number, Value};
use std::cell::{RefCell, RefMut};
use std::collections::HashMap;
use std::net::TcpStream;
use time::precise_time_ns;
use uuid::Uuid;

trait EncodableConsoleMessage {
    fn encode(&self) -> serde_json::Result<String>;
}

impl EncodableConsoleMessage for CachedConsoleMessage {
    fn encode(&self) -> serde_json::Result<String> {
        match *self {
            CachedConsoleMessage::PageError(ref a) => serde_json::to_string(a),
            CachedConsoleMessage::ConsoleAPI(ref a) => serde_json::to_string(a),
        }
    }
}

#[derive(Serialize)]
struct StartedListenersTraits;

#[derive(Serialize)]
struct StartedListenersReply {
    from: String,
    nativeConsoleAPI: bool,
    startedListeners: Vec<String>,
    traits: StartedListenersTraits,
}

#[derive(Serialize)]
struct GetCachedMessagesReply {
    from: String,
    messages: Vec<Map<String, Value>>,
}

#[derive(Serialize)]
struct StopListenersReply {
    from: String,
    stoppedListeners: Vec<String>,
}

#[derive(Serialize)]
struct AutocompleteReply {
    from: String,
    matches: Vec<String>,
    matchProp: String,
}

#[derive(Serialize)]
struct EvaluateJSReply {
    from: String,
    input: String,
    result: Value,
    timestamp: u64,
    exception: Value,
    exceptionMessage: Value,
    helperResult: Value,
}

#[derive(Serialize)]
struct EvaluateJSEvent {
    from: String,
    r#type: String,
    input: String,
    result: Value,
    timestamp: u64,
    resultID: String,
    exception: Value,
    exceptionMessage: Value,
    helperResult: Value,
}

#[derive(Serialize)]
struct EvaluateJSAsyncReply {
    from: String,
    resultID: String,
}

#[derive(Serialize)]
struct SetPreferencesReply {
    from: String,
    updated: Vec<String>,
}

pub(crate) enum Root {
    BrowsingContext(String),
    DedicatedWorker(String),
}

pub(crate) struct ConsoleActor {
    pub name: String,
    pub root: Root,
    pub cached_events: RefCell<HashMap<UniqueId, Vec<CachedConsoleMessage>>>,
}

impl ConsoleActor {
    fn script_chan<'a>(
        &self,
        registry: &'a ActorRegistry,
    ) -> &'a IpcSender<DevtoolScriptControlMsg> {
        match &self.root {
            Root::BrowsingContext(bc) => &registry.find::<BrowsingContextActor>(bc).script_chan,
            Root::DedicatedWorker(worker) => &registry.find::<WorkerActor>(worker).script_chan,
        }
    }

    fn streams_mut<'a>(&self, registry: &'a ActorRegistry) -> RefMut<'a, Vec<TcpStream>> {
        match &self.root {
            Root::BrowsingContext(bc) => registry
                .find::<BrowsingContextActor>(bc)
                .streams
                .borrow_mut(),
            Root::DedicatedWorker(worker) => {
                registry.find::<WorkerActor>(worker).streams.borrow_mut()
            },
        }
    }

    fn current_unique_id(&self, registry: &ActorRegistry) -> UniqueId {
        match &self.root {
            Root::BrowsingContext(bc) => UniqueId::Pipeline(
                registry
                    .find::<BrowsingContextActor>(bc)
                    .active_pipeline
                    .get(),
            ),
            Root::DedicatedWorker(w) => UniqueId::Worker(registry.find::<WorkerActor>(w).id),
        }
    }

    fn evaluateJS(
        &self,
        registry: &ActorRegistry,
        msg: &Map<String, Value>,
    ) -> Result<EvaluateJSReply, ()> {
        let input = msg.get("text").unwrap().as_str().unwrap().to_owned();
        let (chan, port) = ipc::channel().unwrap();
        // FIXME: redesign messages so we don't have to fake pipeline ids when
        //        communicating with workers.
        let pipeline = match self.current_unique_id(registry) {
            UniqueId::Pipeline(p) => p,
            UniqueId::Worker(_) => TEST_PIPELINE_ID,
        };
        self.script_chan(registry)
            .send(DevtoolScriptControlMsg::EvaluateJS(
                pipeline,
                input.clone(),
                chan,
            ))
            .unwrap();

        //TODO: extract conversion into protocol module or some other useful place
        let result = match port.recv().map_err(|_| ())? {
            VoidValue => {
                let mut m = Map::new();
                m.insert("type".to_owned(), Value::String("undefined".to_owned()));
                Value::Object(m)
            },
            NullValue => {
                let mut m = Map::new();
                m.insert("type".to_owned(), Value::String("null".to_owned()));
                Value::Object(m)
            },
            BooleanValue(val) => Value::Bool(val),
            NumberValue(val) => {
                if val.is_nan() {
                    let mut m = Map::new();
                    m.insert("type".to_owned(), Value::String("NaN".to_owned()));
                    Value::Object(m)
                } else if val.is_infinite() {
                    let mut m = Map::new();
                    if val < 0. {
                        m.insert("type".to_owned(), Value::String("-Infinity".to_owned()));
                    } else {
                        m.insert("type".to_owned(), Value::String("Infinity".to_owned()));
                    }
                    Value::Object(m)
                } else if val == 0. && val.is_sign_negative() {
                    let mut m = Map::new();
                    m.insert("type".to_owned(), Value::String("-0".to_owned()));
                    Value::Object(m)
                } else {
                    Value::Number(Number::from_f64(val).unwrap())
                }
            },
            StringValue(s) => Value::String(s),
            ActorValue { class, uuid } => {
                //TODO: make initial ActorValue message include these properties?
                let mut m = Map::new();
                let actor = ObjectActor::new(registry, uuid);

                m.insert("type".to_owned(), Value::String("object".to_owned()));
                m.insert("class".to_owned(), Value::String(class));
                m.insert("actor".to_owned(), Value::String(actor));
                m.insert("extensible".to_owned(), Value::Bool(true));
                m.insert("frozen".to_owned(), Value::Bool(false));
                m.insert("sealed".to_owned(), Value::Bool(false));
                Value::Object(m)
            },
        };

        //TODO: catch and return exception values from JS evaluation
        let reply = EvaluateJSReply {
            from: self.name(),
            input: input,
            result: result,
            timestamp: 0,
            exception: Value::Null,
            exceptionMessage: Value::Null,
            helperResult: Value::Null,
        };
        std::result::Result::Ok(reply)
    }

    pub(crate) fn handle_page_error(
        &self,
        page_error: PageError,
        id: UniqueId,
        registry: &ActorRegistry,
    ) {
        self.cached_events
            .borrow_mut()
            .entry(id.clone())
            .or_insert(vec![])
            .push(CachedConsoleMessage::PageError(page_error.clone()));
        if id == self.current_unique_id(registry) {
            let msg = PageErrorMsg {
                from: self.name(),
                type_: "pageError".to_owned(),
                pageError: page_error,
            };
            for stream in &mut *self.streams_mut(registry) {
                stream.write_json_packet(&msg);
            }
        }
    }

    pub(crate) fn handle_console_api(
        &self,
        console_message: ConsoleMessage,
        id: UniqueId,
        registry: &ActorRegistry,
    ) {
        let level = match console_message.logLevel {
            LogLevel::Debug => "debug",
            LogLevel::Info => "info",
            LogLevel::Warn => "warn",
            LogLevel::Error => "error",
            _ => "log",
        }
        .to_owned();
        self.cached_events
            .borrow_mut()
            .entry(id.clone())
            .or_insert(vec![])
            .push(CachedConsoleMessage::ConsoleAPI(ConsoleAPI {
                type_: "ConsoleAPI".to_owned(),
                level: level.clone(),
                filename: console_message.filename.clone(),
                lineNumber: console_message.lineNumber as u32,
                functionName: "".to_string(), //TODO
                timeStamp: precise_time_ns(),
                private: false,
                arguments: vec![console_message.message.clone()],
            }));
        if id == self.current_unique_id(registry) {
            let msg = ConsoleAPICall {
                from: self.name(),
                type_: "consoleAPICall".to_owned(),
                message: ConsoleMsg {
                    level: level,
                    timeStamp: precise_time_ns(),
                    arguments: vec![console_message.message],
                    filename: console_message.filename,
                    lineNumber: console_message.lineNumber,
                    columnNumber: console_message.columnNumber,
                },
            };
            for stream in &mut *self.streams_mut(registry) {
                stream.write_json_packet(&msg);
            }
        }
    }
}

impl Actor for ConsoleActor {
    fn name(&self) -> String {
        self.name.clone()
    }

    fn handle_message(
        &self,
        registry: &ActorRegistry,
        msg_type: &str,
        msg: &Map<String, Value>,
        stream: &mut TcpStream,
    ) -> Result<ActorMessageStatus, ()> {
        Ok(match msg_type {
            "clearMessagesCache" => {
                self.cached_events
                    .borrow_mut()
                    .remove(&self.current_unique_id(registry));
                ActorMessageStatus::Processed
            },

            "getCachedMessages" => {
                let str_types = msg
                    .get("messageTypes")
                    .unwrap()
                    .as_array()
                    .unwrap()
                    .into_iter()
                    .map(|json_type| json_type.as_str().unwrap());
                let mut message_types = CachedConsoleMessageTypes::empty();
                for str_type in str_types {
                    match str_type {
                        "PageError" => message_types.insert(CachedConsoleMessageTypes::PAGE_ERROR),
                        "ConsoleAPI" => {
                            message_types.insert(CachedConsoleMessageTypes::CONSOLE_API)
                        },
                        s => debug!("unrecognized message type requested: \"{}\"", s),
                    };
                }
                let mut messages = vec![];
                for event in self
                    .cached_events
                    .borrow()
                    .get(&self.current_unique_id(registry))
                    .unwrap_or(&vec![])
                    .iter()
                {
                    let include = match event {
                        CachedConsoleMessage::PageError(_)
                            if message_types.contains(CachedConsoleMessageTypes::PAGE_ERROR) =>
                        {
                            true
                        },
                        CachedConsoleMessage::ConsoleAPI(_)
                            if message_types.contains(CachedConsoleMessageTypes::CONSOLE_API) =>
                        {
                            true
                        },
                        _ => false,
                    };
                    if include {
                        let json_string = event.encode().unwrap();
                        let json = serde_json::from_str::<Value>(&json_string).unwrap();
                        messages.push(json.as_object().unwrap().to_owned())
                    }
                }

                let msg = GetCachedMessagesReply {
                    from: self.name(),
                    messages: messages,
                };
                stream.write_json_packet(&msg);
                ActorMessageStatus::Processed
            },

            "startListeners" => {
                //TODO: actually implement listener filters that support starting/stopping
                let listeners = msg.get("listeners").unwrap().as_array().unwrap().to_owned();
                let msg = StartedListenersReply {
                    from: self.name(),
                    nativeConsoleAPI: true,
                    startedListeners: listeners
                        .into_iter()
                        .map(|s| s.as_str().unwrap().to_owned())
                        .collect(),
                    traits: StartedListenersTraits,
                };
                stream.write_json_packet(&msg);
                ActorMessageStatus::Processed
            },

            "stopListeners" => {
                //TODO: actually implement listener filters that support starting/stopping
                let msg = StopListenersReply {
                    from: self.name(),
                    stoppedListeners: msg
                        .get("listeners")
                        .unwrap()
                        .as_array()
                        .unwrap_or(&vec![])
                        .iter()
                        .map(|listener| listener.as_str().unwrap().to_owned())
                        .collect(),
                };
                stream.write_json_packet(&msg);
                ActorMessageStatus::Processed
            },

            //TODO: implement autocompletion like onAutocomplete in
            //      http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/webconsole.js
            "autocomplete" => {
                let msg = AutocompleteReply {
                    from: self.name(),
                    matches: vec![],
                    matchProp: "".to_owned(),
                };
                stream.write_json_packet(&msg);
                ActorMessageStatus::Processed
            },

            "evaluateJS" => {
                let msg = self.evaluateJS(&registry, &msg);
                stream.write_json_packet(&msg);
                ActorMessageStatus::Processed
            },

            "evaluateJSAsync" => {
                let resultID = Uuid::new_v4().to_string();
                let early_reply = EvaluateJSAsyncReply {
                    from: self.name(),
                    resultID: resultID.clone(),
                };
                // Emit an eager reply so that the client starts listening
                // for an async event with the resultID
                stream.write_json_packet(&early_reply);

                if msg.get("eager").and_then(|v| v.as_bool()).unwrap_or(false) {
                    // We don't support the side-effect free evaluation that eager evalaution
                    // really needs.
                    return Ok(ActorMessageStatus::Processed);
                }

                let reply = self.evaluateJS(&registry, &msg).unwrap();
                let msg = EvaluateJSEvent {
                    from: self.name(),
                    r#type: "evaluationResult".to_owned(),
                    input: reply.input,
                    result: reply.result,
                    timestamp: reply.timestamp,
                    resultID: resultID,
                    exception: reply.exception,
                    exceptionMessage: reply.exceptionMessage,
                    helperResult: reply.helperResult,
                };
                // Send the data from evaluateJS along with a resultID
                stream.write_json_packet(&msg);
                ActorMessageStatus::Processed
            },

            "setPreferences" => {
                let msg = SetPreferencesReply {
                    from: self.name(),
                    updated: vec![],
                };
                stream.write_json_packet(&msg);
                ActorMessageStatus::Processed
            },

            _ => ActorMessageStatus::Ignored,
        })
    }
}

#[derive(Serialize)]
struct ConsoleAPICall {
    from: String,
    #[serde(rename = "type")]
    type_: String,
    message: ConsoleMsg,
}

#[derive(Serialize)]
struct ConsoleMsg {
    level: String,
    timeStamp: u64,
    arguments: Vec<String>,
    filename: String,
    lineNumber: usize,
    columnNumber: usize,
}

#[derive(Serialize)]
struct PageErrorMsg {
    from: String,
    #[serde(rename = "type")]
    type_: String,
    pageError: PageError,
}
<|endoftext|>


<commit_before>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
#![deny(missing_docs)]
//! Minimal implementation of the [HTTP/1.0](https://tools.ietf.org/html/rfc1945)
//! and [HTTP/1.1](https://www.ietf.org/rfc/rfc2616.txt) protocols.
//!
//! HTTP/1.1 has a mandatory header **Host**, but as this crate is only used
//! for parsing MMDS requests, this header (if present) is ignored.
//!
//! This HTTP implementation is stateless thus it does not support chunking or
//! compression.
//!
//! ## Supported Headers
//! The **micro_http** crate has support for parsing the following **Request**
//! headers:
//! - Content-Length
//! - Expect
//! - Transfer-Encoding
//!
//! The **Response** does not have a public interface for adding headers, but whenever
//! a write to the **Body** is made, the headers **ContentLength** and **MediaType**
//! are automatically updated.
//!
//! ### Media Types
//! The supported media types are:
//! - text/plain
//! - application/json
//!
//! ## Supported Methods
//! The supported HTTP Methods are:
//! - GET
//! - PUT
//! - PATCH
//!
//! ## Supported Status Codes
//! The supported status codes are:
//!
//! - Continue - 100
//! - OK - 200
//! - No Content - 204
//! - Bad Request - 400
//! - Not Found - 404
//! - Internal Server Error - 500
//! - Not Implemented - 501
//!
//! ## Example for parsing an HTTP Request from a slice
//! ```
//! extern crate micro_http;
//! use micro_http::{Request, Version};
//!
//! let http_request = Request::try_from(b"GET http://localhost/home HTTP/1.0\r\n\r\n").unwrap();
//! assert_eq!(http_request.http_version(), Version::Http10);
//! assert_eq!(http_request.uri().get_abs_path(), "/home");
//! ```
//!
//! ## Example for creating an HTTP Response
//! ```
//! extern crate micro_http;
//! use micro_http::{Body, Response, StatusCode, Version};
//!
//! let mut response = Response::new(Version::Http10, StatusCode::OK);
//! let body = String::from("This is a test");
//! response.set_body(Body::new(body.clone()));
//!
//! assert!(response.status() == StatusCode::OK);
//! assert_eq!(response.body().unwrap(), Body::new(body));
//! assert_eq!(response.http_version(), Version::Http10);
//!
//! let mut response_buf: [u8; 126] = [0; 126];
//! assert!(response.write_all(&mut response_buf.as_mut()).is_ok());
//! ```

mod common;
mod connection;
mod request;
mod response;
use common::ascii;
use common::headers;

pub use connection::HttpConnection;
pub use request::{Request, RequestError};
pub use response::{Response, StatusCode};

pub use common::headers::Headers;
pub use common::{Body, Version};
<commit_msg>micro_http: Export the Method enum<commit_after>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
#![deny(missing_docs)]
//! Minimal implementation of the [HTTP/1.0](https://tools.ietf.org/html/rfc1945)
//! and [HTTP/1.1](https://www.ietf.org/rfc/rfc2616.txt) protocols.
//!
//! HTTP/1.1 has a mandatory header **Host**, but as this crate is only used
//! for parsing MMDS requests, this header (if present) is ignored.
//!
//! This HTTP implementation is stateless thus it does not support chunking or
//! compression.
//!
//! ## Supported Headers
//! The **micro_http** crate has support for parsing the following **Request**
//! headers:
//! - Content-Length
//! - Expect
//! - Transfer-Encoding
//!
//! The **Response** does not have a public interface for adding headers, but whenever
//! a write to the **Body** is made, the headers **ContentLength** and **MediaType**
//! are automatically updated.
//!
//! ### Media Types
//! The supported media types are:
//! - text/plain
//! - application/json
//!
//! ## Supported Methods
//! The supported HTTP Methods are:
//! - GET
//! - PUT
//! - PATCH
//!
//! ## Supported Status Codes
//! The supported status codes are:
//!
//! - Continue - 100
//! - OK - 200
//! - No Content - 204
//! - Bad Request - 400
//! - Not Found - 404
//! - Internal Server Error - 500
//! - Not Implemented - 501
//!
//! ## Example for parsing an HTTP Request from a slice
//! ```
//! extern crate micro_http;
//! use micro_http::{Request, Version};
//!
//! let http_request = Request::try_from(b"GET http://localhost/home HTTP/1.0\r\n\r\n").unwrap();
//! assert_eq!(http_request.http_version(), Version::Http10);
//! assert_eq!(http_request.uri().get_abs_path(), "/home");
//! ```
//!
//! ## Example for creating an HTTP Response
//! ```
//! extern crate micro_http;
//! use micro_http::{Body, Response, StatusCode, Version};
//!
//! let mut response = Response::new(Version::Http10, StatusCode::OK);
//! let body = String::from("This is a test");
//! response.set_body(Body::new(body.clone()));
//!
//! assert!(response.status() == StatusCode::OK);
//! assert_eq!(response.body().unwrap(), Body::new(body));
//! assert_eq!(response.http_version(), Version::Http10);
//!
//! let mut response_buf: [u8; 126] = [0; 126];
//! assert!(response.write_all(&mut response_buf.as_mut()).is_ok());
//! ```

mod common;
mod connection;
mod request;
mod response;
use common::ascii;
use common::headers;

pub use connection::HttpConnection;
pub use request::{Request, RequestError};
pub use response::{Response, StatusCode};

pub use common::headers::Headers;
pub use common::{Body, Method, Version};
<|endoftext|>


<commit_before>use std::cell::{Cell, RefCell};
use std::io::{self, ErrorKind};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use std::sync::mpsc;
use std::time::Instant;

use mio;
use mio::channel::SendError;
use slab::Slab;
use futures::{Future, Task, TaskHandle, Poll};
use futures::io::Ready;

use slot::{self, Slot};

static NEXT_LOOP_ID: AtomicUsize = ATOMIC_USIZE_INIT;
scoped_thread_local!(static CURRENT_LOOP: Loop);

const SLAB_CAPACITY: usize = 1024 * 64;

/// An event loop.
///
/// The event loop is the main source of blocking in an application which drives
/// all other I/O events and notifications happening. Each event loop can have
/// multiple handles pointing to it, each of which can then be used to create
/// various I/O objects to interact with the event loop in interesting ways.
// TODO: expand this
pub struct Loop {
    id: usize,
    active: Cell<bool>,
    io: mio::Poll,
    tx: mio::channel::Sender<Message>,
    rx: mio::channel::Receiver<Message>,
    dispatch: RefCell<Slab<Scheduled, usize>>,
}

/// Handle to an event loop, used to construct I/O objects, send messages, and
/// otherwise interact indirectly with the event loop itself.
///
/// Handles can be cloned, and when cloned they will still refer to the
/// same underlying event loop.
#[derive(Clone)]
pub struct LoopHandle {
    id: usize,
    tx: mio::channel::Sender<Message>,
}

struct Scheduled {
    source: IoSource,
    waiter: Option<TaskHandle>,
}

enum Message {
    AddSource(IoSource, Arc<Slot<io::Result<usize>>>),
    DropSource(usize),
    Schedule(usize, TaskHandle),
    Deschedule(usize),
    Shutdown,
}

pub struct Source<E: ?Sized> {
    readiness: AtomicUsize,
    io: E,
}

pub type IoSource = Arc<Source<mio::Evented + Sync + Send>>;

fn register(poll: &mio::Poll,
            token: usize,
            sched: &Scheduled) -> io::Result<()> {
    poll.register(&sched.source.io,
                  mio::Token(token),
                  mio::EventSet::readable() | mio::EventSet::writable(),
                  mio::PollOpt::edge())
}

fn deregister(poll: &mio::Poll, sched: &Scheduled) {
    // TODO: handle error
    poll.deregister(&sched.source.io).unwrap();
}

impl Loop {
    /// Creates a new event loop, returning any error that happened during the
    /// creation.
    pub fn new() -> io::Result<Loop> {
        let (tx, rx) = mio::channel::from_std_channel(mpsc::channel());
        let io = try!(mio::Poll::new());
        try!(io.register(&rx,
                         mio::Token(0),
                         mio::EventSet::readable(),
                         mio::PollOpt::edge()));
        Ok(Loop {
            id: NEXT_LOOP_ID.fetch_add(1, Ordering::Relaxed),
            active: Cell::new(true),
            io: io,
            tx: tx,
            rx: rx,
            dispatch: RefCell::new(Slab::new_starting_at(1, SLAB_CAPACITY)),
        })
    }

    /// Generates a handle to this event loop used to construct I/O objects and
    /// send messages.
    ///
    /// Handles to an event loop are cloneable as well and clones will always
    /// refer to the same event loop.
    pub fn handle(&self) -> LoopHandle {
        LoopHandle {
            id: self.id,
            tx: self.tx.clone(),
        }
    }

    #[allow(missing_docs)]
    pub fn run<F: Future>(&mut self, f: F) -> Result<F::Item, F::Error> {
        let (tx_res, rx_res) = mpsc::channel();
        let handle = self.handle();
        f.then(move |res| {
            handle.shutdown();
            tx_res.send(res)
        }).forget();

        self._run();

        rx_res.recv().unwrap()
    }

    fn _run(&mut self) {
        let mut events = mio::Events::new();
        self.active.set(true);
        while self.active.get() {
            let amt;
            // On Linux, Poll::poll is epoll_wait, which may return EINTR if a
            // ptracer attaches. This retry loop prevents crashing when
            // attaching strace, or similar.
            let start = Instant::now();
            loop {
                match self.io.poll(&mut events, None) {
                    Ok(a) => {
                        amt = a;
                        break;
                    }
                    Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
                    err @ Err(_) => {
                        err.unwrap();
                    }
                }
            }
            debug!("loop poll - {:?}", start.elapsed());

            // TODO: coalesce token sets for a given Wake?
            let start = Instant::now();
            for i in 0..events.len() {
                let event = events.get(i).unwrap();
                let token = usize::from(event.token());

                if token == 0 {
                    self.consume_queue();
                    continue
                }

                let mut waiter = None;

                if let Some(sched) = self.dispatch.borrow_mut().get_mut(token) {
                    waiter = sched.waiter.take();
                    if event.kind().is_readable() {
                        sched.source.readiness.fetch_or(1, Ordering::Relaxed);
                    }
                    if event.kind().is_writable() {
                        sched.source.readiness.fetch_or(2, Ordering::Relaxed);
                    }
                } else {
                    debug!("notified on {} which no longer exists", token);
                }
                debug!("dispatching {:?} {:?}", event.token(), event.kind());

                CURRENT_LOOP.set(&self, move || {
                    match waiter {
                        Some(waiter) => waiter.notify(),
                        None => debug!("no waiter"),
                    }
                });
            }

            debug!("loop process - {} events, {:?}", amt, start.elapsed());
        }
    }

    fn add_source(&self, source: IoSource) -> io::Result<usize> {
        let sched = Scheduled {
            source: source,
            waiter: None,
        };
        let mut dispatch = self.dispatch.borrow_mut();
        if dispatch.vacant_entry().is_none() {
            let amt = dispatch.count();
            dispatch.grow(amt);
        }
        let entry = dispatch.vacant_entry().unwrap();
        try!(register(&self.io, entry.index(), &sched));
        Ok(entry.insert(sched).index())
    }

    fn drop_source(&self, token: usize) {
        let sched = self.dispatch.borrow_mut().remove(token).unwrap();
        deregister(&self.io, &sched);
    }

    fn schedule(&self, token: usize, wake: TaskHandle) {
        let to_call = {
            let mut dispatch = self.dispatch.borrow_mut();
            let sched = dispatch.get_mut(token).unwrap();
            if sched.source.readiness.load(Ordering::Relaxed) != 0 {
                sched.waiter = None;
                Some(wake)
            } else {
                sched.waiter = Some(wake);
                None
            }
        };
        if let Some(to_call) = to_call {
            to_call.notify();
        }
    }

    fn deschedule(&self, token: usize) {
        let mut dispatch = self.dispatch.borrow_mut();
        dispatch.get_mut(token).unwrap();
    }

    fn consume_queue(&self) {
        while let Ok(msg) = self.rx.try_recv() {
            self.notify(msg);
        }
    }

    fn notify(&self, msg: Message) {
        match msg {
            Message::AddSource(source, slot) => {
                // This unwrap() should always be ok as we're the only producer
                slot.try_produce(self.add_source(source))
                    .ok().expect("interference with try_produce");
            }
            Message::DropSource(tok) => self.drop_source(tok),
            Message::Schedule(tok, wake) => self.schedule(tok, wake),
            Message::Deschedule(tok) => self.deschedule(tok),
            Message::Shutdown => self.active.set(false),
        }
    }
}

impl LoopHandle {
    fn send(&self, msg: Message) {
        self.with_loop(|lp| {
            match lp {
                Some(lp) => {
                    // Need to execute all existing requests first, to ensure
                    // that our message is processed "in order"
                    lp.consume_queue();
                    lp.notify(msg);
                }
                None => {
                    match self.tx.send(msg) {
                        Ok(()) => {}

                        // This should only happen when there was an error
                        // writing to the pipe to wake up the event loop,
                        // hopefully that never happens
                        Err(SendError::Io(e)) => {
                            panic!("error sending message to event loop: {}", e)
                        }

                        // If we're still sending a message to the event loop
                        // after it's closed, then that's bad!
                        Err(SendError::Disconnected(_)) => {
                            panic!("event loop is no longer available")
                        }
                    }
                }
            }
        })
    }

    fn with_loop<F, R>(&self, f: F) -> R
        where F: FnOnce(Option<&Loop>) -> R
    {
        if CURRENT_LOOP.is_set() {
            CURRENT_LOOP.with(|lp| {
                if lp.id == self.id {
                    f(Some(lp))
                } else {
                    f(None)
                }
            })
        } else {
            f(None)
        }
    }

    /// Add a new source to an event loop, returning a future which will resolve
    /// to the token that can be used to identify this source.
    ///
    /// When a new I/O object is created it needs to be communicated to the
    /// event loop to ensure that it's registered and ready to receive
    /// notifications. The event loop with then respond with a unique token that
    /// this handle can be identified with (the resolved value of the returned
    /// future).
    ///
    /// This token is then passed in turn to each of the methods below to
    /// interact with notifications on the I/O object itself.
    ///
    /// # Panics
    ///
    /// The returned future will panic if the event loop this handle is
    /// associated with has gone away, or if there is an error communicating
    /// with the event loop.
    pub fn add_source(&self, source: IoSource) -> AddSource {
        AddSource {
            loop_handle: self.clone(),
            source: Some(source),
            result: None,
        }
    }

    fn add_source_(&self, source: IoSource, slot: Arc<Slot<io::Result<usize>>>) {
        self.send(Message::AddSource(source, slot));
    }

    /// Begin listening for events on an event loop.
    ///
    /// Once an I/O object has been registered with the event loop through the
    /// `add_source` method, this method can be used with the assigned token to
    /// begin awaiting notifications.
    ///
    /// The `dir` argument indicates how the I/O object is expected to be
    /// awaited on (either readable or writable) and the `wake` callback will be
    /// invoked. Note that one the `wake` callback is invoked once it will not
    /// be invoked again, it must be re-`schedule`d to continue receiving
    /// notifications.
    ///
    /// # Panics
    ///
    /// This function will panic if the event loop this handle is associated
    /// with has gone away, or if there is an error communicating with the event
    /// loop.
    pub fn schedule(&self, tok: usize, task: &mut Task) {
        // TODO: plumb through `&mut Task` if we're on the event loop
        self.send(Message::Schedule(tok, task.handle().clone()));
    }

    /// Stop listening for events on an event loop.
    ///
    /// Once a callback has been scheduled with the `schedule` method, it can be
    /// unregistered from the event loop with this method. This method does not
    /// guarantee that the callback will not be invoked if it hasn't already,
    /// but a best effort will be made to ensure it is not called.
    ///
    /// # Panics
    ///
    /// This function will panic if the event loop this handle is associated
    /// with has gone away, or if there is an error communicating with the event
    /// loop.
    pub fn deschedule(&self, tok: usize) {
        self.send(Message::Deschedule(tok));
    }

    /// Unregister all information associated with a token on an event loop,
    /// deallocating all internal resources assigned to the given token.
    ///
    /// This method should be called whenever a source of events is being
    /// destroyed. This will ensure that the event loop can reuse `tok` for
    /// another I/O object if necessary and also remove it from any poll
    /// notifications and callbacks.
    ///
    /// Note that wake callbacks may still be invoked after this method is
    /// called as it may take some time for the message to drop a source to
    /// reach the event loop. Despite this fact, this method will attempt to
    /// ensure that the callbacks are **not** invoked, so pending scheduled
    /// callbacks cannot be relied upon to get called.
    ///
    /// # Panics
    ///
    /// This function will panic if the event loop this handle is associated
    /// with has gone away, or if there is an error communicating with the event
    /// loop.
    pub fn drop_source(&self, tok: usize) {
        self.send(Message::DropSource(tok));
    }

    /// Send a message to the associated event loop that it should shut down, or
    /// otherwise break out of its current loop of iteration.
    ///
    /// This method does not forcibly cause the event loop to shut down or
    /// perform an interrupt on whatever task is currently running, instead a
    /// message is simply enqueued to at a later date process the request to
    /// stop looping ASAP.
    ///
    /// # Panics
    ///
    /// This function will panic if the event loop this handle is associated
    /// with has gone away, or if there is an error communicating with the event
    /// loop.
    pub fn shutdown(&self) {
        self.send(Message::Shutdown);
    }
}

/// A future which will resolve a unique `tok` token for an I/O object.
///
/// Created through the `LoopHandle::add_source` method, this future can also
/// resolve to an error if there's an issue communicating with the event loop.
pub struct AddSource {
    loop_handle: LoopHandle,
    source: Option<IoSource>,
    result: Option<(Arc<Slot<io::Result<usize>>>, slot::Token)>,
}

impl Future for AddSource {
    type Item = usize;
    type Error = io::Error;

    fn poll(&mut self, _task: &mut Task) -> Poll<usize, io::Error> {
        match self.result {
            Some((ref result, ref token)) => {
                result.cancel(*token);
                match result.try_consume() {
                    Ok(t) => t.into(),
                    Err(_) => Poll::NotReady,
                }
            }
            None => {
                let source = &mut self.source;
                self.loop_handle.with_loop(|lp| {
                    match lp {
                        Some(lp) => lp.add_source(source.take().unwrap()).into(),
                        None => Poll::NotReady,
                    }
                })
            }
        }
    }

    fn schedule(&mut self, task: &mut Task) {
        if let Some((ref result, ref mut token)) = self.result {
            result.cancel(*token);
            let handle = task.handle().clone();
            *token = result.on_full(move |_| {
                handle.notify();
            });
            return
        }

        let handle = task.handle().clone();
        let result = Arc::new(Slot::new(None));
        let token = result.on_full(move |_| {
            handle.notify();
        });
        self.result = Some((result.clone(), token));
        self.loop_handle.add_source_(self.source.take().unwrap(), result);
    }
}

impl<E> Source<E> {
    pub fn new(e: E) -> Source<E> {
        Source {
            readiness: AtomicUsize::new(0),
            io: e,
        }
    }
}

impl<E: ?Sized> Source<E> {
    pub fn take_readiness(&self) -> Option<Ready> {
        match self.readiness.swap(0, Ordering::SeqCst) {
            0 => None,
            1 => Some(Ready::Read),
            2 => Some(Ready::Write),
            3 => Some(Ready::ReadWrite),
            _ => panic!(),
        }
    }

    pub fn io(&self) -> &E {
        &self.io
    }
}
<commit_msg>Add a few small debugging statements<commit_after>use std::cell::{Cell, RefCell};
use std::io::{self, ErrorKind};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use std::sync::mpsc;
use std::time::Instant;

use mio;
use mio::channel::SendError;
use slab::Slab;
use futures::{Future, Task, TaskHandle, Poll};
use futures::io::Ready;

use slot::{self, Slot};

static NEXT_LOOP_ID: AtomicUsize = ATOMIC_USIZE_INIT;
scoped_thread_local!(static CURRENT_LOOP: Loop);

const SLAB_CAPACITY: usize = 1024 * 64;

/// An event loop.
///
/// The event loop is the main source of blocking in an application which drives
/// all other I/O events and notifications happening. Each event loop can have
/// multiple handles pointing to it, each of which can then be used to create
/// various I/O objects to interact with the event loop in interesting ways.
// TODO: expand this
pub struct Loop {
    id: usize,
    active: Cell<bool>,
    io: mio::Poll,
    tx: mio::channel::Sender<Message>,
    rx: mio::channel::Receiver<Message>,
    dispatch: RefCell<Slab<Scheduled, usize>>,
}

/// Handle to an event loop, used to construct I/O objects, send messages, and
/// otherwise interact indirectly with the event loop itself.
///
/// Handles can be cloned, and when cloned they will still refer to the
/// same underlying event loop.
#[derive(Clone)]
pub struct LoopHandle {
    id: usize,
    tx: mio::channel::Sender<Message>,
}

struct Scheduled {
    source: IoSource,
    waiter: Option<TaskHandle>,
}

enum Message {
    AddSource(IoSource, Arc<Slot<io::Result<usize>>>),
    DropSource(usize),
    Schedule(usize, TaskHandle),
    Deschedule(usize),
    Shutdown,
}

pub struct Source<E: ?Sized> {
    readiness: AtomicUsize,
    io: E,
}

pub type IoSource = Arc<Source<mio::Evented + Sync + Send>>;

fn register(poll: &mio::Poll,
            token: usize,
            sched: &Scheduled) -> io::Result<()> {
    poll.register(&sched.source.io,
                  mio::Token(token),
                  mio::EventSet::readable() | mio::EventSet::writable(),
                  mio::PollOpt::edge())
}

fn deregister(poll: &mio::Poll, sched: &Scheduled) {
    // TODO: handle error
    poll.deregister(&sched.source.io).unwrap();
}

impl Loop {
    /// Creates a new event loop, returning any error that happened during the
    /// creation.
    pub fn new() -> io::Result<Loop> {
        let (tx, rx) = mio::channel::from_std_channel(mpsc::channel());
        let io = try!(mio::Poll::new());
        try!(io.register(&rx,
                         mio::Token(0),
                         mio::EventSet::readable(),
                         mio::PollOpt::edge()));
        Ok(Loop {
            id: NEXT_LOOP_ID.fetch_add(1, Ordering::Relaxed),
            active: Cell::new(true),
            io: io,
            tx: tx,
            rx: rx,
            dispatch: RefCell::new(Slab::new_starting_at(1, SLAB_CAPACITY)),
        })
    }

    /// Generates a handle to this event loop used to construct I/O objects and
    /// send messages.
    ///
    /// Handles to an event loop are cloneable as well and clones will always
    /// refer to the same event loop.
    pub fn handle(&self) -> LoopHandle {
        LoopHandle {
            id: self.id,
            tx: self.tx.clone(),
        }
    }

    #[allow(missing_docs)]
    pub fn run<F: Future>(&mut self, f: F) -> Result<F::Item, F::Error> {
        let (tx_res, rx_res) = mpsc::channel();
        let handle = self.handle();
        f.then(move |res| {
            handle.shutdown();
            tx_res.send(res)
        }).forget();

        self._run();

        rx_res.recv().unwrap()
    }

    fn _run(&mut self) {
        let mut events = mio::Events::new();
        self.active.set(true);
        while self.active.get() {
            let amt;
            // On Linux, Poll::poll is epoll_wait, which may return EINTR if a
            // ptracer attaches. This retry loop prevents crashing when
            // attaching strace, or similar.
            let start = Instant::now();
            loop {
                match self.io.poll(&mut events, None) {
                    Ok(a) => {
                        amt = a;
                        break;
                    }
                    Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
                    err @ Err(_) => {
                        err.unwrap();
                    }
                }
            }
            debug!("loop poll - {:?}", start.elapsed());

            // TODO: coalesce token sets for a given Wake?
            let start = Instant::now();
            for i in 0..events.len() {
                let event = events.get(i).unwrap();
                let token = usize::from(event.token());

                if token == 0 {
                    debug!("consuming notification queue");
                    self.consume_queue();
                    continue
                }

                let mut waiter = None;

                if let Some(sched) = self.dispatch.borrow_mut().get_mut(token) {
                    waiter = sched.waiter.take();
                    if event.kind().is_readable() {
                        sched.source.readiness.fetch_or(1, Ordering::Relaxed);
                    }
                    if event.kind().is_writable() {
                        sched.source.readiness.fetch_or(2, Ordering::Relaxed);
                    }
                } else {
                    debug!("notified on {} which no longer exists", token);
                }
                debug!("dispatching {:?} {:?}", event.token(), event.kind());

                CURRENT_LOOP.set(&self, move || {
                    match waiter {
                        Some(waiter) => waiter.notify(),
                        None => debug!("no waiter"),
                    }
                });
            }

            debug!("loop process - {} events, {:?}", amt, start.elapsed());
        }

        debug!("loop is done!");
    }

    fn add_source(&self, source: IoSource) -> io::Result<usize> {
        let sched = Scheduled {
            source: source,
            waiter: None,
        };
        let mut dispatch = self.dispatch.borrow_mut();
        if dispatch.vacant_entry().is_none() {
            let amt = dispatch.count();
            dispatch.grow(amt);
        }
        let entry = dispatch.vacant_entry().unwrap();
        try!(register(&self.io, entry.index(), &sched));
        Ok(entry.insert(sched).index())
    }

    fn drop_source(&self, token: usize) {
        let sched = self.dispatch.borrow_mut().remove(token).unwrap();
        deregister(&self.io, &sched);
    }

    fn schedule(&self, token: usize, wake: TaskHandle) {
        let to_call = {
            let mut dispatch = self.dispatch.borrow_mut();
            let sched = dispatch.get_mut(token).unwrap();
            if sched.source.readiness.load(Ordering::Relaxed) != 0 {
                sched.waiter = None;
                Some(wake)
            } else {
                sched.waiter = Some(wake);
                None
            }
        };
        if let Some(to_call) = to_call {
            to_call.notify();
        }
    }

    fn deschedule(&self, token: usize) {
        let mut dispatch = self.dispatch.borrow_mut();
        dispatch.get_mut(token).unwrap();
    }

    fn consume_queue(&self) {
        while let Ok(msg) = self.rx.try_recv() {
            self.notify(msg);
        }
    }

    fn notify(&self, msg: Message) {
        match msg {
            Message::AddSource(source, slot) => {
                // This unwrap() should always be ok as we're the only producer
                slot.try_produce(self.add_source(source))
                    .ok().expect("interference with try_produce");
            }
            Message::DropSource(tok) => self.drop_source(tok),
            Message::Schedule(tok, wake) => self.schedule(tok, wake),
            Message::Deschedule(tok) => self.deschedule(tok),
            Message::Shutdown => self.active.set(false),
        }
    }
}

impl LoopHandle {
    fn send(&self, msg: Message) {
        self.with_loop(|lp| {
            match lp {
                Some(lp) => {
                    // Need to execute all existing requests first, to ensure
                    // that our message is processed "in order"
                    lp.consume_queue();
                    lp.notify(msg);
                }
                None => {
                    match self.tx.send(msg) {
                        Ok(()) => {}

                        // This should only happen when there was an error
                        // writing to the pipe to wake up the event loop,
                        // hopefully that never happens
                        Err(SendError::Io(e)) => {
                            panic!("error sending message to event loop: {}", e)
                        }

                        // If we're still sending a message to the event loop
                        // after it's closed, then that's bad!
                        Err(SendError::Disconnected(_)) => {
                            panic!("event loop is no longer available")
                        }
                    }
                }
            }
        })
    }

    fn with_loop<F, R>(&self, f: F) -> R
        where F: FnOnce(Option<&Loop>) -> R
    {
        if CURRENT_LOOP.is_set() {
            CURRENT_LOOP.with(|lp| {
                if lp.id == self.id {
                    f(Some(lp))
                } else {
                    f(None)
                }
            })
        } else {
            f(None)
        }
    }

    /// Add a new source to an event loop, returning a future which will resolve
    /// to the token that can be used to identify this source.
    ///
    /// When a new I/O object is created it needs to be communicated to the
    /// event loop to ensure that it's registered and ready to receive
    /// notifications. The event loop with then respond with a unique token that
    /// this handle can be identified with (the resolved value of the returned
    /// future).
    ///
    /// This token is then passed in turn to each of the methods below to
    /// interact with notifications on the I/O object itself.
    ///
    /// # Panics
    ///
    /// The returned future will panic if the event loop this handle is
    /// associated with has gone away, or if there is an error communicating
    /// with the event loop.
    pub fn add_source(&self, source: IoSource) -> AddSource {
        AddSource {
            loop_handle: self.clone(),
            source: Some(source),
            result: None,
        }
    }

    fn add_source_(&self, source: IoSource, slot: Arc<Slot<io::Result<usize>>>) {
        self.send(Message::AddSource(source, slot));
    }

    /// Begin listening for events on an event loop.
    ///
    /// Once an I/O object has been registered with the event loop through the
    /// `add_source` method, this method can be used with the assigned token to
    /// begin awaiting notifications.
    ///
    /// The `dir` argument indicates how the I/O object is expected to be
    /// awaited on (either readable or writable) and the `wake` callback will be
    /// invoked. Note that one the `wake` callback is invoked once it will not
    /// be invoked again, it must be re-`schedule`d to continue receiving
    /// notifications.
    ///
    /// # Panics
    ///
    /// This function will panic if the event loop this handle is associated
    /// with has gone away, or if there is an error communicating with the event
    /// loop.
    pub fn schedule(&self, tok: usize, task: &mut Task) {
        // TODO: plumb through `&mut Task` if we're on the event loop
        self.send(Message::Schedule(tok, task.handle().clone()));
    }

    /// Stop listening for events on an event loop.
    ///
    /// Once a callback has been scheduled with the `schedule` method, it can be
    /// unregistered from the event loop with this method. This method does not
    /// guarantee that the callback will not be invoked if it hasn't already,
    /// but a best effort will be made to ensure it is not called.
    ///
    /// # Panics
    ///
    /// This function will panic if the event loop this handle is associated
    /// with has gone away, or if there is an error communicating with the event
    /// loop.
    pub fn deschedule(&self, tok: usize) {
        self.send(Message::Deschedule(tok));
    }

    /// Unregister all information associated with a token on an event loop,
    /// deallocating all internal resources assigned to the given token.
    ///
    /// This method should be called whenever a source of events is being
    /// destroyed. This will ensure that the event loop can reuse `tok` for
    /// another I/O object if necessary and also remove it from any poll
    /// notifications and callbacks.
    ///
    /// Note that wake callbacks may still be invoked after this method is
    /// called as it may take some time for the message to drop a source to
    /// reach the event loop. Despite this fact, this method will attempt to
    /// ensure that the callbacks are **not** invoked, so pending scheduled
    /// callbacks cannot be relied upon to get called.
    ///
    /// # Panics
    ///
    /// This function will panic if the event loop this handle is associated
    /// with has gone away, or if there is an error communicating with the event
    /// loop.
    pub fn drop_source(&self, tok: usize) {
        self.send(Message::DropSource(tok));
    }

    /// Send a message to the associated event loop that it should shut down, or
    /// otherwise break out of its current loop of iteration.
    ///
    /// This method does not forcibly cause the event loop to shut down or
    /// perform an interrupt on whatever task is currently running, instead a
    /// message is simply enqueued to at a later date process the request to
    /// stop looping ASAP.
    ///
    /// # Panics
    ///
    /// This function will panic if the event loop this handle is associated
    /// with has gone away, or if there is an error communicating with the event
    /// loop.
    pub fn shutdown(&self) {
        self.send(Message::Shutdown);
    }
}

/// A future which will resolve a unique `tok` token for an I/O object.
///
/// Created through the `LoopHandle::add_source` method, this future can also
/// resolve to an error if there's an issue communicating with the event loop.
pub struct AddSource {
    loop_handle: LoopHandle,
    source: Option<IoSource>,
    result: Option<(Arc<Slot<io::Result<usize>>>, slot::Token)>,
}

impl Future for AddSource {
    type Item = usize;
    type Error = io::Error;

    fn poll(&mut self, _task: &mut Task) -> Poll<usize, io::Error> {
        match self.result {
            Some((ref result, ref token)) => {
                result.cancel(*token);
                match result.try_consume() {
                    Ok(t) => t.into(),
                    Err(_) => Poll::NotReady,
                }
            }
            None => {
                let source = &mut self.source;
                self.loop_handle.with_loop(|lp| {
                    match lp {
                        Some(lp) => lp.add_source(source.take().unwrap()).into(),
                        None => Poll::NotReady,
                    }
                })
            }
        }
    }

    fn schedule(&mut self, task: &mut Task) {
        if let Some((ref result, ref mut token)) = self.result {
            result.cancel(*token);
            let handle = task.handle().clone();
            *token = result.on_full(move |_| {
                handle.notify();
            });
            return
        }

        let handle = task.handle().clone();
        let result = Arc::new(Slot::new(None));
        let token = result.on_full(move |_| {
            handle.notify();
        });
        self.result = Some((result.clone(), token));
        self.loop_handle.add_source_(self.source.take().unwrap(), result);
    }
}

impl<E> Source<E> {
    pub fn new(e: E) -> Source<E> {
        Source {
            readiness: AtomicUsize::new(0),
            io: e,
        }
    }
}

impl<E: ?Sized> Source<E> {
    pub fn take_readiness(&self) -> Option<Ready> {
        match self.readiness.swap(0, Ordering::SeqCst) {
            0 => None,
            1 => Some(Ready::Read),
            2 => Some(Ready::Write),
            3 => Some(Ready::ReadWrite),
            _ => panic!(),
        }
    }

    pub fn io(&self) -> &E {
        &self.io
    }
}
<|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/.

// Code to handle a single block device.

use std::collections::{HashMap, HashSet};
use std::io::ErrorKind;
use std::fs::{OpenOptions, read_dir};
use std::os::linux::fs::MetadataExt;
use std::path::PathBuf;
use std::str::FromStr;

use nix::Errno;
use nix::sys::stat::{S_IFBLK, S_IFMT};
use serde_json;

use devicemapper::Device;

use super::super::errors::{EngineResult, EngineError, ErrorEnum};
use super::super::types::PoolUuid;

use super::blockdev::BlockDev;
use super::blockdevmgr::BlockDevMgr;
use super::device::blkdev_size;
use super::engine::DevOwnership;
use super::metadata::{BDA, StaticHeader};
use super::range_alloc::RangeAllocator;
use super::serde_structs::PoolSave;

/// Find all Stratis devices.
///
/// Returns a map of pool uuids to a vector of devices for each pool.
pub fn find_all() -> EngineResult<HashMap<PoolUuid, Vec<PathBuf>>> {

    let mut pool_map = HashMap::new();
    for dir_e in try!(read_dir("/dev")) {
        let dir_e = try!(dir_e);
        let mode = try!(dir_e.metadata()).st_mode();

        // Device node can't belong to Stratis if it is not a block device
        if mode & S_IFMT.bits() != S_IFBLK.bits() {
            continue;
        }

        let devnode = dir_e.path();

        let f = OpenOptions::new().read(true).open(&devnode);

        // There are some reasons for OpenOptions::open() to return an error
        // which are not reasons for this method to return an error.
        // Try to distinguish. Non-error conditions are:
        //
        // 1. ENXIO: The device does not exist anymore. This means that the device
        // was volatile for some reason; in that case it can not belong to
        // Stratis so it is safe to ignore it.
        //
        // 2. ENOMEDIUM: The device has no medium. An example of this case is an
        // empty optical drive.
        //
        // Note that it is better to be conservative and return with an
        // error in any case where failure to read the device could result
        // in bad data for Stratis. Additional exceptions may be added,
        // but only with a complete justification.
        if f.is_err() {
            let err = f.unwrap_err();
            match err.kind() {
                ErrorKind::NotFound => {
                    continue;
                }
                _ => {
                    if let Some(errno) = err.raw_os_error() {
                        match Errno::from_i32(errno) {
                            Errno::ENXIO | Errno::ENOMEDIUM => continue,
                            _ => return Err(EngineError::Io(err)),
                        };
                    } else {
                        return Err(EngineError::Io(err));
                    }
                }
            }
        }

        let mut f = f.expect("unreachable if f is err");
        if let DevOwnership::Ours(uuid) = try!(StaticHeader::determine_ownership(&mut f)) {
            pool_map
                .entry(uuid)
                .or_insert_with(Vec::new)
                .push(devnode)
        };
    }

    Ok(pool_map)
}

/// Get the most recent metadata from a set of Devices for a given pool UUID.
/// Returns None if no metadata found for this pool.
pub fn get_metadata(pool_uuid: &PoolUuid, devnodes: &[PathBuf]) -> EngineResult<Option<PoolSave>> {

    // No device nodes means no metadata
    if devnodes.is_empty() {
        return Ok(None);
    }

    // Get pairs of device nodes and matching BDAs
    // If no BDA, or BDA UUID does not match pool UUID, skip.
    // If there is an error reading the BDA, error. There could have been
    // vital information on that BDA, for example, it may have contained
    // the newest metadata.
    let mut bdas = Vec::new();
    for devnode in devnodes {
        let bda = try!(BDA::load(&mut try!(OpenOptions::new().read(true).open(devnode))));
        if bda.is_none() {
            continue;
        }
        let bda = bda.expect("unreachable if bda is None");

        if bda.pool_uuid() != pool_uuid {
            continue;
        }
        bdas.push((devnode, bda));
    }

    // We may have had no devices with BDAs for this pool, so return if no BDAs.
    if bdas.is_empty() {
        return Ok(None);
    }

    // Get a most recent BDA
    let &(_, ref most_recent_bda) = bdas.iter()
        .max_by_key(|p| p.1.last_update_time())
        .expect("bdas is not empty, must have a max");

    // Most recent time should never be None if this was a properly
    // created pool; this allows for the method to be called in other
    // circumstances.
    let most_recent_time = most_recent_bda.last_update_time();
    if most_recent_time.is_none() {
        return Ok(None);
    }

    // Try to read from all available devnodes that could contain most
    // recent metadata. In the event of errors, continue to try until all are
    // exhausted.
    for &(devnode, ref bda) in
        bdas.iter()
            .filter(|p| p.1.last_update_time() == most_recent_time) {

        let f = OpenOptions::new().read(true).open(devnode);
        if f.is_err() {
            continue;
        }
        let mut f = f.expect("f is not err");

        if let Ok(Some(data)) = bda.load_state(&mut f) {
            let json: serde_json::Result<PoolSave> = serde_json::from_slice(&data);
            if let Ok(pool) = json {
                return Ok(Some(pool));
            } else {
                continue;
            }
        } else {
            continue;
        }
    }

    // If no data has yet returned, we have an error. That is, we should have
    // some metadata, because we have a most recent time, but we failed to
    // get any.
    let err_str = "timestamp indicates data was written, but no data succesfully read";
    Err(EngineError::Engine(ErrorEnum::NotFound, err_str.into()))
}

/// Get the most recent metadata for each pool.
/// Since the metadata is written immediately after a pool is created, it
/// is considered an error for a pool to be w/out metadata.
pub fn get_pool_metadata(pool_table: &HashMap<PoolUuid, Vec<PathBuf>>)
                         -> EngineResult<HashMap<PoolUuid, PoolSave>> {
    let mut metadata = HashMap::new();
    for (pool_uuid, devices) in pool_table.iter() {
        if let Ok(Some(pool)) = get_metadata(pool_uuid, &devices) {
            metadata.insert(pool_uuid.clone(), pool);
        } else {
            return Err(EngineError::Engine(ErrorEnum::NotFound,
                                           format!("no metadata for pool {}", pool_uuid)));
        }
    }
    Ok(metadata)
}

/// Instantiate each pool's blockdevs.
/// Return a map from the pool uuid to the pools blockdevs.
pub fn get_pool_blockdevs(devnode_table: &HashMap<PoolUuid, Vec<PathBuf>>,
                          metadata_table: &HashMap<PoolUuid, PoolSave>)
                          -> EngineResult<HashMap<PoolUuid, BlockDevMgr>> {
    let mut result = HashMap::new();
    for (pool_uuid, pool_save) in metadata_table {
        let segments = pool_save
            .flex_devs
            .meta_dev
            .iter()
            .chain(pool_save.flex_devs.thin_meta_dev.iter())
            .chain(pool_save.flex_devs.thin_data_dev.iter());

        let mut segment_table = HashMap::new();
        for seg in segments {
            segment_table
                .entry(seg.0.clone())
                .or_insert(vec![])
                .push((seg.1, seg.2))
        }

        let devnodes = devnode_table
            .get(&pool_uuid)
            .expect("devnode_table.keys() == metadata_table.keys()");

        let mut blockdevs = vec![];
        let mut devices = HashSet::new();
        for dev in devnodes {
            let device = try!(Device::from_str(&dev.to_string_lossy()));

            // If we've seen this device already, skip it.
            if devices.contains(&device) {
                continue;
            } else {
                devices.insert(device);
            }

            let bda = try!(BDA::load(&mut try!(OpenOptions::new().read(true).open(dev))));
            let bda = try!(bda.ok_or(EngineError::Engine(ErrorEnum::NotFound,
                                                         "no BDA found for Stratis device"
                                                             .into())));

            let actual_size = try!(blkdev_size(&try!(OpenOptions::new().read(true).open(dev))))
                .sectors();

            // If size of device has changed and is less, then it is possible
            // that the segments previously allocated for this blockdev no
            // longer exist. If that is the case, RangeAllocator::new() will
            // return an error.
            let allocator = match segment_table.get(bda.dev_uuid()) {
                Some(segments) => try!(RangeAllocator::new(actual_size, segments)),
                None => try!(RangeAllocator::new(actual_size, &vec![])),
            };

            blockdevs.push(BlockDev::new(device, dev.clone(), bda, allocator));
        }
        result.insert(pool_uuid.clone(), BlockDevMgr::new(blockdevs));
    }
    Ok(result)
}
<commit_msg>Verify agreement between discovered blockdevs and pool metadata<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/.

// Code to handle a single block device.

use std::collections::{HashMap, HashSet};
use std::io::ErrorKind;
use std::fs::{OpenOptions, read_dir};
use std::os::linux::fs::MetadataExt;
use std::path::PathBuf;
use std::str::FromStr;

use nix::Errno;
use nix::sys::stat::{S_IFBLK, S_IFMT};
use serde_json;

use devicemapper::Device;

use super::super::errors::{EngineResult, EngineError, ErrorEnum};
use super::super::types::PoolUuid;

use super::blockdev::BlockDev;
use super::blockdevmgr::BlockDevMgr;
use super::device::blkdev_size;
use super::engine::DevOwnership;
use super::metadata::{BDA, StaticHeader};
use super::range_alloc::RangeAllocator;
use super::serde_structs::PoolSave;

/// Find all Stratis devices.
///
/// Returns a map of pool uuids to a vector of devices for each pool.
pub fn find_all() -> EngineResult<HashMap<PoolUuid, Vec<PathBuf>>> {

    let mut pool_map = HashMap::new();
    for dir_e in try!(read_dir("/dev")) {
        let dir_e = try!(dir_e);
        let mode = try!(dir_e.metadata()).st_mode();

        // Device node can't belong to Stratis if it is not a block device
        if mode & S_IFMT.bits() != S_IFBLK.bits() {
            continue;
        }

        let devnode = dir_e.path();

        let f = OpenOptions::new().read(true).open(&devnode);

        // There are some reasons for OpenOptions::open() to return an error
        // which are not reasons for this method to return an error.
        // Try to distinguish. Non-error conditions are:
        //
        // 1. ENXIO: The device does not exist anymore. This means that the device
        // was volatile for some reason; in that case it can not belong to
        // Stratis so it is safe to ignore it.
        //
        // 2. ENOMEDIUM: The device has no medium. An example of this case is an
        // empty optical drive.
        //
        // Note that it is better to be conservative and return with an
        // error in any case where failure to read the device could result
        // in bad data for Stratis. Additional exceptions may be added,
        // but only with a complete justification.
        if f.is_err() {
            let err = f.unwrap_err();
            match err.kind() {
                ErrorKind::NotFound => {
                    continue;
                }
                _ => {
                    if let Some(errno) = err.raw_os_error() {
                        match Errno::from_i32(errno) {
                            Errno::ENXIO | Errno::ENOMEDIUM => continue,
                            _ => return Err(EngineError::Io(err)),
                        };
                    } else {
                        return Err(EngineError::Io(err));
                    }
                }
            }
        }

        let mut f = f.expect("unreachable if f is err");
        if let DevOwnership::Ours(uuid) = try!(StaticHeader::determine_ownership(&mut f)) {
            pool_map
                .entry(uuid)
                .or_insert_with(Vec::new)
                .push(devnode)
        };
    }

    Ok(pool_map)
}

/// Get the most recent metadata from a set of Devices for a given pool UUID.
/// Returns None if no metadata found for this pool.
pub fn get_metadata(pool_uuid: &PoolUuid, devnodes: &[PathBuf]) -> EngineResult<Option<PoolSave>> {

    // No device nodes means no metadata
    if devnodes.is_empty() {
        return Ok(None);
    }

    // Get pairs of device nodes and matching BDAs
    // If no BDA, or BDA UUID does not match pool UUID, skip.
    // If there is an error reading the BDA, error. There could have been
    // vital information on that BDA, for example, it may have contained
    // the newest metadata.
    let mut bdas = Vec::new();
    for devnode in devnodes {
        let bda = try!(BDA::load(&mut try!(OpenOptions::new().read(true).open(devnode))));
        if bda.is_none() {
            continue;
        }
        let bda = bda.expect("unreachable if bda is None");

        if bda.pool_uuid() != pool_uuid {
            continue;
        }
        bdas.push((devnode, bda));
    }

    // We may have had no devices with BDAs for this pool, so return if no BDAs.
    if bdas.is_empty() {
        return Ok(None);
    }

    // Get a most recent BDA
    let &(_, ref most_recent_bda) = bdas.iter()
        .max_by_key(|p| p.1.last_update_time())
        .expect("bdas is not empty, must have a max");

    // Most recent time should never be None if this was a properly
    // created pool; this allows for the method to be called in other
    // circumstances.
    let most_recent_time = most_recent_bda.last_update_time();
    if most_recent_time.is_none() {
        return Ok(None);
    }

    // Try to read from all available devnodes that could contain most
    // recent metadata. In the event of errors, continue to try until all are
    // exhausted.
    for &(devnode, ref bda) in
        bdas.iter()
            .filter(|p| p.1.last_update_time() == most_recent_time) {

        let f = OpenOptions::new().read(true).open(devnode);
        if f.is_err() {
            continue;
        }
        let mut f = f.expect("f is not err");

        if let Ok(Some(data)) = bda.load_state(&mut f) {
            let json: serde_json::Result<PoolSave> = serde_json::from_slice(&data);
            if let Ok(pool) = json {
                return Ok(Some(pool));
            } else {
                continue;
            }
        } else {
            continue;
        }
    }

    // If no data has yet returned, we have an error. That is, we should have
    // some metadata, because we have a most recent time, but we failed to
    // get any.
    let err_str = "timestamp indicates data was written, but no data succesfully read";
    Err(EngineError::Engine(ErrorEnum::NotFound, err_str.into()))
}

/// Get the most recent metadata for each pool.
/// Since the metadata is written immediately after a pool is created, it
/// is considered an error for a pool to be w/out metadata.
pub fn get_pool_metadata(pool_table: &HashMap<PoolUuid, Vec<PathBuf>>)
                         -> EngineResult<HashMap<PoolUuid, PoolSave>> {
    let mut metadata = HashMap::new();
    for (pool_uuid, devices) in pool_table.iter() {
        if let Ok(Some(pool)) = get_metadata(pool_uuid, &devices) {
            metadata.insert(pool_uuid.clone(), pool);
        } else {
            return Err(EngineError::Engine(ErrorEnum::NotFound,
                                           format!("no metadata for pool {}", pool_uuid)));
        }
    }
    Ok(metadata)
}

/// Instantiate each pool's blockdevs.
/// Return a map from the pool uuid to the pools blockdevs.
pub fn get_pool_blockdevs(devnode_table: &HashMap<PoolUuid, Vec<PathBuf>>,
                          metadata_table: &HashMap<PoolUuid, PoolSave>)
                          -> EngineResult<HashMap<PoolUuid, BlockDevMgr>> {
    let mut result = HashMap::new();
    for (pool_uuid, pool_save) in metadata_table {
        let segments = pool_save
            .flex_devs
            .meta_dev
            .iter()
            .chain(pool_save.flex_devs.thin_meta_dev.iter())
            .chain(pool_save.flex_devs.thin_data_dev.iter());

        let mut segment_table = HashMap::new();
        for seg in segments {
            segment_table
                .entry(seg.0.clone())
                .or_insert(vec![])
                .push((seg.1, seg.2))
        }

        let devnodes = devnode_table
            .get(&pool_uuid)
            .expect("devnode_table.keys() == metadata_table.keys()");

        let mut blockdevs = vec![];
        let mut devices = HashSet::new();
        for dev in devnodes {
            let device = try!(Device::from_str(&dev.to_string_lossy()));

            // If we've seen this device already, skip it.
            if devices.contains(&device) {
                continue;
            } else {
                devices.insert(device);
            }

            let bda = try!(BDA::load(&mut try!(OpenOptions::new().read(true).open(dev))));
            let bda = try!(bda.ok_or(EngineError::Engine(ErrorEnum::NotFound,
                                                         "no BDA found for Stratis device"
                                                             .into())));

            let actual_size = try!(blkdev_size(&try!(OpenOptions::new().read(true).open(dev))))
                .sectors();

            // If size of device has changed and is less, then it is possible
            // that the segments previously allocated for this blockdev no
            // longer exist. If that is the case, RangeAllocator::new() will
            // return an error.
            let allocator = match segment_table.get(bda.dev_uuid()) {
                Some(segments) => try!(RangeAllocator::new(actual_size, segments)),
                None => try!(RangeAllocator::new(actual_size, &vec![])),
            };

            blockdevs.push(BlockDev::new(device, dev.clone(), bda, allocator));
        }

        // Verify that blockdevs found match blockdevs recorded.
        let current_uuids: HashSet<_> = blockdevs.iter().map(|b| *b.uuid()).collect();
        let recorded_uuids: HashSet<_> = pool_save.block_devs.keys().map(|u| *u).collect();

        if current_uuids != recorded_uuids {
            let err_msg = "Recorded block dev UUIDs != discovered blockdev UUIDs";
            return Err(EngineError::Engine(ErrorEnum::Invalid, err_msg.into()));
        }

        if blockdevs.len() != current_uuids.len() {
            let err_msg = "Duplicate block devices found in environment";
            return Err(EngineError::Engine(ErrorEnum::Invalid, err_msg.into()));
        }

        result.insert(pool_uuid.clone(), BlockDevMgr::new(blockdevs));
    }
    Ok(result)
}
<|endoftext|>


<commit_before>// https://leetcode.com/problems/find-the-kth-largest-integer-in-the-array/
pub fn kth_largest_number(nums: Vec<String>, k: i32) -> String {
    fn pad(s: &str, length: usize) -> String {
        "0".repeat(length) + &s.to_owned()
    }
    let mut nums = nums.clone();
    nums.sort_by(|a, b| {
        if a.len() > b.len() {
            return pad(&b, a.len() - b.len()).cmp(a);
        }
        if a.len() < b.len() {
            return b.cmp(&pad(a, b.len() - a.len()));
        }
        b.cmp(a)
    });
    nums[k as usize - 1].to_owned()
}

fn main() {
    println!(
        "{}",
        kth_largest_number(
            vec![
                String::from("3"),
                String::from("6"),
                String::from("7"),
                String::from("10")
            ],
            4
        )
    ); // "3"
    println!(
        "{}",
        kth_largest_number(
            vec![
                String::from("2"),
                String::from("21"),
                String::from("12"),
                String::from("1")
            ],
            3
        )
    ); // "2"
    println!(
        "{}",
        kth_largest_number(vec![String::from("0"), String::from("0")], 2)
    ); // "0"
}
<commit_msg>Update problem 1985<commit_after>// https://leetcode.com/problems/find-the-kth-largest-integer-in-the-array/
pub fn kth_largest_number(mut nums: Vec<String>, k: i32) -> String {
    fn pad(s: &str, length: usize) -> String {
        "0".repeat(length) + &s.to_owned()
    }
    nums.sort_by(|a, b| {
        if a.len() > b.len() {
            return pad(&b, a.len() - b.len()).cmp(a);
        }
        if a.len() < b.len() {
            return b.cmp(&pad(a, b.len() - a.len()));
        }
        b.cmp(a)
    });
    nums[k as usize - 1].to_owned()
}

fn main() {
    println!(
        "{}",
        kth_largest_number(
            vec![
                String::from("3"),
                String::from("6"),
                String::from("7"),
                String::from("10")
            ],
            4
        )
    ); // "3"
    println!(
        "{}",
        kth_largest_number(
            vec![
                String::from("2"),
                String::from("21"),
                String::from("12"),
                String::from("1")
            ],
            3
        )
    ); // "2"
    println!(
        "{}",
        kth_largest_number(vec![String::from("0"), String::from("0")], 2)
    ); // "0"
}
<|endoftext|>


<commit_before>extern crate libimagstore;

<commit_msg>Add modules<commit_after>extern crate libimagstore;

pub mod error;
pub mod result;
pub mod viewer;

<|endoftext|>