<commit_before>// TODO
// impl ToGeojson for ....
// generic number instead of f64 for position?
// rename Position to Pos or Positions

extern crate serialize;

use std::collections::TreeMap;
use serialize::json::ToJson;
use serialize::json;

/// Position
///
/// [GeoJSON Format Specification § 2.1.1](http://geojson.org/geojson-spec.html#positions)
pub struct Position(pub Vec<f64>);

impl ToJson for Position {
    fn to_json(&self) -> json::Json {
        let &Position(ref nums) = self;
        nums.to_json()
    }
}

impl Clone for Position {
    fn clone(&self) -> Position {
        let &Position(ref nums) = self;
        Position(nums.clone())
    }
}

/// Point
///
/// [GeoJSON Format Specification § 2.1.2](http://geojson.org/geojson-spec.html#point)
pub struct Point {
    pub coordinates: Position,
}

impl ToJson for Point {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "Point".to_string().to_json());
        d.insert("coordinates".to_string(), self.coordinates.to_json());
        d.to_json()
    }
}

/// MultiPoint
///
/// [GeoJSON Format Specification § 2.1.3](http://geojson.org/geojson-spec.html#multipoint)
pub struct MultiPoint {
    pub points: Vec<Point>,
}

impl ToJson for MultiPoint {
    fn to_json(&self) -> json::Json {
        let coordinates: Vec<Position> =
            self.points.iter().map(|p| p.coordinates.clone()).collect();
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "MultiPoint".to_string().to_json());
        d.insert("coordinates".to_string(), coordinates.to_json());
        d.to_json()
    }
}

/// LineString
///
/// [GeoJSON Format Specification § 2.1.4](http://geojson.org/geojson-spec.html#linestring)
pub struct LineString {
    pub coordinates: Vec<Position>,
}

impl ToJson for LineString {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "LineString".to_string().to_json());
        d.insert("coordinates".to_string(), self.coordinates.to_json());
        d.to_json()
    }
}

/// MultiLineString
///
/// [GeoJSON Format Specification § 2.1.5](http://geojson.org/geojson-spec.html#multilinestring)
pub struct MultiLineString {
    pub line_strings: Vec<LineString>,
}

impl ToJson for MultiLineString {
    fn to_json(&self) -> json::Json {
        let coordinates: Vec<Vec<Position>> =
            self.line_strings.iter().map(|l| l.coordinates.clone()).collect();
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "MultiLineString".to_string().to_json());
        d.insert("coordinates".to_string(), coordinates.to_json());
        d.to_json()
    }
}

/// Polygon
///
/// [GeoJSON Format Specification § 2.1.6](http://geojson.org/geojson-spec.html#polygon)
pub struct Polygon {
    pub exterior: Vec<Position>,
    pub holes: Vec<Vec<Position>>,
}

impl Polygon {
    fn coordinates(&self) -> Vec<Vec<Position>> {
        let mut coordinates = self.holes.clone();
        coordinates.insert(0, self.exterior.clone());
        coordinates
    }
}

impl ToJson for Polygon {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "Polygon".to_string().to_json());
        d.insert("coordinates".to_string(), self.coordinates().to_json());
        d.to_json()
    }
}

/// MultiPolygon
///
/// [GeoJSON Format Specification § 2.1.7](http://geojson.org/geojson-spec.html#multipolygon)
pub struct MultiPolygon {
    pub polygons: Vec<Polygon>,
}

impl ToJson for MultiPolygon {
    fn to_json(&self) -> json::Json {
        let coordinates: Vec<Vec<Vec<Position>>> =
            self.polygons.iter().map(|p| p.coordinates()).collect();
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "MultiPolygon".to_string().to_json());
        d.insert("coordinates".to_string(), coordinates.to_json());
        d.to_json()
    }
}

/// Geometry
pub enum Geometry {
    Point(Point),
    MultiPoint(MultiPoint),
    LineString(LineString),
    MultiLineString(MultiLineString),
    Polygon(Polygon),
    MultiPolygon(MultiPolygon),
}

impl ToJson for Geometry {
    fn to_json(&self) -> json::Json {
        match *self {
            // TODO: is there a better way of doing this?
            Point(ref geom) => geom.to_json(),
            MultiPoint(ref geom) => geom.to_json(),
            LineString(ref geom) => geom.to_json(),
            MultiLineString(ref geom) => geom.to_json(),
            Polygon(ref geom) => geom.to_json(),
            MultiPolygon(ref geom) => geom.to_json(),
            // TODO: GeometryCollection(ref geom) => geom.to_json(),
        }
    }
}

/// GeometryCollection
///
/// [GeoJSON Format Specification § 2.1.8](http://geojson.org/geojson-spec.html#geometry-collection)
pub struct GeometryCollection {
    geometries: Vec<Geometry>,
}

impl ToJson for GeometryCollection {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "GeometryCollection".to_string().to_json());
        d.insert("geometries".to_string(), self.geometries.to_json());
        d.to_json()
    }
}

/// Feature
///
/// [GeoJSON Format Specification § 2.2](http://geojson.org/geojson-spec.html#feature-objects)
pub struct Feature {
    geometry: Geometry,
    properties: json::Json,
}

impl ToJson for Feature {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "Feature".to_string().to_json());
        d.insert("geometry".to_string(), self.geometry.to_json());
        d.insert("properties".to_string(), self.properties.to_json());
        d.to_json()
    }
}

/// FeatureCollection
///
/// [GeoJSON Format Specification § 2.3](http://geojson.org/geojson-spec.html#feature-collection-objects)
pub struct FeatureCollection {
    features: Vec<Feature>,
}

impl ToJson for FeatureCollection {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("features".to_string(), self.features.to_json());
        d.to_json()
    }
}

fn main() {
    let point = Point {
        coordinates: Position(vec![1., 2., 3.]),
    };

    let j: json::Json = point.to_json();
    let s: String = j.to_pretty_str();

    println!("{}", s);
}
<commit_msg>Add TODO<commit_after>// TODO
// impl ToGeojson for ....
// generic number instead of f64 for position?
// rename Position to Pos or Positions

extern crate serialize;

use std::collections::TreeMap;
use serialize::json::ToJson;
use serialize::json;

/// Position
///
/// [GeoJSON Format Specification § 2.1.1](http://geojson.org/geojson-spec.html#positions)
pub struct Position(pub Vec<f64>);

impl ToJson for Position {
    fn to_json(&self) -> json::Json {
        let &Position(ref nums) = self;
        nums.to_json()
    }
}

impl Clone for Position {
    fn clone(&self) -> Position {
        let &Position(ref nums) = self;
        Position(nums.clone())
    }
}

/// Point
///
/// [GeoJSON Format Specification § 2.1.2](http://geojson.org/geojson-spec.html#point)
pub struct Point {
    pub coordinates: Position,
}

impl ToJson for Point {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "Point".to_string().to_json());
        d.insert("coordinates".to_string(), self.coordinates.to_json());
        d.to_json()
    }
}

/// MultiPoint
///
/// [GeoJSON Format Specification § 2.1.3](http://geojson.org/geojson-spec.html#multipoint)
pub struct MultiPoint {
    pub points: Vec<Point>,
}

impl ToJson for MultiPoint {
    fn to_json(&self) -> json::Json {
        let coordinates: Vec<Position> =
            self.points.iter().map(|p| p.coordinates.clone()).collect();
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "MultiPoint".to_string().to_json());
        d.insert("coordinates".to_string(), coordinates.to_json());
        d.to_json()
    }
}

/// LineString
///
/// [GeoJSON Format Specification § 2.1.4](http://geojson.org/geojson-spec.html#linestring)
pub struct LineString {
    pub coordinates: Vec<Position>,
}

impl ToJson for LineString {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "LineString".to_string().to_json());
        d.insert("coordinates".to_string(), self.coordinates.to_json());
        d.to_json()
    }
}

/// MultiLineString
///
/// [GeoJSON Format Specification § 2.1.5](http://geojson.org/geojson-spec.html#multilinestring)
pub struct MultiLineString {
    pub line_strings: Vec<LineString>,
}

impl ToJson for MultiLineString {
    fn to_json(&self) -> json::Json {
        let coordinates: Vec<Vec<Position>> =
            self.line_strings.iter().map(|l| l.coordinates.clone()).collect();
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "MultiLineString".to_string().to_json());
        d.insert("coordinates".to_string(), coordinates.to_json());
        d.to_json()
    }
}

/// Polygon
///
/// [GeoJSON Format Specification § 2.1.6](http://geojson.org/geojson-spec.html#polygon)
pub struct Polygon {
    pub exterior: Vec<Position>,
    pub holes: Vec<Vec<Position>>,  // TODO: this should be optional
}

impl Polygon {
    fn coordinates(&self) -> Vec<Vec<Position>> {
        let mut coordinates = self.holes.clone();
        coordinates.insert(0, self.exterior.clone());
        coordinates
    }
}

impl ToJson for Polygon {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "Polygon".to_string().to_json());
        d.insert("coordinates".to_string(), self.coordinates().to_json());
        d.to_json()
    }
}

/// MultiPolygon
///
/// [GeoJSON Format Specification § 2.1.7](http://geojson.org/geojson-spec.html#multipolygon)
pub struct MultiPolygon {
    pub polygons: Vec<Polygon>,
}

impl ToJson for MultiPolygon {
    fn to_json(&self) -> json::Json {
        let coordinates: Vec<Vec<Vec<Position>>> =
            self.polygons.iter().map(|p| p.coordinates()).collect();
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "MultiPolygon".to_string().to_json());
        d.insert("coordinates".to_string(), coordinates.to_json());
        d.to_json()
    }
}

/// Geometry
pub enum Geometry {
    Point(Point),
    MultiPoint(MultiPoint),
    LineString(LineString),
    MultiLineString(MultiLineString),
    Polygon(Polygon),
    MultiPolygon(MultiPolygon),
}

impl ToJson for Geometry {
    fn to_json(&self) -> json::Json {
        match *self {
            // TODO: is there a better way of doing this?
            Point(ref geom) => geom.to_json(),
            MultiPoint(ref geom) => geom.to_json(),
            LineString(ref geom) => geom.to_json(),
            MultiLineString(ref geom) => geom.to_json(),
            Polygon(ref geom) => geom.to_json(),
            MultiPolygon(ref geom) => geom.to_json(),
            // TODO: GeometryCollection(ref geom) => geom.to_json(),
        }
    }
}

/// GeometryCollection
///
/// [GeoJSON Format Specification § 2.1.8](http://geojson.org/geojson-spec.html#geometry-collection)
pub struct GeometryCollection {
    geometries: Vec<Geometry>,
}

impl ToJson for GeometryCollection {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "GeometryCollection".to_string().to_json());
        d.insert("geometries".to_string(), self.geometries.to_json());
        d.to_json()
    }
}

/// Feature
///
/// [GeoJSON Format Specification § 2.2](http://geojson.org/geojson-spec.html#feature-objects)
pub struct Feature {
    geometry: Geometry,
    properties: json::Json,
}

impl ToJson for Feature {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "Feature".to_string().to_json());
        d.insert("geometry".to_string(), self.geometry.to_json());
        d.insert("properties".to_string(), self.properties.to_json());
        d.to_json()
    }
}

/// FeatureCollection
///
/// [GeoJSON Format Specification § 2.3](http://geojson.org/geojson-spec.html#feature-collection-objects)
pub struct FeatureCollection {
    features: Vec<Feature>,
}

impl ToJson for FeatureCollection {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("features".to_string(), self.features.to_json());
        d.to_json()
    }
}

fn main() {
    let point = Point {
        coordinates: Position(vec![1., 2., 3.]),
    };

    let j: json::Json = point.to_json();
    let s: String = j.to_pretty_str();

    println!("{}", s);
}
<|endoftext|>


<commit_before>extern crate backtrace;
#[macro_use(crate_version, crate_authors)]
extern crate clap;

extern crate api_server;
extern crate data_model;
#[macro_use]
extern crate logger;
extern crate seccomp;
extern crate vmm;

use backtrace::Backtrace;
use clap::{App, Arg};
use std::panic;
use std::path::PathBuf;
use std::sync::mpsc::channel;
use std::sync::{Arc, RwLock};

use api_server::request::instance_info::{InstanceInfo, InstanceState};
use api_server::ApiServer;
use data_model::mmds::MMDS;
use logger::{Metric, LOGGER, METRICS};

const DEFAULT_API_SOCK_PATH: &str = "/tmp/firecracker.socket";

fn main() {
    // If the signal handler can't be set, it's OK to panic.
    seccomp::setup_sigsys_handler().expect("Failed to register signal handler");
    // Start firecracker by setting up a panic hook, which will be called before
    // terminating as we're building with panic = "abort".
    // It's worth noting that the abort is caused by sending a SIG_ABORT signal to the process.
    panic::set_hook(Box::new(move |info| {
        // We're currently using the closure parameter, which is a &PanicInfo, for printing the
        // origin of the panic, including the payload passed to panic! and the source code location
        // from which the panic originated.
        error!("Panic occurred: {:?}", info);
        METRICS.vmm.panic_count.inc();

        let bt = Backtrace::new();
        error!("{:?}", bt);

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

    let cmd_arguments = App::new("firecracker")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Launch a microvm.")
        .arg(
            Arg::with_name("api_sock")
                .long("api-sock")
                .help("Path to unix domain socket used by the API")
                .default_value(DEFAULT_API_SOCK_PATH)
                .takes_value(true),
        ).arg(
            Arg::with_name("jailed")
                .long("jailed")
                .help("Let Firecracker know it's running inside a jail."),
        ).arg(
            Arg::with_name("seccomp-level")
                .long("seccomp-level")
                .help(
                    "Level of seccomp filtering.\n
    - Level 0: No filtering.\n
    - Level 1: Seccomp filtering by syscall number.\n
    - Level 2: Seccomp filtering by syscall number and argument values.\n
",
                ).takes_value(true)
                .default_value("0")
                .possible_values(&["0", "1", "2"]),
        ).get_matches();

    let bind_path = cmd_arguments
        .value_of("api_sock")
        .map(|s| PathBuf::from(s))
        .unwrap();

    if cmd_arguments.is_present("jailed") {
        data_model::FIRECRACKER_IS_JAILED.store(true, std::sync::atomic::Ordering::Relaxed);
    }

    // The value of the argument can be safely unwrapped, because a default value was specified.
    // The value of the argument can be parsed into an unsigned integer since its possible values
    // were specified and they are all unsigned integers, therefore the result of the parsing can be
    // safely unwrapped.
    let seccomp_level = cmd_arguments
        .value_of("seccomp-level")
        .unwrap()
        .parse::<u32>()
        .unwrap();

    let shared_info = Arc::new(RwLock::new(InstanceInfo {
        state: InstanceState::Uninitialized,
    }));
    let mmds_info = MMDS.clone();
    let (to_vmm, from_api) = channel();
    let server = ApiServer::new(mmds_info, shared_info.clone(), to_vmm).unwrap();

    let api_event_fd = server
        .get_event_fd_clone()
        .expect("Cannot clone API eventFD.");
    let _vmm_thread_handle =
        vmm::start_vmm_thread(shared_info, api_event_fd, from_api, seccomp_level);

    server.bind_and_run(bind_path).unwrap();
}

#[cfg(test)]
mod tests {
    extern crate tempfile;

    use self::tempfile::NamedTempFile;
    use super::*;

    use std::fs::File;
    use std::io::BufRead;
    use std::io::BufReader;
    use std::path::Path;
    use std::time::Duration;
    use std::{fs, thread};

    struct EnterDrop {
        log_file: String,
    }

    impl Drop for EnterDrop {
        fn drop(&mut self) {
            validate_backtrace(
                &self.log_file,
                &[
                    // This is the assertion string. Making sure that stack backtrace is outputted
                    // upon panic.
                    ("[ERROR", "main.rs", "Panic occurred"),
                    ("[ERROR", "main.rs", "stack backtrace:"),
                    ("0:", "0x", "backtrace::"),
                ],
            );
            fs::remove_file(DEFAULT_API_SOCK_PATH).expect("Failure in removing socket file.");
        }
    }

    fn validate_backtrace(log_path: &str, expected: &[(&'static str, &'static str, &'static str)]) {
        let f = File::open(log_path).unwrap();
        let mut reader = BufReader::new(f);

        let mut line = String::new();
        for tuple in expected {
            line.clear();
            reader.read_line(&mut line).unwrap();
            assert!(line.contains(&tuple.0));
            assert!(line.contains(&tuple.1));
            assert!(line.contains(&tuple.2));
        }
    }

    #[test]
    #[should_panic(expected = "Test that panic outputs backtrace")]
    fn test_main() {
        // There is no reason to run this test if the default socket path exists.
        assert!(!Path::new(DEFAULT_API_SOCK_PATH).exists());

        let log_file_temp =
            NamedTempFile::new().expect("Failed to create temporary output logging file.");
        let metrics_file_temp =
            NamedTempFile::new().expect("Failed to create temporary metrics logging file.");
        let log_file = String::from(log_file_temp.path().to_path_buf().to_str().unwrap());

        thread::spawn(|| {
            main();
        });

        const MAX_WAIT_ITERS: u32 = 20;
        let mut iter_count = 0;
        loop {
            thread::sleep(Duration::from_secs(1));
            if Path::new(DEFAULT_API_SOCK_PATH).exists() {
                LOGGER
                    .init(
                        Some(log_file_temp.path().to_str().unwrap().to_string()),
                        Some(metrics_file_temp.path().to_str().unwrap().to_string()),
                    ).expect("Could not initialize logger.");
                // EnterDrop is used here only to be able to check the content of the log file
                // after panicking.
                let _my_setup = EnterDrop { log_file };

                // This string argument has to match the one from the should_panic above.
                panic!("Test that panic outputs backtrace");
            }
            iter_count += 1;
            if iter_count > MAX_WAIT_ITERS {
                fs::remove_file(DEFAULT_API_SOCK_PATH).expect("failure in removing socket file");
                assert!(false);
            }
        }
    }
}
<commit_msg>src/main.rs::test_main clean up + double panic fix<commit_after>extern crate backtrace;
#[macro_use(crate_version, crate_authors)]
extern crate clap;

extern crate api_server;
extern crate data_model;
#[macro_use]
extern crate logger;
extern crate seccomp;
extern crate vmm;

use backtrace::Backtrace;
use clap::{App, Arg};
use std::panic;
use std::path::PathBuf;
use std::sync::mpsc::channel;
use std::sync::{Arc, RwLock};

use api_server::request::instance_info::{InstanceInfo, InstanceState};
use api_server::ApiServer;
use data_model::mmds::MMDS;
use logger::{Metric, LOGGER, METRICS};

const DEFAULT_API_SOCK_PATH: &str = "/tmp/firecracker.socket";

fn main() {
    // If the signal handler can't be set, it's OK to panic.
    seccomp::setup_sigsys_handler().expect("Failed to register signal handler");
    // Start firecracker by setting up a panic hook, which will be called before
    // terminating as we're building with panic = "abort".
    // It's worth noting that the abort is caused by sending a SIG_ABORT signal to the process.
    panic::set_hook(Box::new(move |info| {
        // We're currently using the closure parameter, which is a &PanicInfo, for printing the
        // origin of the panic, including the payload passed to panic! and the source code location
        // from which the panic originated.
        error!("Panic occurred: {:?}", info);
        METRICS.vmm.panic_count.inc();

        let bt = Backtrace::new();
        error!("{:?}", bt);

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

    let cmd_arguments = App::new("firecracker")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Launch a microvm.")
        .arg(
            Arg::with_name("api_sock")
                .long("api-sock")
                .help("Path to unix domain socket used by the API")
                .default_value(DEFAULT_API_SOCK_PATH)
                .takes_value(true),
        ).arg(
            Arg::with_name("jailed")
                .long("jailed")
                .help("Let Firecracker know it's running inside a jail."),
        ).arg(
            Arg::with_name("seccomp-level")
                .long("seccomp-level")
                .help(
                    "Level of seccomp filtering.\n
    - Level 0: No filtering.\n
    - Level 1: Seccomp filtering by syscall number.\n
    - Level 2: Seccomp filtering by syscall number and argument values.\n
",
                ).takes_value(true)
                .default_value("0")
                .possible_values(&["0", "1", "2"]),
        ).get_matches();

    let bind_path = cmd_arguments
        .value_of("api_sock")
        .map(|s| PathBuf::from(s))
        .unwrap();

    if cmd_arguments.is_present("jailed") {
        data_model::FIRECRACKER_IS_JAILED.store(true, std::sync::atomic::Ordering::Relaxed);
    }

    // The value of the argument can be safely unwrapped, because a default value was specified.
    // The value of the argument can be parsed into an unsigned integer since its possible values
    // were specified and they are all unsigned integers, therefore the result of the parsing can be
    // safely unwrapped.
    let seccomp_level = cmd_arguments
        .value_of("seccomp-level")
        .unwrap()
        .parse::<u32>()
        .unwrap();

    let shared_info = Arc::new(RwLock::new(InstanceInfo {
        state: InstanceState::Uninitialized,
    }));
    let mmds_info = MMDS.clone();
    let (to_vmm, from_api) = channel();
    let server = ApiServer::new(mmds_info, shared_info.clone(), to_vmm).unwrap();

    let api_event_fd = server
        .get_event_fd_clone()
        .expect("Cannot clone API eventFD.");
    let _vmm_thread_handle =
        vmm::start_vmm_thread(shared_info, api_event_fd, from_api, seccomp_level);

    server.bind_and_run(bind_path).unwrap();
}

#[cfg(test)]
mod tests {
    extern crate tempfile;

    use self::tempfile::NamedTempFile;
    use super::*;

    use std::fs::File;
    use std::io::BufRead;
    use std::io::BufReader;
    use std::path::Path;
    use std::time::Duration;
    use std::{fs, thread};

    /// Look through the log for lines that match expectations.
    /// expectations is a list of tuples of words we're looking for.
    /// A tuple matches a line if all the words in the tuple can be found on that line.
    /// For this test to pass, every tuple must match at least one line.
    fn validate_backtrace(
        log_path: &str,
        expectations: &[(&'static str, &'static str, &'static str)],
    ) {
        let f = File::open(log_path).unwrap();
        let reader = BufReader::new(f);
        let mut pass = false;
        let mut expectation_iter = expectations.iter();
        let mut expected_words = expectation_iter.next().unwrap();

        for ln_res in reader.lines() {
            let line = ln_res.unwrap();
            if !(line.contains(expected_words.0)
                && line.contains(expected_words.1)
                && line.contains(expected_words.2))
            {
                continue;
            }
            if let Some(w) = expectation_iter.next() {
                expected_words = w;
                continue;
            }
            pass = true;
            break;
        }
        assert!(pass);
    }

    #[test]
    fn test_main() {
        const FIRECRACKER_INIT_TIMEOUT_MILLIS: u64 = 100;

        // There is no reason to run this test if the default socket path exists.
        assert!(!Path::new(DEFAULT_API_SOCK_PATH).exists());

        let log_file_temp =
            NamedTempFile::new().expect("Failed to create temporary output logging file.");
        let metrics_file_temp =
            NamedTempFile::new().expect("Failed to create temporary metrics logging file.");
        let log_file = String::from(log_file_temp.path().to_path_buf().to_str().unwrap());

        // Start Firecracker in a separate thread
        thread::spawn(|| {
            main();
        });

        // Wait around for a bit, so Firecracker has time to initialize and create the
        // API socket.
        thread::sleep(Duration::from_millis(FIRECRACKER_INIT_TIMEOUT_MILLIS));

        // If Firecracker hasn't finished initializing yet, something is really wrong!
        assert!(Path::new(DEFAULT_API_SOCK_PATH).exists());

        // Initialize the logger
        LOGGER
            .init(
                Some(log_file_temp.path().to_str().unwrap().to_string()),
                Some(metrics_file_temp.path().to_str().unwrap().to_string()),
            ).expect("Could not initialize logger.");

        // Cause some controlled panic and see if a backtrace shows up in the log,
        // as it's supposed to.
        let _ = panic::catch_unwind(|| {
            panic!("Oh, noes!");
        });

        // Look for the expected backtrace inside the log
        validate_backtrace(
            log_file.as_str(),
            &[
                // Lines containing these words should have appeared in the log, in this order
                ("ERROR", "main.rs", "Panic occurred"),
                ("ERROR", "main.rs", "stack backtrace:"),
                ("0:", "0x", "backtrace::"),
            ],
        );

        // Clean up
        fs::remove_file(DEFAULT_API_SOCK_PATH).expect("failure in removing socket file");
    }
}
<|endoftext|>


<commit_before>extern crate clap;

use clap::{Arg, App};
use std::cmp::Ordering::Equal;
use std::error::Error;
use std::f32;
use std::i32;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::path::Path;
use std::str::FromStr;

fn print_results(mut timings: Vec<f32>, percentiles: Vec<i32>) {
    timings.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Equal));

    let start: usize = 0;
    let end: usize = timings.len() - 1;
    if end == 0 {
        return;
    };
    let median: usize = end / 2;

    println!("");
    println!("Results:");
    println!("    Total count:   {}", timings.len());
    println!("    Min:           {}", timings[start]);
    println!("    Median:        {} ({})", timings[median], median + 1);

    for perc in percentiles {
        let pvalue: usize = end.checked_mul(perc as usize).unwrap().checked_div(100).unwrap();
        println!("    {} percentile: {} ({})", perc, timings[pvalue], pvalue + 1);
    }

    println!("    Max:           {} ({})", timings[end], end + 1);
}

// This is the main function
fn main() {

    let matches = App::new("Calculate response time percentiles on logs")
        .version("0.0.1")
        .author("Vladimir Prokhoda <vladimirbright@gmail.com>")
        .arg(Arg::with_name("separator")
            .short("s")
            .long("separator")
            .default_value(" ")
            .help("Column separator")
            .takes_value(true))
        .arg(Arg::with_name("column")
            .short("c")
            .long("column")
            .default_value("8")
            .use_delimiter(false)
            .help("Column number to use")
            .takes_value(true))
        .arg(Arg::with_name("input")
            .help("Sets the input file to use")
            .required(true)
            .index(1))
        .arg(Arg::with_name("percentiles")
            .short("r")
            .long("percentiles")
            .default_value("70,80,95,99")
            .help("Percentiles to compute")
            .takes_value(true))
        .arg(Arg::with_name("print")
            .short("p")
            .long("print")
            .help("Print matched rows"))
        .get_matches();

    let sep = match matches.value_of("separator") {
        Some(f) => f,
        None => panic!("provide --separator option"),
    };
    let separator = match sep {
        "\\t" => "\t",
        _ => sep,
    };
    let column = match matches.value_of("column") {
        Some(f) => f,
        None => panic!("provide --column option"),
    };
    let timing_index: usize = match usize::from_str(column) {
        Ok(f) => f,
        Err(_) => panic!("--column must be integer"),
    };

    let mut percentiles: Vec<i32> = vec![];
    for part in matches.value_of("percentiles").unwrap().split(",") {
        match i32::from_str(&part) {
            Err(e) => panic!("invalid percentile: {}: {}", e, &part),
            Ok(p) => percentiles.push(p),
        }
    }

	let file_path = matches.value_of("input").unwrap();
    let path = Path::new(&file_path);
    let display = path.display();

    let f = match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", display, why.description()),
        Ok(f) => f,
    };
	let file = BufReader::with_capacity(128 * 1024, &f);

    let mut timings: Vec<f32> = vec![];
    let mut has_errors: bool = false;

	for line in file.lines().filter_map(|result| result.ok()) {
		let v: Vec<&str> = line.split(separator).collect();
        let t = v[timing_index];

        if t == "-" {
            continue;
        }
		if matches.is_present("print") {
			println!("{}", line);
		}
        match f32::from_str(&t) {
            Err(_) => {
                has_errors = true;
                continue;
            },
            Ok(f) => timings.push(f),
        };
    }

    if timings.len() == 0 && has_errors {
        panic!("column {} does not contain valid numbers", timing_index)
    }

    print_results(timings, percentiles);
}
<commit_msg>Tabs for spaces.<commit_after>extern crate clap;

use clap::{Arg, App};
use std::cmp::Ordering::Equal;
use std::error::Error;
use std::f32;
use std::i32;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::path::Path;
use std::str::FromStr;

fn print_results(mut timings: Vec<f32>, percentiles: Vec<i32>) {
    timings.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Equal));

    let start: usize = 0;
    let end: usize = timings.len() - 1;
    if end == 0 {
        return;
    };
    let median: usize = end / 2;

    println!("");
    println!("Results:");
    println!("    Total count:   {}", timings.len());
    println!("    Min:           {}", timings[start]);
    println!("    Median:        {} ({})", timings[median], median + 1);

    for perc in percentiles {
        let pvalue: usize = end.checked_mul(perc as usize).unwrap().checked_div(100).unwrap();
        println!("    {} percentile: {} ({})", perc, timings[pvalue], pvalue + 1);
    }

    println!("    Max:           {} ({})", timings[end], end + 1);
}

// This is the main function
fn main() {

    let matches = App::new("Calculate response time percentiles on logs")
        .version("0.0.1")
        .arg(Arg::with_name("separator")
            .short("s")
            .long("separator")
            .default_value(" ")
            .help("Column separator")
            .takes_value(true))
        .arg(Arg::with_name("column")
            .short("c")
            .long("column")
            .default_value("8")
            .use_delimiter(false)
            .help("Column number to use")
            .takes_value(true))
        .arg(Arg::with_name("input")
            .help("Sets the input file to use")
            .required(true)
            .index(1))
        .arg(Arg::with_name("percentiles")
            .short("r")
            .long("percentiles")
            .default_value("70,80,95,99")
            .help("Percentiles to compute")
            .takes_value(true))
        .arg(Arg::with_name("print")
            .short("p")
            .long("print")
            .help("Print matched rows"))
        .get_matches();

    let sep = match matches.value_of("separator") {
        Some(f) => f,
        None => panic!("provide --separator option"),
    };
    let separator = match sep {
        "\\t" => "\t",
        _ => sep,
    };
    let column = match matches.value_of("column") {
        Some(f) => f,
        None => panic!("provide --column option"),
    };
    let timing_index: usize = match usize::from_str(column) {
        Ok(f) => f,
        Err(_) => panic!("--column must be integer"),
    };

    let mut percentiles: Vec<i32> = vec![];
    for part in matches.value_of("percentiles").unwrap().split(",") {
        match i32::from_str(&part) {
            Err(e) => panic!("invalid percentile: {}: {}", e, &part),
            Ok(p) => percentiles.push(p),
        }
    }

    let file_path = matches.value_of("input").unwrap();
    let path = Path::new(&file_path);
    let display = path.display();

    let f = match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", display, why.description()),
        Ok(f) => f,
    };
    let file = BufReader::with_capacity(128 * 1024, &f);

    let mut timings: Vec<f32> = vec![];
    let mut has_errors: bool = false;

    for line in file.lines().filter_map(|result| result.ok()) {
        let v: Vec<&str> = line.split(separator).collect();
        let t = v[timing_index];

        if t == "-" {
            continue;
        }
        if matches.is_present("print") {
            println!("{}", line);
        }
        match f32::from_str(&t) {
            Err(_) => {
                has_errors = true;
                continue;
            },
            Ok(f) => timings.push(f),
        };
    }

    if timings.len() == 0 && has_errors {
        panic!("column {} does not contain valid numbers", timing_index)
    }

    print_results(timings, percentiles);
}
<|endoftext|>


<commit_before>extern crate rustless;
extern crate hyper;
extern crate iron;
extern crate valico;
extern crate crypto;

#[macro_use(bson, doc)]
extern crate bson;
extern crate mongodb;

use mongodb::{Client, ThreadedClient};
use mongodb::db::ThreadedDatabase;
use bson::
{
    Bson,
    Document
};

use std::fs::File;
use std::io::BufReader;
use std::io::Read;
use std::path::Path;
use std::process::Command;
use std::thread;
use std::time::
{
    Duration,
    SystemTime,
    UNIX_EPOCH
};
use crypto::digest::Digest;
use crypto::sha3::Sha3;
use valico::json_dsl;
use rustless::server::status::StatusCode;
use rustless::json::ToJson;
use rustless::
{
    Application,
    Api,
    Nesting,
    Versioning
};
use hyper::header::
{
    ContentDisposition,
    DispositionType,
    DispositionParam,
    Charset
};

fn is_atom(repository:&str, category:&str, package:&str, version:&str) -> bool
{
    let output = Command::new("equery").arg("w")
        .arg(format!("{}/{}-{}::{}", category, package, version, repository)).output().unwrap();
    let ex = format!("{}/{}/{}/{}-{}.ebuild\n", repository, category, package, package, version);
    return output.status.success() && output.stdout.ends_with(ex.as_bytes());
}

#[test]
fn test_is_atom()
{
    assert_eq!(false, is_atom("test","test","test","test"));
    assert_eq!(true, is_atom("gentoo","app-editors","vim","8.0.1298"));
}

fn main()
{
    let client = Client::connect("localhost", 27017)
        .expect("Failed to initialize standalone client.");
    let coll = client.db("gbs").collection("builds");

    let api = Api::build(|api|
    {
        api.prefix("api");
        api.version("1", Versioning::Path);

        api.namespace("atoms/:repositories/:categories/:packages/:versions", |atoms_ns|
        {
            atoms_ns.params(|params|
            {
                params.req_typed("repositories", json_dsl::string());
                params.req_typed("categories", json_dsl::string());
                params.req_typed("packages", json_dsl::string());
                params.req_typed("versions", json_dsl::string());
            });
            atoms_ns.get("builds", |endpoint|
            {
                endpoint.handle(|client, params|
                {
                    return client.json(&params.to_json());
                })
            });
            atoms_ns.get("builds/:id/status", |endpoint|
            {
                endpoint.handle(|mut client, params|
                {
                    let repository = params.find("repositories").unwrap().to_string().trim_matches('"').to_string();
                    let category = params.find("categories").unwrap().to_string().trim_matches('"').to_string();
                    let package = params.find("packages").unwrap().to_string().trim_matches('"').to_string();
                    let version = params.find("versions").unwrap().to_string().trim_matches('"').to_string();
                    // TODO check [0-0a-f]
                    let id = params.find("id").unwrap().to_string().trim_matches('"').to_string();

                    if is_atom(&repository, &category, &package, &version)
                    {
                        let s = format!("{}/{}/{}/{}/{}/status", repository, category, package, version, id);
                        let path = Path::new(&s);
                        if path.is_file()
                        {
                            let f = File::open(&s).unwrap();
                            let mut reader = BufReader::new(f);
                            let mut buf = String::new();
                            reader.read_to_string(&mut buf);
                            return client.text(buf);
                        }
                    }
                    client.set_status(StatusCode::NotFound);
                    return client.empty();
                })
            });
            atoms_ns.get("builds/:id", |endpoint|
            {
                endpoint.params(|params|
                {
                    params.req_typed("id", json_dsl::string());
                });
                endpoint.handle(|mut client, params|
                {
                    let repository = params.find("repositories").unwrap().to_string().trim_matches('"').to_string();
                    let category = params.find("categories").unwrap().to_string().trim_matches('"').to_string();
                    let package = params.find("packages").unwrap().to_string().trim_matches('"').to_string();
                    let version = params.find("versions").unwrap().to_string().trim_matches('"').to_string();
                    // TODO check [0-0a-f]
                    let id = params.find("id").unwrap().to_string().trim_matches('"').to_string();

                    if is_atom(&repository, &category, &package, &version)
                    {
                        let s = format!("{}/{}/{}/{}/{}/{2}-{3}.tbz2", repository, category, package, version, id);
                        let path = Path::new(&s);
                        if path.is_file()
                        {
                            client.set_header(ContentDisposition
                            {
                              disposition: DispositionType::Attachment,
                              parameters: vec![DispositionParam::Filename(
                                Charset::Us_Ascii,
                                None,
                                format!("{}-{}.tbz2", package, version).into_bytes()
                            )]});
                            return client.file(path);
                        }
                    }
                    client.set_status(StatusCode::NotFound);
                    return client.empty();
                })
            });
            atoms_ns.post("builds", |endpoint|
            {
                endpoint.handle(move |mut client, params|
                {
                    let repository = params.find("repositories").unwrap().to_string().trim_matches('"').to_string();
                    let category = params.find("categories").unwrap().to_string().trim_matches('"').to_string();
                    let package = params.find("packages").unwrap().to_string().trim_matches('"').to_string();
                    let version = params.find("versions").unwrap().to_string().trim_matches('"').to_string();

                    let mut uses = String::new();
                    let use_flag = params.find("use").unwrap();
                    for (key, value) in use_flag.as_object().unwrap().iter()
                    {
                        uses.push_str(&format!("{}{} ", match value.as_bool().unwrap(){true => "", false => "-"}, key));
                    }
                    println!("{}", params);
                    println!("{}", uses);

                    let mut hasher = Sha3::sha3_256();
                    hasher.input_str(&format!("{}{}{}{}{}", repository, category, package, version, uses));
                    let id = hasher.result_str();

                    let url = format!("{}/{}/{}/{}/builds/{}", repository, category, package, version, id);

                    if is_atom(&repository, &category, &package, &version)
                    {
                        if !Path::new(&format!("{}/{}/{}/{}/{}/{2}-{3}.tbz2", repository, category, package, version, id)).exists()
                        {
                            {
                                let mut uses = Document::new();
                                for (key, value) in use_flag.as_object().unwrap().iter()
                                {
                                    uses.insert(key.clone(), Bson::Boolean(value.as_bool().unwrap().clone()));
                                }
                                let s = SystemTime::now();
                                let doc = doc!
                                {
                                    "date" => &format!("{}", s.duration_since(UNIX_EPOCH).unwrap().as_secs()),
                                    "id" => id.clone(),
                                    "repository" => repository.clone(),
                                    "category" => category.clone(),
                                    "package" => package.clone(),
                                    "version" => version.clone(),
                                    "use" => uses
                                };
                                coll.insert_one(doc.clone(), None).ok().expect("Failed to insert document.");
                            }
                            thread::spawn(||
                            {
                                let b = Command::new("./buildreq.sh")
                                .arg(repository)
                                .arg(category)
                                .arg(package)
                                .arg(version)
                                .arg(id)
                                .arg(uses)
                                .output().unwrap();
                                println!("{}\n", String::from_utf8_lossy(&b.stdout));
                                println!("{}\n", String::from_utf8_lossy(&b.stderr));
                            });
                        }
                        return client.text(url);
                    }
                    else
                    {
                        client.set_status(StatusCode::NotFound);
                        return client.empty();
                    }
                })
            });
        });
    });

    let app = Application::new(api);

    iron::Iron::new(app).http("0.0.0.0:4000").unwrap();
}
<commit_msg>add log api<commit_after>extern crate rustless;
extern crate hyper;
extern crate iron;
extern crate valico;
extern crate crypto;

#[macro_use(bson, doc)]
extern crate bson;
extern crate mongodb;

use mongodb::{Client, ThreadedClient};
use mongodb::db::ThreadedDatabase;
use bson::
{
    Bson,
    Document
};

use std::fs::File;
use std::io::BufReader;
use std::io::Read;
use std::path::Path;
use std::process::Command;
use std::thread;
use std::time::
{
    Duration,
    SystemTime,
    UNIX_EPOCH
};
use crypto::digest::Digest;
use crypto::sha3::Sha3;
use valico::json_dsl;
use rustless::server::status::StatusCode;
use rustless::json::ToJson;
use rustless::
{
    Application,
    Api,
    Nesting,
    Versioning
};
use hyper::header::
{
    ContentDisposition,
    DispositionType,
    DispositionParam,
    Charset
};

fn is_atom(repository:&str, category:&str, package:&str, version:&str) -> bool
{
    let output = Command::new("equery").arg("w")
        .arg(format!("{}/{}-{}::{}", category, package, version, repository)).output().unwrap();
    let ex = format!("{}/{}/{}/{}-{}.ebuild\n", repository, category, package, package, version);
    return output.status.success() && output.stdout.ends_with(ex.as_bytes());
}

#[test]
fn test_is_atom()
{
    assert_eq!(false, is_atom("test","test","test","test"));
    assert_eq!(true, is_atom("gentoo","app-editors","vim","8.0.1298"));
}

fn main()
{
    let client = Client::connect("localhost", 27017)
        .expect("Failed to initialize standalone client.");
    let coll = client.db("gbs").collection("builds");

    let api = Api::build(|api|
    {
        api.prefix("api");
        api.version("1", Versioning::Path);

        api.namespace("atoms/:repositories/:categories/:packages/:versions", |atoms_ns|
        {
            atoms_ns.params(|params|
            {
                params.req_typed("repositories", json_dsl::string());
                params.req_typed("categories", json_dsl::string());
                params.req_typed("packages", json_dsl::string());
                params.req_typed("versions", json_dsl::string());
            });
            atoms_ns.get("builds", |endpoint|
            {
                endpoint.handle(|client, params|
                {
                    return client.json(&params.to_json());
                })
            });
            atoms_ns.get("builds/:id/status", |endpoint|
            {
                endpoint.handle(|mut client, params|
                {
                    let repository = params.find("repositories").unwrap().to_string().trim_matches('"').to_string();
                    let category = params.find("categories").unwrap().to_string().trim_matches('"').to_string();
                    let package = params.find("packages").unwrap().to_string().trim_matches('"').to_string();
                    let version = params.find("versions").unwrap().to_string().trim_matches('"').to_string();
                    // TODO check [0-0a-f]
                    let id = params.find("id").unwrap().to_string().trim_matches('"').to_string();

                    if is_atom(&repository, &category, &package, &version)
                    {
                        let s = format!("{}/{}/{}/{}/{}/status", repository, category, package, version, id);
                        let path = Path::new(&s);
                        if path.is_file()
                        {
                            let f = File::open(&s).unwrap();
                            let mut reader = BufReader::new(f);
                            let mut buf = String::new();
                            reader.read_to_string(&mut buf);
                            return client.text(buf);
                        }
                    }
                    client.set_status(StatusCode::NotFound);
                    return client.empty();
                })
            });
            atoms_ns.get("builds/:id/log", |endpoint|
            {
                endpoint.params(|params|
                {
                    params.req_typed("id", json_dsl::string());
                });
                endpoint.handle(|mut client, params|
                {
                    let repository = params.find("repositories").unwrap().to_string().trim_matches('"').to_string();
                    let category = params.find("categories").unwrap().to_string().trim_matches('"').to_string();
                    let package = params.find("packages").unwrap().to_string().trim_matches('"').to_string();
                    let version = params.find("versions").unwrap().to_string().trim_matches('"').to_string();
                    // TODO check [0-0a-f]
                    let id = params.find("id").unwrap().to_string().trim_matches('"').to_string();

                    if is_atom(&repository, &category, &package, &version)
                    {
                        let s = format!("{}/{}/{}/{}/{}/log", repository, category, package, version, id);
                        let path = Path::new(&s);
                        if path.is_file()
                        {
                            let f = File::open(&s).unwrap();
                            let mut reader = BufReader::new(f);
                            let mut buf = String::new();
                            reader.read_to_string(&mut buf);
                            return client.text(buf);
                        }
                    }
                    client.set_status(StatusCode::NotFound);
                    return client.empty();
                })
            });
            atoms_ns.get("builds/:id", |endpoint|
            {
                endpoint.params(|params|
                {
                    params.req_typed("id", json_dsl::string());
                });
                endpoint.handle(|mut client, params|
                {
                    let repository = params.find("repositories").unwrap().to_string().trim_matches('"').to_string();
                    let category = params.find("categories").unwrap().to_string().trim_matches('"').to_string();
                    let package = params.find("packages").unwrap().to_string().trim_matches('"').to_string();
                    let version = params.find("versions").unwrap().to_string().trim_matches('"').to_string();
                    // TODO check [0-0a-f]
                    let id = params.find("id").unwrap().to_string().trim_matches('"').to_string();

                    if is_atom(&repository, &category, &package, &version)
                    {
                        let s = format!("{}/{}/{}/{}/{}/{2}-{3}.tbz2", repository, category, package, version, id);
                        let path = Path::new(&s);
                        if path.is_file()
                        {
                            client.set_header(ContentDisposition
                            {
                              disposition: DispositionType::Attachment,
                              parameters: vec![DispositionParam::Filename(
                                Charset::Us_Ascii,
                                None,
                                format!("{}-{}.tbz2", package, version).into_bytes()
                            )]});
                            return client.file(path);
                        }
                    }
                    client.set_status(StatusCode::NotFound);
                    return client.empty();
                })
            });
            atoms_ns.post("builds", |endpoint|
            {
                endpoint.handle(move |mut client, params|
                {
                    let repository = params.find("repositories").unwrap().to_string().trim_matches('"').to_string();
                    let category = params.find("categories").unwrap().to_string().trim_matches('"').to_string();
                    let package = params.find("packages").unwrap().to_string().trim_matches('"').to_string();
                    let version = params.find("versions").unwrap().to_string().trim_matches('"').to_string();

                    let mut uses = String::new();
                    let use_flag = params.find("use").unwrap();
                    for (key, value) in use_flag.as_object().unwrap().iter()
                    {
                        uses.push_str(&format!("{}{} ", match value.as_bool().unwrap(){true => "", false => "-"}, key));
                    }
                    println!("{}", params);
                    println!("{}", uses);

                    let mut hasher = Sha3::sha3_256();
                    hasher.input_str(&format!("{}{}{}{}{}", repository, category, package, version, uses));
                    let id = hasher.result_str();

                    let url = format!("{}/{}/{}/{}/builds/{}", repository, category, package, version, id);

                    if is_atom(&repository, &category, &package, &version)
                    {
                        if !Path::new(&format!("{}/{}/{}/{}/{}/{2}-{3}.tbz2", repository, category, package, version, id)).exists()
                        {
                            {
                                let mut uses = Document::new();
                                for (key, value) in use_flag.as_object().unwrap().iter()
                                {
                                    uses.insert(key.clone(), Bson::Boolean(value.as_bool().unwrap().clone()));
                                }
                                let s = SystemTime::now();
                                let doc = doc!
                                {
                                    "date" => &format!("{}", s.duration_since(UNIX_EPOCH).unwrap().as_secs()),
                                    "id" => id.clone(),
                                    "repository" => repository.clone(),
                                    "category" => category.clone(),
                                    "package" => package.clone(),
                                    "version" => version.clone(),
                                    "use" => uses
                                };
                                coll.insert_one(doc.clone(), None).ok().expect("Failed to insert document.");
                            }
                            thread::spawn(||
                            {
                                let b = Command::new("./buildreq.sh")
                                .arg(repository)
                                .arg(category)
                                .arg(package)
                                .arg(version)
                                .arg(id)
                                .arg(uses)
                                .output().unwrap();
                                println!("{}\n", String::from_utf8_lossy(&b.stdout));
                                println!("{}\n", String::from_utf8_lossy(&b.stderr));
                            });
                        }
                        return client.text(url);
                    }
                    else
                    {
                        client.set_status(StatusCode::NotFound);
                        return client.empty();
                    }
                })
            });
        });
    });

    let app = Application::new(api);

    iron::Iron::new(app).http("0.0.0.0:4000").unwrap();
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;
extern crate env_logger;
extern crate hyper;
extern crate mozprofile;
extern crate mozrunner;
extern crate regex;
extern crate rustc_serialize;
#[macro_use]
extern crate webdriver;
extern crate zip;

use clap::{App, Arg};
use marionette::{MarionetteHandler, LogLevel, MarionetteSettings, extension_routes};
use std::borrow::ToOwned;
use std::io::Write;
use std::net::{SocketAddr, IpAddr};
use std::path::PathBuf;
use std::str::FromStr;
use webdriver::server::start;

macro_rules! try_opt {
    ($expr:expr, $err_type:expr, $err_msg:expr) => ({
        match $expr {
            Some(x) => x,
            None => return Err(WebDriverError::new($err_type, $err_msg))
        }
    })
}

mod marionette;

lazy_static! {
    pub static ref VERSION: String =
        format!("{}\n\n{}", crate_version!(),
"The source code of this program is available at
https://github.com/mozilla/geckodriver.

This program is subject to the terms of the Mozilla Public License 2.0.
You can obtain a copy of the license at https://mozilla.org/MPL/2.0/.");
}

type ProgramResult = std::result::Result<(), (ExitCode, String)>;

enum ExitCode {
    Ok = 0,
    Usage = 64,
}

fn app<'a, 'b>() -> App<'a, 'b> {
    App::new("geckodriver")
        .about("WebDriver implementation for Firefox.")
        .version(&**VERSION)
        .arg(Arg::with_name("webdriver_host")
             .long("host")
             .value_name("HOST")
             .help("Host ip to use for WebDriver server (default: 127.0.0.1)")
             .takes_value(true))
        .arg(Arg::with_name("webdriver_port")
             .short("p")
             .long("port")
             .value_name("PORT")
             .help("Port to use for WebDriver server (default: 4444)")
             .takes_value(true))
        .arg(Arg::with_name("binary")
             .short("b")
             .long("binary")
             .value_name("BINARY")
             .help("Path to the Firefox binary, if no binary capability provided")
             .takes_value(true))
        .arg(Arg::with_name("marionette_port")
             .long("marionette-port")
             .value_name("PORT")
             .help("Port to use to connect to gecko (default: random free port)")
             .takes_value(true))
        .arg(Arg::with_name("connect_existing")
             .long("connect-existing")
             .requires("marionette_port")
             .help("Connect to an existing Firefox instance"))
        .arg(Arg::with_name("verbosity")
             .short("v")
             .multiple(true)
             .conflicts_with("log_level")
             .help("Set the level of verbosity. Pass once for debug level logging and twice for trace level logging"))
        .arg(Arg::with_name("log_level")
             .long("log")
             .takes_value(true)
             .value_name("LEVEL")
             .possible_values(
                 &["fatal", "error", "warn", "info", "config", "debug", "trace"])
             .help("Set Gecko log level"))
}

fn print_err(reason: &str) {
    let _ = writeln!(&mut ::std::io::stderr(), "\n{}", reason);
}

fn run() -> ProgramResult {
    let matches = app().get_matches();

    let host = matches.value_of("webdriver_host").unwrap_or("127.0.0.1");
    let port = match u16::from_str(matches.value_of("webdriver_port").unwrap_or("4444")) {
        Ok(x) => x,
        Err(_) => return Err((ExitCode::Usage, "Invalid WebDriver port".to_owned())),
    };
    let addr = match IpAddr::from_str(host) {
        Ok(addr) => SocketAddr::new(addr, port),
        Err(_) => return Err((ExitCode::Usage, "invalid host address".to_owned())),
    };

    let binary = matches.value_of("binary").map(|x| PathBuf::from(x));

    let marionette_port = match matches.value_of("marionette_port") {
        Some(x) => match u16::from_str(x) {
            Ok(x) => Some(x),
            Err(_) => return Err((ExitCode::Usage, "Invalid Marionette port".to_owned())),
        },
        None => None
    };

    // overrides defaults in Gecko
    // which are info for optimised builds
    // and debug for debug builds
    let log_level =
        if matches.is_present("log_level") {
            LogLevel::from_str(matches.value_of("log_level").unwrap()).ok()
        } else {
            match matches.occurrences_of("v") {
                0 => None,
                1 => Some(LogLevel::Debug),
                _ => Some(LogLevel::Trace),
            }
        };

    let settings = MarionetteSettings {
        port: marionette_port,
        binary: binary,
        connect_existing: matches.is_present("connect_existing"),
        log_level: log_level,
    };

    start(addr, MarionetteHandler::new(settings), extension_routes());

    Ok(())
}

fn main() {
    let _ = env_logger::init();

    let exit_code = match run() {
        Ok(_) => ExitCode::Ok,
        Err((exit_code, reason)) => {
            print_err(&reason.to_string());
            exit_code
        },
    };

    std::io::stdout().flush().unwrap();
    std::process::exit(exit_code as i32);
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeMap;
    use marionette::{FirefoxOptions};
    use webdriver::command::NewSessionParameters;
    use rustc_serialize::json::Json;
    use std::fs::File;
    use rustc_serialize::base64::{ToBase64, Config, CharacterSet, Newline};
    use mozprofile::preferences::Pref;
    use std::io::Read;

    #[test]
    fn test_profile() {
        let mut profile_data = Vec::with_capacity(1024);
        let mut profile = File::open("src/tests/profile.zip").unwrap();
        profile.read_to_end(&mut profile_data).unwrap();
        let base64_config = Config {
            char_set: CharacterSet::Standard,
            newline: Newline::LF,
            pad: true,
            line_length: None
        };
        let encoded_profile = Json::String(profile_data.to_base64(base64_config));

        let desired: BTreeMap<String, Json> = BTreeMap::new();
        let mut required: BTreeMap<String, Json> = BTreeMap::new();
        let mut firefox_options: BTreeMap<String, Json> = BTreeMap::new();
        firefox_options.insert("profile".into(), encoded_profile);
        required.insert("firefoxOptions".into(), Json::Object(firefox_options));
        let mut capabilities = NewSessionParameters {
            desired: desired,
            required: required
        };

        let options = FirefoxOptions::from_capabilities(&mut capabilities).unwrap();
        let mut profile = options.profile.unwrap();
        let prefs = profile.user_prefs().unwrap();

        println!("{:?}",prefs.prefs);

        assert_eq!(prefs.get("startup.homepage_welcome_url"),
                   Some(&Pref::new("data:text/html,PASS")));
    }

}
<commit_msg>Incrementally improve the UI (#214)<commit_after>#[macro_use]
extern crate clap;
#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;
extern crate env_logger;
extern crate hyper;
extern crate mozprofile;
extern crate mozrunner;
extern crate regex;
extern crate rustc_serialize;
#[macro_use]
extern crate webdriver;
extern crate zip;

use clap::{App, Arg};
use marionette::{MarionetteHandler, LogLevel, MarionetteSettings, extension_routes};
use std::borrow::ToOwned;
use std::io::Write;
use std::net::{SocketAddr, IpAddr};
use std::path::PathBuf;
use std::str::FromStr;

macro_rules! try_opt {
    ($expr:expr, $err_type:expr, $err_msg:expr) => ({
        match $expr {
            Some(x) => x,
            None => return Err(WebDriverError::new($err_type, $err_msg))
        }
    })
}

mod marionette;

type ProgramResult = std::result::Result<(), (ExitCode, String)>;

enum ExitCode {
    Ok = 0,
    Usage = 64,
}

fn app<'a, 'b>() -> App<'a, 'b> {
    App::new(format!("geckodriver {}", crate_version!()))
        .about("WebDriver implementation for Firefox.")
        .arg(Arg::with_name("webdriver_host")
             .long("host")
             .value_name("HOST")
             .help("Host ip to use for WebDriver server (default: 127.0.0.1)")
             .takes_value(true))
        .arg(Arg::with_name("webdriver_port")
             .short("p")
             .long("port")
             .value_name("PORT")
             .help("Port to use for WebDriver server (default: 4444)")
             .takes_value(true))
        .arg(Arg::with_name("binary")
             .short("b")
             .long("binary")
             .value_name("BINARY")
             .help("Path to the Firefox binary")
             .takes_value(true))
        .arg(Arg::with_name("marionette_port")
             .long("marionette-port")
             .value_name("PORT")
             .help("Port to use to connect to Gecko (default: random free port)")
             .takes_value(true))
        .arg(Arg::with_name("connect_existing")
             .long("connect-existing")
             .requires("marionette_port")
             .help("Connect to an existing Firefox instance"))
        .arg(Arg::with_name("verbosity")
             .short("v")
             .multiple(true)
             .conflicts_with("log_level")
             .help("Log level verbosity (-v for debug and -vv for trace level)"))
        .arg(Arg::with_name("log_level")
             .long("log")
             .takes_value(true)
             .value_name("LEVEL")
             .possible_values(
                 &["fatal", "error", "warn", "info", "config", "debug", "trace"])
             .help("Set Gecko log level"))
         .arg(Arg::with_name("version")
             .short("V")
             .long("version")
             .help("Prints version and copying information"))
}

fn print_err(reason: &str) {
    let prog = std::env::args().next().unwrap_or("geckodriver".to_owned());
    let _ = writeln!(&mut ::std::io::stderr(), "{}: error: {}", prog, reason);
}

fn run() -> ProgramResult {
    let matches = app().get_matches();

    if matches.is_present("version") {
        println!("geckodriver {}\n\n{}", crate_version!(),
"The source code of this program is available at
https://github.com/mozilla/geckodriver.

This program is subject to the terms of the Mozilla Public License 2.0.
You can obtain a copy of the license at https://mozilla.org/MPL/2.0/.");
        return Ok(())
    }

    let host = matches.value_of("webdriver_host").unwrap_or("127.0.0.1");
    let port = match u16::from_str(matches.value_of("webdriver_port").unwrap_or("4444")) {
        Ok(x) => x,
        Err(_) => return Err((ExitCode::Usage, "invalid WebDriver port".to_owned())),
    };
    let addr = match IpAddr::from_str(host) {
        Ok(addr) => SocketAddr::new(addr, port),
        Err(_) => return Err((ExitCode::Usage, "invalid host address".to_owned())),
    };

    let binary = matches.value_of("binary").map(|x| PathBuf::from(x));

    let marionette_port = match matches.value_of("marionette_port") {
        Some(x) => match u16::from_str(x) {
            Ok(x) => Some(x),
            Err(_) => return Err((ExitCode::Usage, "invalid Marionette port".to_owned())),
        },
        None => None
    };

    // overrides defaults in Gecko
    // which are info for optimised builds
    // and debug for debug builds
    let log_level =
        if matches.is_present("log_level") {
            LogLevel::from_str(matches.value_of("log_level").unwrap()).ok()
        } else {
            match matches.occurrences_of("v") {
                0 => None,
                1 => Some(LogLevel::Debug),
                _ => Some(LogLevel::Trace),
            }
        };

    let settings = MarionetteSettings {
        port: marionette_port,
        binary: binary,
        connect_existing: matches.is_present("connect_existing"),
        log_level: log_level,
    };

    let handler = MarionetteHandler::new(settings);
    webdriver::server::start(addr, handler, extension_routes());

    Ok(())
}

fn main() {
    let _ = env_logger::init();

    let exit_code = match run() {
        Ok(_) => ExitCode::Ok,
        Err((exit_code, reason)) => {
            print_err(&reason.to_string());
            exit_code
        },
    };

    std::io::stdout().flush().unwrap();
    std::process::exit(exit_code as i32);
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeMap;
    use marionette::{FirefoxOptions};
    use webdriver::command::NewSessionParameters;
    use rustc_serialize::json::Json;
    use std::fs::File;
    use rustc_serialize::base64::{ToBase64, Config, CharacterSet, Newline};
    use mozprofile::preferences::Pref;
    use std::io::Read;

    #[test]
    fn test_profile() {
        let mut profile_data = Vec::with_capacity(1024);
        let mut profile = File::open("src/tests/profile.zip").unwrap();
        profile.read_to_end(&mut profile_data).unwrap();
        let base64_config = Config {
            char_set: CharacterSet::Standard,
            newline: Newline::LF,
            pad: true,
            line_length: None
        };
        let encoded_profile = Json::String(profile_data.to_base64(base64_config));

        let desired: BTreeMap<String, Json> = BTreeMap::new();
        let mut required: BTreeMap<String, Json> = BTreeMap::new();
        let mut firefox_options: BTreeMap<String, Json> = BTreeMap::new();
        firefox_options.insert("profile".into(), encoded_profile);
        required.insert("firefoxOptions".into(), Json::Object(firefox_options));
        let mut capabilities = NewSessionParameters {
            desired: desired,
            required: required
        };

        let options = FirefoxOptions::from_capabilities(&mut capabilities).unwrap();
        let mut profile = options.profile.unwrap();
        let prefs = profile.user_prefs().unwrap();

        println!("{:?}",prefs.prefs);

        assert_eq!(prefs.get("startup.homepage_welcome_url"),
                   Some(&Pref::new("data:text/html,PASS")));
    }

}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
#[macro_use]
extern crate lazy_static;

extern crate libc;
extern crate scmp;
extern crate tempdir;

use std::fs;
use std::env;
use std::ffi::OsStr;
use std::ffi::CString;
use std::io::Error;
use std::os::raw::c_void;
use std::os::unix::process::CommandExt;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use scmp::*;

unsafe fn allow_syscall(ctx : *mut c_void, nr : u32) {
    assert!(seccomp_rule_add_array(ctx, SCMP_ACT_ALLOW, nr as i32, 0, std::ptr::null()) == 0);
}

fn setup_seccomp() -> Result<(), Error> {
    unsafe {
        let ctx0 = seccomp_init(SCMP_ACT_TRAP);
        assert!(!ctx0.is_null());
        allow_syscall(ctx0, __NR_execve);
        allow_syscall(ctx0, __NR_brk);
        allow_syscall(ctx0, __NR_access);
        allow_syscall(ctx0, __NR_mmap);
        allow_syscall(ctx0, __NR_open);
        allow_syscall(ctx0, __NR_read);
        allow_syscall(ctx0, __NR_write);
        allow_syscall(ctx0, __NR_stat);
        allow_syscall(ctx0, __NR_fstat);
        allow_syscall(ctx0, __NR_close);
        allow_syscall(ctx0, __NR_mprotect);
        allow_syscall(ctx0, __NR_arch_prctl);
        allow_syscall(ctx0, __NR_munmap);
        allow_syscall(ctx0, __NR_set_tid_address);
        allow_syscall(ctx0, __NR_set_robust_list);
        allow_syscall(ctx0, __NR_rt_sigaction);
        allow_syscall(ctx0, __NR_rt_sigprocmask);
        allow_syscall(ctx0, __NR_getrlimit);
        allow_syscall(ctx0, __NR_statfs);
        allow_syscall(ctx0, __NR_fcntl);
        allow_syscall(ctx0, __NR_geteuid);
        allow_syscall(ctx0, __NR_umask);
        allow_syscall(ctx0, __NR_pipe);
        allow_syscall(ctx0, __NR_clone);
        allow_syscall(ctx0, __NR_wait4);
        allow_syscall(ctx0, __NR_exit_group);
        allow_syscall(ctx0, __NR_dup);
        allow_syscall(ctx0, __NR_ioctl);
        allow_syscall(ctx0, __NR_getpid);
        allow_syscall(ctx0, __NR_gettid);
        allow_syscall(ctx0, __NR_tgkill);
        allow_syscall(ctx0, __NR_rt_sigreturn);
        allow_syscall(ctx0, __NR_socket);
        allow_syscall(ctx0, __NR_connect);
        allow_syscall(ctx0, __NR_lseek);
        allow_syscall(ctx0, __NR_openat);
        allow_syscall(ctx0, __NR_unlinkat);
        allow_syscall(ctx0, __NR_utimensat);
        allow_syscall(ctx0, __NR_lstat);
        allow_syscall(ctx0, __NR_fchmod);
        allow_syscall(ctx0, __NR_unlink);
        allow_syscall(ctx0, __NR_utime);
        allow_syscall(ctx0, __NR_futex);
        allow_syscall(ctx0, __NR_chmod);
        allow_syscall(ctx0, __NR_getuid);
        allow_syscall(ctx0, __NR_getgid);
        allow_syscall(ctx0, __NR_getppid);
        allow_syscall(ctx0, __NR_getcwd);
        allow_syscall(ctx0, __NR_getegid);
        allow_syscall(ctx0, __NR_getdents);
        allow_syscall(ctx0, __NR_readlink);
        allow_syscall(ctx0, __NR_mkdirat);
        allow_syscall(ctx0, __NR_mkdir);
        allow_syscall(ctx0, __NR_madvise);
        allow_syscall(ctx0, __NR_exit);
        assert!(seccomp_load(ctx0) == 0);
    }
    return Ok(());
}

const RENAME_NOREPLACE : u64 = 1;

fn path_to_cstring(path : &Path) -> CString {
    return CString::new(path.as_os_str().as_bytes()).unwrap();
}

fn rename_noreplace(path0 : &Path, path1 : &Path) -> Result<(), Error> {
    unsafe {
        if libc::syscall(__NR_renameat2 as i64, libc::AT_FDCWD, path_to_cstring(path0).as_ptr(), libc::AT_FDCWD, path_to_cstring(path1).as_ptr(), RENAME_NOREPLACE, 0) == 0 {
            return Ok(());
        } else {
            return Err(Error::last_os_error());
        }
    }
}

fn rename_or_suffix(path0 : &Path, path1 : &Path) -> Result<PathBuf, Error> {
    let rv = rename_noreplace(path0, path1);
    if let Err(err) = rv {
        if err.raw_os_error().expect("Error fails to wrap errno") != libc::EEXIST {
            return Err(err);
        }
        for suffix in 1.. {
            let mut builder = path1.as_os_str().to_os_string();
            builder.push(format!(".{}", suffix));
            let rv = rename_noreplace(path0, Path::new(&builder));
            if let Err(err) = rv {
                if err.raw_os_error().expect("Error fails to wrap errno") != libc::EEXIST {
                    return Err(err);
                }
                continue;
            }
            return Ok(Path::new(&builder).to_path_buf());
        }
        return Ok(PathBuf::new()); // XXX Unreachable
    } else {
        return Ok(path1.to_path_buf());
    }
}

struct ArchiveType {
    extensions: Vec<&'static str>,
    extract_cmd: Vec<&'static str>,
}

lazy_static! {
    static ref ARCHIVE_TYPES : Vec<ArchiveType> = vec![
        ArchiveType {
            extensions: vec![
                ".tgz",
                ".tar.gz",
                ".tar.Z",
                ".tbz2",
                ".tar.bz2",
                ".tlz",
                ".tar.lz",
                ".tar.lzma",
                ".tar.lzo",
                ".tar.xz",
            ],
            extract_cmd: vec!["tar", "-xf"],
        },
        ArchiveType {
            extensions: vec![
                ".zip",
            ],
            extract_cmd: vec!["unzip", "--"],
        },
        ArchiveType {
            extensions: vec![
                ".part1.rar",
                ".rar",
            ],
            extract_cmd: vec!["unrar", "x", "--"],
        },
        ArchiveType {
            extensions: vec![
                ".7z",
            ],
            extract_cmd: vec!["7z", "x", "--"],
        },
    ];
}

fn main() {
    let matches = clap_app!(myapp =>
        (@arg ARCHIVE: +required ... "Archives to extract")
    ).get_matches();

    let mut found = false;

    for arch in matches.values_of_os("ARCHIVE").unwrap() {
        let arch = Path::new(arch);
        let arch_bytes = arch.as_os_str().as_bytes();
        println!("Extract {:?}", arch);
        for at in ARCHIVE_TYPES.iter() {
            for &ext in &at.extensions {
                let ext_bytes = ext.as_bytes();
                if !arch_bytes.ends_with(ext_bytes) {
                    continue;
                }
                found = true;

                let noext : &OsStr = OsStrExt::from_bytes(&arch_bytes[0..arch_bytes.len()-ext_bytes.len()]);
                let stripped =
                    if let Some(noext) = Path::new(noext).file_name() {
                        noext
                    } else {
                        OsStr::new("extracted")
                    };
                let cmd = at.extract_cmd[0];
                let tmpdir = tempdir::TempDir::new_in(
                    env::current_dir().expect("Unable to get the current directory"),
                    stripped.to_string_lossy().to_mut().as_str()).expect("Unable to create temporary directory");
                let status = Command::new(cmd)
                    .args(&at.extract_cmd[1..])
                    .arg(fs::canonicalize(&arch).expect("Unable to find archive"))
                    .current_dir(&tmpdir)
                    .before_exec(setup_seccomp)
                    .status()
                    .expect(format!("Failed to launch {}", cmd).as_str());
                assert!(status.success(), format!("{} wasn't successful", cmd));
                let extracted_path = tmpdir.into_path();
                let mut dir_list = fs::read_dir(&extracted_path).expect("Unable to read the extraction directory");
                if let Some(path) = dir_list.next() {
                    let path = path.expect("Unable to iterate the extraction directory");
                    if let Some(path1) = dir_list.next() {
                        path1.expect("Unable to iterate the extraction directory");
                        let newname = rename_or_suffix(&extracted_path, Path::new(stripped)).expect("Failed to rename");
                        println!("Extracted to {:?}", newname);
                    } else {
                        let newname = rename_or_suffix(&path.path(), Path::new(stripped)).expect("Failed to rename");
                        println!("Extracted to {:?}", newname);
                        fs::remove_dir(&extracted_path).expect("Failed to remove empty extraction directory");
                    }
                } else {
                    println!("No files were extracted");
                }
                break;
            }
        }
        if !found {
            println!("Can't handle {:?}", arch);
        }
    }
}

<commit_msg>Stricter ioctl filtering<commit_after>#[macro_use]
extern crate clap;
#[macro_use]
extern crate lazy_static;

extern crate libc;
extern crate scmp;
extern crate tempdir;

use std::fs;
use std::env;
use std::ffi::OsStr;
use std::ffi::CString;
use std::io::Error;
use std::os::raw::c_void;
use std::os::unix::process::CommandExt;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use scmp::*;

unsafe fn allow_syscall(ctx : *mut c_void, nr : u32) {
    assert!(seccomp_rule_add_array(ctx, SCMP_ACT_ALLOW, nr as i32, 0, std::ptr::null()) == 0);
}

fn setup_seccomp() -> Result<(), Error> {
    unsafe {
        let ctx = seccomp_init(SCMP_ACT_TRAP);
        assert!(!ctx.is_null());
        allow_syscall(ctx, __NR_execve);
        allow_syscall(ctx, __NR_brk);
        allow_syscall(ctx, __NR_access);
        allow_syscall(ctx, __NR_mmap);
        allow_syscall(ctx, __NR_open);
        allow_syscall(ctx, __NR_read);
        allow_syscall(ctx, __NR_write);
        allow_syscall(ctx, __NR_stat);
        allow_syscall(ctx, __NR_fstat);
        allow_syscall(ctx, __NR_close);
        allow_syscall(ctx, __NR_mprotect);
        allow_syscall(ctx, __NR_arch_prctl);
        allow_syscall(ctx, __NR_munmap);
        allow_syscall(ctx, __NR_set_tid_address);
        allow_syscall(ctx, __NR_set_robust_list);
        allow_syscall(ctx, __NR_rt_sigaction);
        allow_syscall(ctx, __NR_rt_sigprocmask);
        allow_syscall(ctx, __NR_getrlimit);
        allow_syscall(ctx, __NR_statfs);
        allow_syscall(ctx, __NR_fcntl);
        allow_syscall(ctx, __NR_geteuid);
        allow_syscall(ctx, __NR_umask);
        allow_syscall(ctx, __NR_pipe);
        allow_syscall(ctx, __NR_clone);
        allow_syscall(ctx, __NR_wait4);
        allow_syscall(ctx, __NR_exit_group);
        allow_syscall(ctx, __NR_dup);
        assert!(seccomp_rule_add(ctx, SCMP_ACT_ALLOW, __NR_ioctl as i32, 1, scmp_arg_cmp {
            arg: 1, op: scmp_compare::SCMP_CMP_EQ, datum_a: libc::FIOCLEX, datum_b: 0,
        }) == 0);
        assert!(seccomp_rule_add(ctx, SCMP_ACT_ALLOW, __NR_ioctl as i32, 1, scmp_arg_cmp {
            arg: 1, op: scmp_compare::SCMP_CMP_EQ, datum_a: libc::FIONBIO, datum_b: 0,
        }) == 0);
        assert!(seccomp_rule_add(ctx, SCMP_ACT_ALLOW, __NR_ioctl as i32, 1, scmp_arg_cmp {
            arg: 1, op: scmp_compare::SCMP_CMP_EQ, datum_a: libc::TCGETS, datum_b: 0,
        }) == 0);
        assert!(seccomp_rule_add(ctx, SCMP_ACT_ALLOW, __NR_ioctl as i32, 1, scmp_arg_cmp {
            arg: 1, op: scmp_compare::SCMP_CMP_EQ, datum_a: libc::TIOCGWINSZ, datum_b: 0,
        }) == 0);
        allow_syscall(ctx, __NR_getpid);
        allow_syscall(ctx, __NR_gettid);
        allow_syscall(ctx, __NR_tgkill);
        allow_syscall(ctx, __NR_rt_sigreturn);
        allow_syscall(ctx, __NR_socket);
        allow_syscall(ctx, __NR_connect);
        allow_syscall(ctx, __NR_lseek);
        allow_syscall(ctx, __NR_openat);
        allow_syscall(ctx, __NR_unlinkat);
        allow_syscall(ctx, __NR_utimensat);
        allow_syscall(ctx, __NR_lstat);
        allow_syscall(ctx, __NR_fchmod);
        allow_syscall(ctx, __NR_unlink);
        allow_syscall(ctx, __NR_utime);
        allow_syscall(ctx, __NR_futex);
        allow_syscall(ctx, __NR_chmod);
        allow_syscall(ctx, __NR_getuid);
        allow_syscall(ctx, __NR_getgid);
        allow_syscall(ctx, __NR_getppid);
        allow_syscall(ctx, __NR_getcwd);
        allow_syscall(ctx, __NR_getegid);
        allow_syscall(ctx, __NR_getdents);
        allow_syscall(ctx, __NR_readlink);
        allow_syscall(ctx, __NR_mkdirat);
        allow_syscall(ctx, __NR_mkdir);
        allow_syscall(ctx, __NR_madvise);
        allow_syscall(ctx, __NR_exit);
        assert!(seccomp_load(ctx) == 0);
    }
    return Ok(());
}

const RENAME_NOREPLACE : u64 = 1;

fn path_to_cstring(path : &Path) -> CString {
    return CString::new(path.as_os_str().as_bytes()).unwrap();
}

fn rename_noreplace(path0 : &Path, path1 : &Path) -> Result<(), Error> {
    unsafe {
        if libc::syscall(__NR_renameat2 as i64, libc::AT_FDCWD, path_to_cstring(path0).as_ptr(), libc::AT_FDCWD, path_to_cstring(path1).as_ptr(), RENAME_NOREPLACE, 0) == 0 {
            return Ok(());
        } else {
            return Err(Error::last_os_error());
        }
    }
}

fn rename_or_suffix(path0 : &Path, path1 : &Path) -> Result<PathBuf, Error> {
    let rv = rename_noreplace(path0, path1);
    if let Err(err) = rv {
        if err.raw_os_error().expect("Error fails to wrap errno") != libc::EEXIST {
            return Err(err);
        }
        for suffix in 1.. {
            let mut builder = path1.as_os_str().to_os_string();
            builder.push(format!(".{}", suffix));
            let rv = rename_noreplace(path0, Path::new(&builder));
            if let Err(err) = rv {
                if err.raw_os_error().expect("Error fails to wrap errno") != libc::EEXIST {
                    return Err(err);
                }
                continue;
            }
            return Ok(Path::new(&builder).to_path_buf());
        }
        return Ok(PathBuf::new()); // XXX Unreachable
    } else {
        return Ok(path1.to_path_buf());
    }
}

struct ArchiveType {
    extensions: Vec<&'static str>,
    extract_cmd: Vec<&'static str>,
}

lazy_static! {
    static ref ARCHIVE_TYPES : Vec<ArchiveType> = vec![
        ArchiveType {
            extensions: vec![
                ".tgz",
                ".tar.gz",
                ".tar.Z",
                ".tbz2",
                ".tar.bz2",
                ".tlz",
                ".tar.lz",
                ".tar.lzma",
                ".tar.lzo",
                ".tar.xz",
            ],
            extract_cmd: vec!["tar", "-xf"],
        },
        ArchiveType {
            extensions: vec![
                ".zip",
            ],
            extract_cmd: vec!["unzip", "--"],
        },
        ArchiveType {
            extensions: vec![
                ".part1.rar",
                ".rar",
            ],
            extract_cmd: vec!["unrar", "x", "--"],
        },
        ArchiveType {
            extensions: vec![
                ".7z",
            ],
            extract_cmd: vec!["7z", "x", "--"],
        },
    ];
}

fn main() {
    let matches = clap_app!(myapp =>
        (@arg ARCHIVE: +required ... "Archives to extract")
    ).get_matches();

    let mut found = false;

    for arch in matches.values_of_os("ARCHIVE").unwrap() {
        let arch = Path::new(arch);
        let arch_bytes = arch.as_os_str().as_bytes();
        println!("Extract {:?}", arch);
        for at in ARCHIVE_TYPES.iter() {
            for &ext in &at.extensions {
                let ext_bytes = ext.as_bytes();
                if !arch_bytes.ends_with(ext_bytes) {
                    continue;
                }
                found = true;

                let noext : &OsStr = OsStrExt::from_bytes(&arch_bytes[0..arch_bytes.len()-ext_bytes.len()]);
                let stripped =
                    if let Some(noext) = Path::new(noext).file_name() {
                        noext
                    } else {
                        OsStr::new("extracted")
                    };
                let cmd = at.extract_cmd[0];
                let tmpdir = tempdir::TempDir::new_in(
                    env::current_dir().expect("Unable to get the current directory"),
                    stripped.to_string_lossy().to_mut().as_str()).expect("Unable to create temporary directory");
                let status = Command::new(cmd)
                    .args(&at.extract_cmd[1..])
                    .arg(fs::canonicalize(&arch).expect("Unable to find archive"))
                    .current_dir(&tmpdir)
                    .before_exec(setup_seccomp)
                    .status()
                    .expect(format!("Failed to launch {}", cmd).as_str());
                assert!(status.success(), format!("{} wasn't successful", cmd));
                let extracted_path = tmpdir.into_path();
                let mut dir_list = fs::read_dir(&extracted_path).expect("Unable to read the extraction directory");
                if let Some(path) = dir_list.next() {
                    let path = path.expect("Unable to iterate the extraction directory");
                    if let Some(path1) = dir_list.next() {
                        path1.expect("Unable to iterate the extraction directory");
                        let newname = rename_or_suffix(&extracted_path, Path::new(stripped)).expect("Failed to rename");
                        println!("Extracted to {:?}", newname);
                    } else {
                        let newname = rename_or_suffix(&path.path(), Path::new(stripped)).expect("Failed to rename");
                        println!("Extracted to {:?}", newname);
                        fs::remove_dir(&extracted_path).expect("Failed to remove empty extraction directory");
                    }
                } else {
                    println!("No files were extracted");
                }
                break;
            }
        }
        if !found {
            println!("Can't handle {:?}", arch);
        }
    }
}

<|endoftext|>


<commit_before>//#![deny(warnings)]

extern crate git2;
extern crate docopt;
extern crate rustc_serialize;

use std::process::{Command, exit};

use docopt::Docopt;
use git2::{Repository, Error, StatusOptions, ErrorCode};

#[derive(RustcDecodable)]
struct Args {
    arg_spec: Vec<String>,
    flag_git_dir: Option<String>,
}

/// Gets the current branch.
///
/// # Examples
///
/// ```
/// let repo = try!(Repository::open("."));
/// let branchname = try!(branch(&repo));
/// ```
fn branch<'a>(repo: &'a Repository) -> Result<String, Error> {
    let head = match repo.head() {
        Ok(head) => Some(head),
            Err(ref e) if e.code() == ErrorCode::UnbornBranch ||
                          e.code() == ErrorCode::NotFound
                       => None,
            Err(e) => return Err(e),
    };
    let branchref = head.as_ref().and_then(|h| h.shorthand());
    let branchname = branchref.unwrap_or("HEAD (no branch)").to_string();

    Ok(branchname)
}

fn run(args: &Args) -> Result<(), Error> {
    let path = args.flag_git_dir.clone().unwrap_or(".".to_string());
    let repo = try!(Repository::open(&path));

    if repo.is_bare() {
        return Err(Error::from_str("Bare repositories are not allowed"))
    }

    let mut opts = StatusOptions::new();

    for spec in args.arg_spec.iter() {
        opts.pathspec(spec);
    }

    let current_branch = try!(branch(&repo));

    println!("Current branch {:?}", current_branch);

    checkout("master");
    pull("origin", "master");
    checkout(&current_branch);
    // rebase("master");

    return Ok(())
}

fn checkout(target_branch: &str) {
    let output = Command::new("git")
                         .arg("checkout")
                         .arg(target_branch)
                         .output()
                         .unwrap_or_else(|e| {
                            panic!("failed to execute process: {}", e) });
    let status_code = output.status.code();

    println!("Checking out {} ...", target_branch);

    match status_code {
        Some(0) => println!("{}", String::from_utf8_lossy(&output.stdout)),
        Some(_) => {
             println!("{}", String::from_utf8_lossy(&output.stderr));
             exit(status_code.unwrap());
        }
        None => println!("Something wrong happened"),
    }
}

fn pull(target_remote: &str, target_branch: &str) {
    let output = Command::new("git")
                         .arg("pull")
                         .arg("--quiet")
                         .arg("--rebase=preserve")
                         .arg(target_remote)
                         .arg(target_branch)
                         .output()
                         .unwrap_or_else(|e| {
                            panic!("failed to execute process: {}", e) });
    let status_code = output.status.code();

    println!("Pulling {} {} ...", target_remote, target_branch);

    match status_code {
        Some(0) => println!("{}", String::from_utf8_lossy(&output.stdout)),
        Some(_) => {
             println!("{}", String::from_utf8_lossy(&output.stderr));
             exit(status_code.unwrap());
        }
        None => println!("Something wrong happened"),
    }
}

fn main() {
    const USAGE: &'static str = "
usage: absorb [options] [--] [<branchname>]

Options:
    -h, --help                  show this message
";

    let args = Docopt::new(USAGE).and_then(|d| d.decode())
                                 .unwrap_or_else(|e| e.exit());
    match run(&args) {
        Ok(()) => {}
        Err(e) => println!("error: {}", e),
    }
}
<commit_msg>Add rebase() function<commit_after>//#![deny(warnings)]

extern crate git2;
extern crate docopt;
extern crate rustc_serialize;

use std::process::{Command, exit};

use docopt::Docopt;
use git2::{Repository, Error, StatusOptions, ErrorCode};

#[derive(RustcDecodable)]
struct Args {
    arg_spec: Vec<String>,
    flag_git_dir: Option<String>,
}

/// Gets the current branch.
///
/// # Examples
///
/// ```
/// let repo = try!(Repository::open("."));
/// let branchname = try!(branch(&repo));
/// ```
fn branch<'a>(repo: &'a Repository) -> Result<String, Error> {
    let head = match repo.head() {
        Ok(head) => Some(head),
            Err(ref e) if e.code() == ErrorCode::UnbornBranch ||
                          e.code() == ErrorCode::NotFound
                       => None,
            Err(e) => return Err(e),
    };
    let branchref = head.as_ref().and_then(|h| h.shorthand());
    let branchname = branchref.unwrap_or("HEAD (no branch)").to_string();

    Ok(branchname)
}

fn run(args: &Args) -> Result<(), Error> {
    let path = args.flag_git_dir.clone().unwrap_or(".".to_string());
    let repo = try!(Repository::open(&path));

    if repo.is_bare() {
        return Err(Error::from_str("Bare repositories are not allowed"))
    }

    let mut opts = StatusOptions::new();

    for spec in args.arg_spec.iter() {
        opts.pathspec(spec);
    }

    let current_branch = try!(branch(&repo));

    println!("Current branch {:?}", current_branch);

    checkout("master");
    pull("origin", "master");
    checkout(&current_branch);
    rebase("master");

    return Ok(())
}

fn checkout(target_branch: &str) {
    let output = Command::new("git")
                         .arg("checkout")
                         .arg(target_branch)
                         .output()
                         .unwrap_or_else(|e| {
                            panic!("failed to execute process: {}", e) });
    let status_code = output.status.code();

    println!("Checking out {} ...", target_branch);

    match status_code {
        Some(0) => println!("{}", String::from_utf8_lossy(&output.stdout)),
        Some(_) => {
             println!("{}", String::from_utf8_lossy(&output.stderr));
             exit(status_code.unwrap());
        }
        None => println!("Something wrong happened"),
    }
}

fn pull(target_remote: &str, target_branch: &str) {
    let output = Command::new("git")
                         .arg("pull")
                         .arg("--quiet")
                         .arg("--rebase=preserve")
                         .arg(target_remote)
                         .arg(target_branch)
                         .output()
                         .unwrap_or_else(|e| {
                            panic!("failed to execute process: {}", e) });
    let status_code = output.status.code();

    println!("Pulling {} {} ...", target_remote, target_branch);

    match status_code {
        Some(0) => println!("{}", String::from_utf8_lossy(&output.stdout)),
        Some(_) => {
             println!("{}", String::from_utf8_lossy(&output.stderr));
             exit(status_code.unwrap());
        }
        None => println!("Something wrong happened"),
    }
}

fn rebase(target_branch: &str) {
    let output = Command::new("git")
                         .arg("rebase")
                         .arg(target_branch)
                         .output()
                         .unwrap_or_else(|e| {
                            panic!("failed to execute process: {}", e) });
    let status_code = output.status.code();

    println!("Rebasing {} ...", target_branch);

    match status_code {
        Some(0) => println!("{}", String::from_utf8_lossy(&output.stdout)),
        Some(_) => {
             println!("{}", String::from_utf8_lossy(&output.stderr));
             exit(status_code.unwrap());
        }
        None => println!("Something wrong happened"),
    }
}

fn main() {
    const USAGE: &'static str = "
usage: absorb [options] [--] [<branchname>]

Options:
    -h, --help                  show this message
";

    let args = Docopt::new(USAGE).and_then(|d| d.decode())
                                 .unwrap_or_else(|e| e.exit());
    match run(&args) {
        Ok(()) => {}
        Err(e) => println!("error: {}", e),
    }
}
<|endoftext|>


<commit_before>#[cfg(not(test))]
fn main() {
    use std::env;
    use std::process::{self, Command};

    let args = wrap_args(env::args());
    let mut command = Command::new("cargo");
    command.args(&args);
    let mut child = command.spawn().unwrap_or_else(|e| panic!("{}", e));
    let exit_status = child.wait().unwrap_or_else(|e| panic!("{}", e));

    if let Some(code) = exit_status.code() {
        process::exit(code);
    }
}

fn wrap_args<I: Iterator<Item=String>>(it: I) -> Vec<String> {
    let mut args = vec!["rustc".to_owned()];
    let mut has_double_hyphen = false;
    for arg in it.skip(2) {
        has_double_hyphen |= &arg == "--";
        args.push(arg);
    }

    if !has_double_hyphen {
        args.push("--".to_owned());
    }
    args.push("-Zno-trans".to_owned());
    args
}

#[test]
fn wrap_args_1() {
    let args = vec![
        "/usr/local/bin/cargo-check".to_owned(),
        "check".to_owned(),
        "-h".to_owned()
    ];
    let actual = wrap_args(args.into_iter());
    let expected = vec![
        "rustc".to_owned(),
        "-h".to_owned(),
        "--".to_owned(),
        "-Zno-trans".to_owned()
    ];
    assert_eq!(actual, expected);
}

#[test]
fn wrap_args_2() {
    let args = vec![
        "/usr/local/bin/cargo-check".to_owned(),
        "check".to_owned(),
        "--".to_owned(),
        "-Zverbose".to_owned()
    ];
    let actual = wrap_args(args.into_iter());
    let expected = vec![
        "rustc".to_owned(),
        "--".to_owned(),
        "-Zverbose".to_owned(),
        "-Zno-trans".to_owned()
    ];
    assert_eq!(actual, expected);
}
<commit_msg>Simplified test cases by allowing &str<commit_after>#[cfg(not(test))]
fn main() {
    use std::env;
    use std::process::{self, Command};

    let args = wrap_args(env::args());
    let mut command = Command::new("cargo");
    command.args(&args);
    let mut child = command.spawn().unwrap_or_else(|e| panic!("{}", e));
    let exit_status = child.wait().unwrap_or_else(|e| panic!("{}", e));

    if let Some(code) = exit_status.code() {
        process::exit(code);
    }
}

fn wrap_args<T, I>(it: I) -> Vec<String>
    where T: AsRef<str>,
          I: IntoIterator<Item=T> {

    let it = it.into_iter();
    let mut args = vec!["rustc".to_owned()];
    let mut has_double_hyphen = false;

    for arg in it.skip(2) {
        let arg = arg.as_ref().to_owned();
        has_double_hyphen |= &arg == "--";
        args.push(arg);
    }

    if !has_double_hyphen {
        args.push("--".to_owned());
    }
    args.push("-Zno-trans".to_owned());
    args
}

#[test]
fn wrap_args_1() {
    let args = [
        "/usr/local/bin/cargo-check",
        "check",
        "-h"
    ];
    let actual = wrap_args(&args);
    let expected = [
        "rustc",
        "-h",
        "--",
        "-Zno-trans"
    ];
    assert_eq!(actual, expected);
}

#[test]
fn wrap_args_2() {
    let args = [
        "/usr/local/bin/cargo-check",
        "check",
        "--",
        "-Zverbose"
    ];
    let actual = wrap_args(&args);
    let expected = [
        "rustc",
        "--",
        "-Zverbose",
        "-Zno-trans"
    ];
    assert_eq!(actual, expected);
}
<|endoftext|>