<commit_before>extern crate clap;
extern crate pretty_bytes;
extern crate chrono;
extern crate ansi_term;
extern crate iron;
extern crate multipart;

use std::env;
use std::fs::{self, File};
use std::path::{PathBuf};
use std::error::Error;
use std::time::{SystemTime, UNIX_EPOCH};
use std::os::unix::ffi::OsStrExt;

use iron::headers;
use iron::status;
use iron::mime;
use iron::method;
use iron::modifiers::Redirect;
use iron::{Iron, Request, Response, IronResult, Set, Chain, Handler,
           AfterMiddleware};
use multipart::server::{Multipart, SaveResult};
use pretty_bytes::converter::convert;
use chrono::{DateTime, Local, TimeZone};
use ansi_term::Colour::{Red, Green, Yellow, Blue};

const ROOT_LINK: &'static str = "[ROOT]";

fn main() {
    let matches = clap::App::new("Simple HTTP Server")
        .arg(clap::Arg::with_name("root")
             .index(1)
             .validator(|s| {
                 match File::open(s) {
                     Ok(f) => {
                         if f.metadata().unwrap().is_dir() { Ok(()) } else {
                             Err("Not directory".to_owned())
                         }
                     },
                     Err(e) => Err(e.description().to_string())
                 }
             })
             .help("Root directory"))
        .arg(clap::Arg::with_name("index")
             .short("i")
             .long("index")
             .help("Enable automatic render index page [index.html, index.htm]"))
        .arg(clap::Arg::with_name("upload")
             .short("u")
             .long("upload")
             .help("Enable upload files (multiple select)"))
        .arg(clap::Arg::with_name("port")
             .short("p")
             .long("port")
             .takes_value(true)
             .default_value("8000")
             .validator(|s| {
                 match s.parse::<u16>() {
                     Ok(_) => Ok(()),
                     Err(e) => Err(e.description().to_string())
                 }
             })
             .help("Port number"))
        .arg(clap::Arg::with_name("threads")
             .short("t")
             .long("threads")
             .takes_value(true)
             .default_value("3")
             .validator(|s| {
                 match s.parse::<u8>() {
                     Ok(v) => {
                         if v > 0 { Ok(()) } else {
                             Err("Nagetive threads".to_owned())
                         }
                     }
                     Err(e) => Err(e.description().to_string())
                 }
             })
             .help("How many http threads"))
        .get_matches();
    let root = matches
        .value_of("root")
        .map(|s| PathBuf::from(s))
        .unwrap_or(env::current_dir().unwrap());
    let index = matches.is_present("index");
    let upload = matches.is_present("upload");
    let port = matches
        .value_of("port")
        .unwrap()
        .parse::<u16>()
        .unwrap();
    let threads = matches
        .value_of("threads")
        .unwrap()
        .parse::<u8>()
        .unwrap();

    let addr = format!("0.0.0.0:{}", port);
    println!("   Root: {}", Blue.paint(root.to_str().unwrap()));
    println!("  Index: {}", Blue.paint(index.to_string()));
    println!(" Upload: {}", Blue.paint(upload.to_string()));
    println!("Address: {}", Blue.paint(format!("http://{}", addr)));
    println!("======== [{}] ========", Blue.paint(now_string()));

    let mut chain = Chain::new(MainHandler{root, index, upload});
    chain.link_after(RequestLogger);
    let mut server = Iron::new(chain);
    server.threads = threads as usize;
    server.http(&addr).expect(format!("Could not bind on: {}", addr).as_str());
}

struct MainHandler { root: PathBuf, index: bool, upload: bool }
struct RequestLogger;

impl MainHandler {

    fn error(&self, s: status::Status, msg: &str)  -> IronResult<Response> {
        let mut resp = Response::with((s, format!(
            "{root_link}

[ERROR {code}]: {msg}
",
            root_link=ROOT_LINK,
            code=s.to_u16(),
            msg=msg
        )));
        resp.headers.set(headers::ContentType::html());
        Ok(resp)
    }

    fn save_files(&self, req: &mut Request, path: &PathBuf) -> Result<(), (status::Status, String)> {
        match Multipart::from_request(req) {
            Ok(mut multipart) => {
                // Fetching all data and processing it.
                // save().temp() reads the request fully, parsing all fields and saving all files
                // in a new temporary directory under the OS temporary directory.
                match multipart.save().temp() {
                    SaveResult::Full(entries) => {
                        for (_, files) in entries.files {
                            for file in files {
                                let mut target_path = path.clone();
                                target_path.push(file.filename.unwrap());
                                if let Err(errno) = fs::copy(file.path, target_path) {
                                    return Err((status::InternalServerError, format!("Copy file failed: {}", errno)));
                                }
                            }
                        }
                        Ok(())
                    },
                    SaveResult::Partial(_entries, reason) => {
                        Err((status::InternalServerError, reason.unwrap_err().description().to_owned()))
                    }
                    SaveResult::Error(error) => Err((status::InternalServerError, error.description().to_owned())),
                }
            }
            Err(_) => Err((status::BadRequest ,"The request is not multipart".to_owned()))
        }
    }

    fn send_file(&self, mut resp: Response, path: &PathBuf) -> IronResult<Response> {
        resp.set_mut(path.as_path());
        if resp.headers.get::<headers::ContentType>() == Some(
            &headers::ContentType(mime::Mime(mime::TopLevel::Text, mime::SubLevel::Plain, vec![]))
        ) {
            resp.headers.set(headers::ContentDisposition {
                disposition: headers::DispositionType::Attachment,
                parameters: vec![headers::DispositionParam::Filename(
                    headers::Charset::Ext("utf-8".to_owned()), // The character set for the bytes of the filename
                    None, // The optional language tag (see `language-tag` crate)
                    path.file_name().unwrap().as_bytes().to_vec() // the actual bytes of the filename
                )]
            });
            let default_mime: iron::mime::Mime = "application/octet-stream".parse().unwrap();
            resp.headers.set(headers::ContentType(default_mime));
        }
        return Ok(resp)
    }
}

impl Handler for MainHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        let mut path = self.root.clone();
        for part in req.url.path() {
            path.push(part);
        }

        if self.upload && req.method == method::Post {
            if let Err((s, msg)) = self.save_files(req, &path) {
                return self.error(s, &msg);
            } else {
                return Ok(Response::with((status::Found, Redirect(req.url.clone()))))
            }
        }

        match File::open(&path) {
            Ok(f) => {
                let mut resp = Response::with(status::Ok);
                let metadata = f.metadata().unwrap();
                if metadata.is_dir() {
                    let mut rows = Vec::new();

                    let path_prefix = req.url.path()
                        .into_iter()
                        .filter(|s| !s.is_empty())
                        .collect::<Vec<&str>>();
                    let breadcrumb = if path_prefix.len() > 0 {
                        let mut breadcrumb = path_prefix.clone();
                        let mut bread_links: Vec<String> = Vec::new();
                        bread_links.push(breadcrumb.pop().unwrap().to_owned());
                        while breadcrumb.len() > 0 {
                            let link = breadcrumb.join("/");
                            bread_links.push(format!(
                                "{label}",
                                link=link, label=breadcrumb.pop().unwrap().to_owned(),
                            ));
                        }
                        bread_links.push(ROOT_LINK.to_owned());
                        bread_links.reverse();
                        bread_links.join(" / ")
                    } else { ROOT_LINK.to_owned() };

                    if path_prefix.len() > 0 {
                        let mut link = path_prefix.clone();
                        link.pop();
                        rows.push(format!(
                            "
{label}
",
                            link=link.join("/"), label="[Parent Directory]"
                        ));
                    } else {
                        rows.push("

".to_owned());
                    }
                    for entry in fs::read_dir(&path).unwrap() {
                        let entry = entry.unwrap();
                        let entry_meta = entry.metadata().unwrap();
                        let file_name = entry.file_name().into_string().unwrap();
                        if self.index {
                            for fname in vec!["index.html", "index.htm"] {
                                if file_name == fname {
                                    path.push(file_name);
                                    return self.send_file(resp, &path);
                                }
                            }
                        }
                        let file_modified = system_time_to_date_time(entry_meta.modified().unwrap())
                            .format("%Y-%m-%d %H:%M:%S").to_string();
                        let file_size = convert(entry_meta.len() as f64);
                        let file_type = entry_meta.file_type();
                        let link_style = if file_type.is_dir() {
                            "style=\"text-decoration: none; font-weight: bold;\"".to_owned()
                        } else {
                            "style=\"text-decoration: none;\"".to_owned()
                        };
                        let mut link = path_prefix.clone();
                        link.push(&file_name);
                        let link = link.join("/");
                        rows.push(format!(
                            "
{}	 [{}]	 <bold>{}</bold>
",
                            link_style, link, file_name, file_modified, file_size
                        ));
                    }
                    resp.headers.set(headers::ContentType::html());
                    let upload_form = if self.upload {
                        format!(r#"
<form style="margin-top: 1em;" action="/{path}" method="POST" enctype="multipart/form-data">
  <input type="file" name="files" accept="*" multiple />
  <input type="submit" value="Upload" />
</form>
"#,
                                path=path_prefix.join("/"))
                    } else { "".to_owned() };
                    resp.set_mut(format!(
                        r#"

  {upload_form}

{breadcrumb}

{rows}

"#,
                        upload_form=upload_form,
                        breadcrumb=breadcrumb,
                        rows=rows.join("\n")));
                    Ok(resp)
                } else {
                    self.send_file(resp, &path)
                }
            },
            Err(e) => self.error(status::NotFound, e.description().to_string().as_str())
        }
    }
}

impl AfterMiddleware for RequestLogger {
    fn after(&self, req: &mut Request, resp: Response) -> IronResult<Response> {
        let status = resp.status.unwrap();
        let status_str = if status.is_success() {
            Green.bold().paint(status.to_u16().to_string())
        } else if status.is_informational() || status.is_redirection() {
            Yellow.bold().paint(status.to_u16().to_string())
        } else {
            Red.bold().paint(status.to_u16().to_string())
        };

        println!(
            // datetime, remote-ip, status-code, method, url-path
            "[{}] - {} - {} - {} /{}",
            now_string(),
            req.remote_addr.ip(),
            status_str,
            req.method,
            req.url.as_ref().to_string().splitn(4, '/').collect::<Vec<&str>>().pop().unwrap()
        );
        Ok(resp)
    }
}

fn now_string() -> String {
    Local::now().format("%Y-%m-%d %H:%M:%S").to_string()
}

fn system_time_to_date_time(t: SystemTime) -> DateTime<Local> {
    let (sec, nsec) = match t.duration_since(UNIX_EPOCH) {
        Ok(dur) => (dur.as_secs() as i64, dur.subsec_nanos()),
        Err(e) => { // unlikely but should be handled
            let dur = e.duration();
            let (sec, nsec) = (dur.as_secs() as i64, dur.subsec_nanos());
            if nsec == 0 {
                (-sec, 0)
            } else {
                (-sec - 1, 1_000_000_000 - nsec)
            }
        },
    };
    Local.timestamp(sec, nsec)
}
<commit_msg>Add threads info to log<commit_after>extern crate clap;
extern crate pretty_bytes;
extern crate chrono;
extern crate ansi_term;
extern crate iron;
extern crate multipart;

use std::env;
use std::fs::{self, File};
use std::path::{PathBuf};
use std::error::Error;
use std::time::{SystemTime, UNIX_EPOCH};
use std::os::unix::ffi::OsStrExt;

use iron::headers;
use iron::status;
use iron::mime;
use iron::method;
use iron::modifiers::Redirect;
use iron::{Iron, Request, Response, IronResult, Set, Chain, Handler,
           AfterMiddleware};
use multipart::server::{Multipart, SaveResult};
use pretty_bytes::converter::convert;
use chrono::{DateTime, Local, TimeZone};
use ansi_term::Colour::{Red, Green, Yellow, Blue};

const ROOT_LINK: &'static str = "[ROOT]";

fn main() {
    let matches = clap::App::new("Simple HTTP Server")
        .arg(clap::Arg::with_name("root")
             .index(1)
             .validator(|s| {
                 match File::open(s) {
                     Ok(f) => {
                         if f.metadata().unwrap().is_dir() { Ok(()) } else {
                             Err("Not directory".to_owned())
                         }
                     },
                     Err(e) => Err(e.description().to_string())
                 }
             })
             .help("Root directory"))
        .arg(clap::Arg::with_name("index")
             .short("i")
             .long("index")
             .help("Enable automatic render index page [index.html, index.htm]"))
        .arg(clap::Arg::with_name("upload")
             .short("u")
             .long("upload")
             .help("Enable upload files (multiple select)"))
        .arg(clap::Arg::with_name("port")
             .short("p")
             .long("port")
             .takes_value(true)
             .default_value("8000")
             .validator(|s| {
                 match s.parse::<u16>() {
                     Ok(_) => Ok(()),
                     Err(e) => Err(e.description().to_string())
                 }
             })
             .help("Port number"))
        .arg(clap::Arg::with_name("threads")
             .short("t")
             .long("threads")
             .takes_value(true)
             .default_value("3")
             .validator(|s| {
                 match s.parse::<u8>() {
                     Ok(v) => {
                         if v > 0 { Ok(()) } else {
                             Err("Nagetive threads".to_owned())
                         }
                     }
                     Err(e) => Err(e.description().to_string())
                 }
             })
             .help("How many http threads"))
        .get_matches();
    let root = matches
        .value_of("root")
        .map(|s| PathBuf::from(s))
        .unwrap_or(env::current_dir().unwrap());
    let index = matches.is_present("index");
    let upload = matches.is_present("upload");
    let port = matches
        .value_of("port")
        .unwrap()
        .parse::<u16>()
        .unwrap();
    let threads = matches
        .value_of("threads")
        .unwrap()
        .parse::<u8>()
        .unwrap();

    let addr = format!("0.0.0.0:{}", port);
    println!("  Index: {}, Upload: {}, Threads: {}",
             Blue.paint(index.to_string()),
             Blue.paint(upload.to_string()),
             Blue.paint(threads.to_string()));
    println!("   Root: {}", Blue.paint(root.to_str().unwrap()));
    println!("Address: {}", Blue.paint(format!("http://{}", addr)));
    println!("======== [{}] ========", Blue.paint(now_string()));

    let mut chain = Chain::new(MainHandler{root, index, upload});
    chain.link_after(RequestLogger);
    let mut server = Iron::new(chain);
    server.threads = threads as usize;
    server.http(&addr).expect(format!("Could not bind on: {}", addr).as_str());
}

struct MainHandler { root: PathBuf, index: bool, upload: bool }
struct RequestLogger;

impl MainHandler {

    fn error(&self, s: status::Status, msg: &str)  -> IronResult<Response> {
        let mut resp = Response::with((s, format!(
            "{root_link}

[ERROR {code}]: {msg}
",
            root_link=ROOT_LINK,
            code=s.to_u16(),
            msg=msg
        )));
        resp.headers.set(headers::ContentType::html());
        Ok(resp)
    }

    fn save_files(&self, req: &mut Request, path: &PathBuf) -> Result<(), (status::Status, String)> {
        match Multipart::from_request(req) {
            Ok(mut multipart) => {
                // Fetching all data and processing it.
                // save().temp() reads the request fully, parsing all fields and saving all files
                // in a new temporary directory under the OS temporary directory.
                match multipart.save().temp() {
                    SaveResult::Full(entries) => {
                        for (_, files) in entries.files {
                            for file in files {
                                let mut target_path = path.clone();
                                target_path.push(file.filename.unwrap());
                                if let Err(errno) = fs::copy(file.path, target_path) {
                                    return Err((status::InternalServerError, format!("Copy file failed: {}", errno)));
                                }
                            }
                        }
                        Ok(())
                    },
                    SaveResult::Partial(_entries, reason) => {
                        Err((status::InternalServerError, reason.unwrap_err().description().to_owned()))
                    }
                    SaveResult::Error(error) => Err((status::InternalServerError, error.description().to_owned())),
                }
            }
            Err(_) => Err((status::BadRequest ,"The request is not multipart".to_owned()))
        }
    }

    fn send_file(&self, mut resp: Response, path: &PathBuf) -> IronResult<Response> {
        resp.set_mut(path.as_path());
        if resp.headers.get::<headers::ContentType>() == Some(
            &headers::ContentType(mime::Mime(mime::TopLevel::Text, mime::SubLevel::Plain, vec![]))
        ) {
            resp.headers.set(headers::ContentDisposition {
                disposition: headers::DispositionType::Attachment,
                parameters: vec![headers::DispositionParam::Filename(
                    headers::Charset::Ext("utf-8".to_owned()), // The character set for the bytes of the filename
                    None, // The optional language tag (see `language-tag` crate)
                    path.file_name().unwrap().as_bytes().to_vec() // the actual bytes of the filename
                )]
            });
            let default_mime: iron::mime::Mime = "application/octet-stream".parse().unwrap();
            resp.headers.set(headers::ContentType(default_mime));
        }
        return Ok(resp)
    }
}

impl Handler for MainHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        let mut path = self.root.clone();
        for part in req.url.path() {
            path.push(part);
        }

        if self.upload && req.method == method::Post {
            if let Err((s, msg)) = self.save_files(req, &path) {
                return self.error(s, &msg);
            } else {
                return Ok(Response::with((status::Found, Redirect(req.url.clone()))))
            }
        }

        match File::open(&path) {
            Ok(f) => {
                let mut resp = Response::with(status::Ok);
                let metadata = f.metadata().unwrap();
                if metadata.is_dir() {
                    let mut rows = Vec::new();

                    let path_prefix = req.url.path()
                        .into_iter()
                        .filter(|s| !s.is_empty())
                        .collect::<Vec<&str>>();
                    let breadcrumb = if path_prefix.len() > 0 {
                        let mut breadcrumb = path_prefix.clone();
                        let mut bread_links: Vec<String> = Vec::new();
                        bread_links.push(breadcrumb.pop().unwrap().to_owned());
                        while breadcrumb.len() > 0 {
                            let link = breadcrumb.join("/");
                            bread_links.push(format!(
                                "{label}",
                                link=link, label=breadcrumb.pop().unwrap().to_owned(),
                            ));
                        }
                        bread_links.push(ROOT_LINK.to_owned());
                        bread_links.reverse();
                        bread_links.join(" / ")
                    } else { ROOT_LINK.to_owned() };

                    if path_prefix.len() > 0 {
                        let mut link = path_prefix.clone();
                        link.pop();
                        rows.push(format!(
                            "
{label}
",
                            link=link.join("/"), label="[Parent Directory]"
                        ));
                    } else {
                        rows.push("

".to_owned());
                    }
                    for entry in fs::read_dir(&path).unwrap() {
                        let entry = entry.unwrap();
                        let entry_meta = entry.metadata().unwrap();
                        let file_name = entry.file_name().into_string().unwrap();
                        if self.index {
                            for fname in vec!["index.html", "index.htm"] {
                                if file_name == fname {
                                    path.push(file_name);
                                    return self.send_file(resp, &path);
                                }
                            }
                        }
                        let file_modified = system_time_to_date_time(entry_meta.modified().unwrap())
                            .format("%Y-%m-%d %H:%M:%S").to_string();
                        let file_size = convert(entry_meta.len() as f64);
                        let file_type = entry_meta.file_type();
                        let link_style = if file_type.is_dir() {
                            "style=\"text-decoration: none; font-weight: bold;\"".to_owned()
                        } else {
                            "style=\"text-decoration: none;\"".to_owned()
                        };
                        let mut link = path_prefix.clone();
                        link.push(&file_name);
                        let link = link.join("/");
                        rows.push(format!(
                            "
{}	 [{}]	 <bold>{}</bold>
",
                            link_style, link, file_name, file_modified, file_size
                        ));
                    }
                    resp.headers.set(headers::ContentType::html());
                    let upload_form = if self.upload {
                        format!(r#"
<form style="margin-top: 1em;" action="/{path}" method="POST" enctype="multipart/form-data">
  <input type="file" name="files" accept="*" multiple />
  <input type="submit" value="Upload" />
</form>
"#,
                                path=path_prefix.join("/"))
                    } else { "".to_owned() };
                    resp.set_mut(format!(
                        r#"

  {upload_form}

{breadcrumb}

{rows}

"#,
                        upload_form=upload_form,
                        breadcrumb=breadcrumb,
                        rows=rows.join("\n")));
                    Ok(resp)
                } else {
                    self.send_file(resp, &path)
                }
            },
            Err(e) => self.error(status::NotFound, e.description().to_string().as_str())
        }
    }
}

impl AfterMiddleware for RequestLogger {
    fn after(&self, req: &mut Request, resp: Response) -> IronResult<Response> {
        let status = resp.status.unwrap();
        let status_str = if status.is_success() {
            Green.bold().paint(status.to_u16().to_string())
        } else if status.is_informational() || status.is_redirection() {
            Yellow.bold().paint(status.to_u16().to_string())
        } else {
            Red.bold().paint(status.to_u16().to_string())
        };

        println!(
            // datetime, remote-ip, status-code, method, url-path
            "[{}] - {} - {} - {} /{}",
            now_string(),
            req.remote_addr.ip(),
            status_str,
            req.method,
            req.url.as_ref().to_string().splitn(4, '/').collect::<Vec<&str>>().pop().unwrap()
        );
        Ok(resp)
    }
}

fn now_string() -> String {
    Local::now().format("%Y-%m-%d %H:%M:%S").to_string()
}

fn system_time_to_date_time(t: SystemTime) -> DateTime<Local> {
    let (sec, nsec) = match t.duration_since(UNIX_EPOCH) {
        Ok(dur) => (dur.as_secs() as i64, dur.subsec_nanos()),
        Err(e) => { // unlikely but should be handled
            let dur = e.duration();
            let (sec, nsec) = (dur.as_secs() as i64, dur.subsec_nanos());
            if nsec == 0 {
                (-sec, 0)
            } else {
                (-sec - 1, 1_000_000_000 - nsec)
            }
        },
    };
    Local.timestamp(sec, nsec)
}
<|endoftext|>


<commit_before>extern crate chrono;
extern crate clap;
extern crate openssl;
use chrono::prelude::*;
use clap::{Arg, App};
use openssl::asn1::Asn1TimeRef;
use openssl::error::ErrorStack;
use openssl::nid;
use openssl::string::OpensslString;
use openssl::x509::X509;

use std::io;
use std::io::Read;
use std::fs::File;

fn main() {
    let args = App::new("X.509 Parse")
        .about("Experiment in parsing X.509 certificates")
        .arg(Arg::with_name("InputFile")
            .long("file")
            .short("f")
            .takes_value(true)
            .required(true))
        .get_matches();

    let input_file_path = args.value_of("InputFile").unwrap();
    let cert_bytes = get_cert_bytes(input_file_path).unwrap();
    let cert = X509::from_pem(&cert_bytes).unwrap();

    println!("------------COMMON NAMES------------");
    let cert_common_names = get_subject_name(&cert);
    for cn in cert_common_names {
        match cn {
            Ok(cn) => println!("{:?}", cn),
            Err(err) => println!("{:?}", err)
        }
    }

    println!("------------SUBJECT ALT NAMES------------");
    let cert_subject_alt_names = get_subject_alt_names(&cert);
    match cert_subject_alt_names {
        None => println!("{:?}", "No SANs listed in certificate".to_string()),
        Some(sans) => {
            for san in sans {
                println!("{:?}", san)
            }
        }
    }

    println!("------------Validity Dates------------");
    let validity_dates = get_validity_dates(&cert);
    println!("Not before: {}", validity_dates.0);
    println!("Not after: {}", validity_dates.1);

    let not_before = convert_Asn1TimeRef_to_DateTimeUTC(validity_dates.0);
    match not_before {
        Err(e) => println!("Error parsing not_before date: {}", e),
        Ok(not_before) => println!("Not before: {:?}", not_before)
    }

    let not_after = convert_Asn1TimeRef_to_DateTimeUTC(validity_dates.1);
    match not_after {
        Err(e) => println!("Error parsing not_after date: {}", e),
        Ok(not_after) => println!("Not after: {:?}", not_after)
    }
}

fn get_cert_bytes(input_file_path: &str) -> Result<Vec<u8>, io::Error> {
    let mut input_data = Vec::new();

    File::open(input_file_path)?.read_to_end(&mut input_data)?;

    Ok(input_data)
}

fn get_subject_name(cert: &X509) -> Vec<Result<OpensslString, ErrorStack>> {
    let entries = cert.subject_name();
    let common_name_entries = entries.entries_by_nid(nid::COMMONNAME);
    common_name_entries.map(|x| (*x).data().as_utf8()).collect()
}

fn get_subject_alt_names(cert: & X509) -> Option<Vec<String>> {
    let entries = cert.subject_alt_names();
    match entries {
        None => None,
        Some(e) => Some(e.into_iter()
            .filter_map(|x| x.dnsname().and_then(|x| Some(x.to_owned())))
            .filter_map(Option::Some)
            .collect())
    }
}

fn get_validity_dates<'a>(cert: &'a X509) -> (&'a Asn1TimeRef, &'a Asn1TimeRef) {
    let not_before = cert.not_before();
    let not_after = cert.not_after();
    (not_before, not_after)
}

fn convert_Asn1TimeRef_to_DateTimeUTC(input: & Asn1TimeRef) -> Result<chrono::DateTime<Utc>, chrono::ParseError> {
    use std::fmt::Write;

    let mut stringified = String::new();
    let result = write!(&mut stringified, "{}", input);
    if result.is_err() {
        panic!("Error writing Asn1TimeRef to string");
    }
    stringified = stringified.replace("GMT", "+00:00");

    let output = DateTime::parse_from_str(& stringified, "%b %d %H:%M:%S %Y %z")?;

    Ok(output.with_timezone(&Utc))
}<commit_msg>Disabled non_snake_case lint for type conversion function<commit_after>extern crate chrono;
extern crate clap;
extern crate openssl;
use chrono::prelude::*;
use clap::{Arg, App};
use openssl::asn1::Asn1TimeRef;
use openssl::error::ErrorStack;
use openssl::nid;
use openssl::string::OpensslString;
use openssl::x509::X509;

use std::io;
use std::io::Read;
use std::fs::File;

fn main() {
    let args = App::new("X.509 Parse")
        .about("Experiment in parsing X.509 certificates")
        .arg(Arg::with_name("InputFile")
            .long("file")
            .short("f")
            .takes_value(true)
            .required(true))
        .get_matches();

    let input_file_path = args.value_of("InputFile").unwrap();
    let cert_bytes = get_cert_bytes(input_file_path).unwrap();
    let cert = X509::from_pem(&cert_bytes).unwrap();

    println!("------------COMMON NAMES------------");
    let cert_common_names = get_subject_name(&cert);
    for cn in cert_common_names {
        match cn {
            Ok(cn) => println!("{:?}", cn),
            Err(err) => println!("{:?}", err)
        }
    }

    println!("------------SUBJECT ALT NAMES------------");
    let cert_subject_alt_names = get_subject_alt_names(&cert);
    match cert_subject_alt_names {
        None => println!("{:?}", "No SANs listed in certificate".to_string()),
        Some(sans) => {
            for san in sans {
                println!("{:?}", san)
            }
        }
    }

    println!("------------Validity Dates------------");
    let validity_dates = get_validity_dates(&cert);
    println!("Not before: {}", validity_dates.0);
    println!("Not after: {}", validity_dates.1);

    let not_before = convert_Asn1TimeRef_to_DateTimeUTC(validity_dates.0);
    match not_before {
        Err(e) => println!("Error parsing not_before date: {}", e),
        Ok(not_before) => println!("Not before: {:?}", not_before)
    }

    let not_after = convert_Asn1TimeRef_to_DateTimeUTC(validity_dates.1);
    match not_after {
        Err(e) => println!("Error parsing not_after date: {}", e),
        Ok(not_after) => println!("Not after: {:?}", not_after)
    }
}

fn get_cert_bytes(input_file_path: &str) -> Result<Vec<u8>, io::Error> {
    let mut input_data = Vec::new();

    File::open(input_file_path)?.read_to_end(&mut input_data)?;

    Ok(input_data)
}

fn get_subject_name(cert: &X509) -> Vec<Result<OpensslString, ErrorStack>> {
    let entries = cert.subject_name();
    let common_name_entries = entries.entries_by_nid(nid::COMMONNAME);
    common_name_entries.map(|x| (*x).data().as_utf8()).collect()
}

fn get_subject_alt_names(cert: & X509) -> Option<Vec<String>> {
    let entries = cert.subject_alt_names();
    match entries {
        None => None,
        Some(e) => Some(e.into_iter()
            .filter_map(|x| x.dnsname().and_then(|x| Some(x.to_owned())))
            .filter_map(Option::Some)
            .collect())
    }
}

fn get_validity_dates<'a>(cert: &'a X509) -> (&'a Asn1TimeRef, &'a Asn1TimeRef) {
    let not_before = cert.not_before();
    let not_after = cert.not_after();
    (not_before, not_after)
}

#[allow(non_snake_case)] //This func converts types, so type names are used in func name
fn convert_Asn1TimeRef_to_DateTimeUTC(input: & Asn1TimeRef) -> Result<chrono::DateTime<Utc>, chrono::ParseError> {
    use std::fmt::Write;

    let mut stringified = String::new();
    let result = write!(&mut stringified, "{}", input);
    if result.is_err() {
        panic!("Error writing Asn1TimeRef to string");
    }
    stringified = stringified.replace("GMT", "+00:00");

    let output = DateTime::parse_from_str(& stringified, "%b %d %H:%M:%S %Y %z")?;

    Ok(output.with_timezone(&Utc))
}<|endoftext|>


<commit_before>#![allow(unknown_lints)]
#![allow(unused_doc_comment)]
#![cfg_attr(feature="clippy", feature(plugin))]

extern crate amy;
extern crate byteorder;
extern crate rand;
extern crate sha1;
extern crate url;
#[macro_use]
extern crate lazy_static;
extern crate net2;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate bincode;
extern crate toml;
extern crate ctrlc;
#[macro_use]
extern crate error_chain;
extern crate c_ares;
extern crate httparse;
extern crate base64;
extern crate base32;
extern crate shellexpand;
extern crate chrono;
extern crate openssl;

// TODO: Get rid of this
extern crate num;

#[macro_use]
mod log;

mod handle;
mod bencode;
mod torrent;
mod util;
mod socket;
mod disk;
mod tracker;
mod control;
mod listener;
mod rpc;
mod throttle;
mod config;

use std::thread;
use std::sync::{atomic, mpsc};
use std::io;

// We need to do this for the log macros
use log::LogLevel;
use control::acio;

pub const DHT_EXT: (usize, u8) = (7, 1);
pub const EXT_PROTO: (usize, u8) = (5, 0x10);
pub const UT_META_ID: u8 = 9;

/// Throttler max token amount
pub const THROT_TOKS: usize = 2 * 1024 * 1024;

lazy_static! {
    pub static ref SHUTDOWN: atomic::AtomicBool = {
        atomic::AtomicBool::new(false)
    };

    pub static ref CONFIG: config::Config = {
        if let Ok(cfg)  = config::ConfigFile::try_load() {
            info!("Loaded config file");
            config::Config::from_file(cfg)
        } else {
            info!("Using default config");
            Default::default()
        }
    };

    pub static ref PEER_ID: [u8; 20] = {
        use rand::{self, Rng};

        let mut pid = [0u8; 20];
        let prefix = b"-SY0001-";
        for i in 0..prefix.len() {
            pid[i] = prefix[i];
        }

        let mut rng = rand::thread_rng();
        for i in prefix.len()..20 {
            pid[i] = rng.gen::<u8>();
        }
        pid
    };
}

fn init() -> io::Result<Vec<thread::JoinHandle<()>>> {
    let cpoll = amy::Poller::new()?;
    let mut creg = cpoll.get_registrar()?;
    let (dh, dhj) = disk::start(&mut creg)?;
    let (lh, lhj) = listener::Listener::start(&mut creg)?;
    let (rh, rhj) = rpc::RPC::start(&mut creg)?;
    let (th, thj) = tracker::Tracker::start(&mut creg)?;
    let chans = acio::ACChans {
        disk_tx: dh.tx,
        disk_rx: dh.rx,
        rpc_tx: rh.tx,
        rpc_rx: rh.rx,
        trk_tx: th.tx,
        trk_rx: th.rx,
        lst_tx: lh.tx,
        lst_rx: lh.rx,
    };
    let (tx, rx) = mpsc::channel();
    let chj = thread::spawn(move || {
        let throttler = throttle::Throttler::new(None, None, THROT_TOKS, &creg);
        let acio = acio::ACIO::new(cpoll, creg, chans);
        match control::Control::new(acio, throttler) {
            Ok(mut c) => {
                tx.send(Ok(())).unwrap();
                c.run();
            }
            Err(e) => {
                tx.send(Err(e)).unwrap();
            }
        }
    });
    rx.recv().unwrap()?;

    ctrlc::set_handler(|| {
        info!("Trigggering shutdown via signal!");
        SHUTDOWN.store(true, atomic::Ordering::SeqCst);
    }).map_err(|_| util::io_err_val("Signal installation failed!"))?;

    Ok(vec![chj, dhj, lhj, rhj, thj])
}

fn main() {
    if cfg!(debug_assertions) {
        log::log_init(log::LogLevel::Debug);
    } else {
        log::log_init(log::LogLevel::Info);
    }
    info!("Initializing");
    match init() {
        Ok(threads) => {
            info!("Initialized");
            for thread in threads {
                if let Err(_) = thread.join() {
                    error!("Unclean shutdown detected, terminating");
                    return;
                }
            }
            info!("Shutdown complete");
        }
        Err(e) => {
            error!("Couldn't initialize synapse: {}", e);
        }
    }
}
<commit_msg>Handle double SIGINT<commit_after>#![allow(unknown_lints)]
#![allow(unused_doc_comment)]
#![cfg_attr(feature="clippy", feature(plugin))]

extern crate amy;
extern crate byteorder;
extern crate rand;
extern crate sha1;
extern crate url;
#[macro_use]
extern crate lazy_static;
extern crate net2;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate bincode;
extern crate toml;
extern crate ctrlc;
#[macro_use]
extern crate error_chain;
extern crate c_ares;
extern crate httparse;
extern crate base64;
extern crate base32;
extern crate shellexpand;
extern crate chrono;
extern crate openssl;

// TODO: Get rid of this
extern crate num;

#[macro_use]
mod log;

mod handle;
mod bencode;
mod torrent;
mod util;
mod socket;
mod disk;
mod tracker;
mod control;
mod listener;
mod rpc;
mod throttle;
mod config;

use std::{thread, process};
use std::sync::{atomic, mpsc};
use std::io;

// We need to do this for the log macros
use log::LogLevel;
use control::acio;

pub const DHT_EXT: (usize, u8) = (7, 1);
pub const EXT_PROTO: (usize, u8) = (5, 0x10);
pub const UT_META_ID: u8 = 9;

/// Throttler max token amount
pub const THROT_TOKS: usize = 2 * 1024 * 1024;

lazy_static! {
    pub static ref SHUTDOWN: atomic::AtomicBool = {
        atomic::AtomicBool::new(false)
    };

    pub static ref CONFIG: config::Config = {
        if let Ok(cfg)  = config::ConfigFile::try_load() {
            info!("Loaded config file");
            config::Config::from_file(cfg)
        } else {
            info!("Using default config");
            Default::default()
        }
    };

    pub static ref PEER_ID: [u8; 20] = {
        use rand::{self, Rng};

        let mut pid = [0u8; 20];
        let prefix = b"-SY0001-";
        for i in 0..prefix.len() {
            pid[i] = prefix[i];
        }

        let mut rng = rand::thread_rng();
        for i in prefix.len()..20 {
            pid[i] = rng.gen::<u8>();
        }
        pid
    };
}

fn init() -> io::Result<Vec<thread::JoinHandle<()>>> {
    let cpoll = amy::Poller::new()?;
    let mut creg = cpoll.get_registrar()?;
    let (dh, dhj) = disk::start(&mut creg)?;
    let (lh, lhj) = listener::Listener::start(&mut creg)?;
    let (rh, rhj) = rpc::RPC::start(&mut creg)?;
    let (th, thj) = tracker::Tracker::start(&mut creg)?;
    let chans = acio::ACChans {
        disk_tx: dh.tx,
        disk_rx: dh.rx,
        rpc_tx: rh.tx,
        rpc_rx: rh.rx,
        trk_tx: th.tx,
        trk_rx: th.rx,
        lst_tx: lh.tx,
        lst_rx: lh.rx,
    };
    let (tx, rx) = mpsc::channel();
    let chj = thread::spawn(move || {
        let throttler = throttle::Throttler::new(None, None, THROT_TOKS, &creg);
        let acio = acio::ACIO::new(cpoll, creg, chans);
        match control::Control::new(acio, throttler) {
            Ok(mut c) => {
                tx.send(Ok(())).unwrap();
                c.run();
            }
            Err(e) => {
                tx.send(Err(e)).unwrap();
            }
        }
    });
    rx.recv().unwrap()?;

    ctrlc::set_handler(|| if SHUTDOWN.load(atomic::Ordering::SeqCst) {
        process::abort();
    } else {
        info!("Trigggering shutdown via signal!");
        SHUTDOWN.store(true, atomic::Ordering::SeqCst);
    }).map_err(|_| util::io_err_val("Signal installation failed!"))?;

    Ok(vec![chj, dhj, lhj, rhj, thj])
}

fn main() {
    if cfg!(debug_assertions) {
        log::log_init(log::LogLevel::Debug);
    } else {
        log::log_init(log::LogLevel::Info);
    }
    info!("Initializing");
    match init() {
        Ok(threads) => {
            info!("Initialized");
            for thread in threads {
                if let Err(_) = thread.join() {
                    error!("Unclean shutdown detected, terminating");
                    return;
                }
            }
            info!("Shutdown complete");
        }
        Err(e) => {
            error!("Couldn't initialize synapse: {}", e);
        }
    }
}
<|endoftext|>


<commit_before>
extern crate hyper;
extern crate futures;

use hyper::header::ContentLength;
use hyper::server::{Http, Request, Response, Service};
use hyper::{Method, StatusCode};

use std::ascii::AsciiExt;
use futures::Stream;
use hyper::{Body, Chunk};

use futures::future;
use futures::future::{Either, Map, FutureResult};
use futures::stream::Concat2;
use futures::Future;

fn main() {
    println!("Hello, world!");
    let addr = "127.0.0.1:3000".parse().unwrap();
    let server = Http::new().bind(&addr, || Ok(LoadBalancingHandler)).unwrap();
    server.run().unwrap();
}

const PHRASE: &'static str = "Hello, World!";

struct LoadBalancingHandler;

impl Service for LoadBalancingHandler {
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    // The future representing the eventual Response your call will
    // resolve to. This can change to whatever Future you need.
    type Future = Either<
        FutureResult<Self::Response, Self::Error>,
        Map<Concat2<Body>, fn(Chunk) -> Self::Response>
    >;

    fn call(&self, req: Self::Request) -> Self::Future {
        match (req.method(), req.path()) {
            (&Method::Get, "/health-check") => {

                let response = Response::new()
                        .with_header(ContentLength(PHRASE.len() as u64))
                        .with_body(PHRASE);
                Either::A(futures::future::ok(response))
            },
            (&Method::Post, "/echo") => {
                Either::B(req.body()
                             .concat2()
                             .map(reverse))
            },
            _ => {
                Either::A(future::ok(Response::new().with_status(StatusCode::NotFound)))
            }
        }
    }
}

fn reverse(chunk: Chunk) -> Response {
    let reversed = chunk.iter()
        .rev()
        .cloned()
        .collect::<Vec<u8>>();
    Response::new()
        .with_body(reversed)
}

//fn to_uppercase(chunk: Chunk) -> Chunk {
//    let uppered = chunk.iter()
//        .map(|byte| byte.to_ascii_uppercase())
//        .collect::<Vec<u8>>();
//    Chunk::from(uppered)
//}

//#[macro_use]
//extern crate serde_derive;
//
//extern crate futures_cpupool;
//extern crate tokio_proto;
//extern crate tokio_minihttp;
//extern crate futures;
//extern crate rand;
//extern crate serde;
//extern crate serde_json;
//extern crate tokio_service;
//
//use std::io;
//
//use futures::{BoxFuture, Future};
//use futures_cpupool::CpuPool;
//use rand::Rng;
//use tokio_minihttp::{Request, Response};
//use tokio_proto::TcpServer;
//use tokio_service::Service;

//fn main() {
////    println!("Hello, world!");
////
////    let addr = "0.0.0.0:8080".parse().unwrap();
////
////    let thread_pool = CpuPool::new(10);
////
////    TcpServer::new(tokio_minihttp::Http, addr).serve(move || {
////        Ok(Server {
////            thread_pool: thread_pool.clone(),
////        })
////    })
//}

//struct Server {
//    thread_pool: CpuPool
//}
//
//impl Service for Server {
//    type Error = io::Error;
//    type Future = BoxFuture<Response, io::Error>;
//    type Request = Request;
//    type Response = Response;
//
//    fn call(&self, req: Self::Request) -> Self::Future {
//        assert_eq!(req.path(), "/db");
//        let random_id = rand::thread_rng().gen_range(1, 5);
//        unimplemented!()
//    }
//}
//
//#[derive(Serialize)]
//struct Message {
//    id: i32,
//    body: String,
//}
<commit_msg>first version running!<commit_after>
extern crate hyper;
extern crate futures;
extern crate tokio_core;

use hyper::header::ContentLength;
use hyper::server::{Http, Request, Response, Service};
use hyper::{Method, StatusCode};

use std::ascii::AsciiExt;
use futures::{Stream, Future};
use hyper::{Body, Chunk};

use futures::future;
use futures::future::{Either, Map, FutureResult};
use futures::stream::Concat2;

use std::io::{self, Write};
use hyper::Client;
use tokio_core::reactor::Core;
use tokio_core::reactor::Handle;

use tokio_core::net::TcpListener;

use hyper::Uri;

fn main() {
    println!("Starting Load Balancer...");

    let addr = "127.0.0.1:3000".parse().unwrap();

    let http = Http::new();
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let listener = TcpListener::bind(&addr, &handle).unwrap();

    let server = listener.incoming()
        .for_each(|(sock, addr)| {
            let service = Proxy { handle: handle.clone() };
            http.bind_connection(&handle, sock, addr, service);
            Ok(())
        });

    core.run(server).unwrap();
}

struct Proxy {
    handle: Handle
}

impl Proxy {

    fn create_proxy_url(&self, host: &str, uri: Uri) -> Uri {
        format!("{}{}{}", host, uri.path(), uri.query().unwrap_or("")).parse().unwrap()
    }
}

impl Service for Proxy {
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    type Future = Box<Future<Item=Self::Response, Error = Self::Error>>;

    fn call(&self, req: Self::Request) -> Self::Future {
        let method = req.method().clone();
        let uri = self.create_proxy_url("http://reddit.com", req.uri().clone());

        let mut client_req = Request::new(method, uri);
        client_req.headers_mut().extend(req.headers().iter());
        client_req.set_body(req.body());

        println!("Request: {:?}", client_req);

        let client = Client::new(&self.handle);
        let resp = client.request(client_req)
            .then(move |result| {
                match result {
                    Ok(client_resp) => {
                        Ok(Response::new()
                            .with_status(client_resp.status())
                            .with_headers(client_resp.headers().clone())
                            .with_body(client_resp.body()))
                    }
                    Err(e) => {
                        println!("{:?}", &e);
                        Err(e)
                    }
                }
            });
        Box::new(resp)
    }
}
<|endoftext|>


<commit_before>extern crate docopt;
extern crate rustc_serialize;

use docopt::Docopt;

const USAGE: &'static str = "
how-do-i-escape: Prints escape sequences for unicode graphemes

Usage:
  how-do-i-escape <grapheme>
  how-do-i-escape (--help | --version)

Options:
  -h, --help  Show this screen.
  --version   Show version.

Example:
  $ how-do-i-escape \u{00A7}
  css  = \"\\00A7\"
  html = §
  js   = \"\\u00A7\"
";

#[derive(RustcDecodable)]
struct Args {
    arg_grapheme: String
}

fn main() {
  let args: Args = Docopt::new(USAGE)
    .and_then(|d| d.help(true).version(Some("v0.1.0".to_string())).decode())
    .unwrap_or_else(|e| e.exit());

  println!("css  = {}", as_css(&args.arg_grapheme));
  println!("html = {}", as_html(&args.arg_grapheme));
  println!("js   = {}", as_js(&args.arg_grapheme));
}

fn as_css(_: &str) -> String {
  "\"\\00A7\"".to_owned()
}

fn as_html(_: &str) -> String {
  "§".to_owned()
}

fn as_js(_: &str) -> String {
  "\"\\u00A7\"".to_owned()
}
<commit_msg>Tool output is now actually based on the input<commit_after>extern crate docopt;
extern crate rustc_serialize;

use docopt::Docopt;

const USAGE: &'static str = "
how-do-i-escape: Prints escape sequences for unicode graphemes

Usage:
  how-do-i-escape <grapheme>
  how-do-i-escape (--help | --version)

Options:
  -h, --help  Show this screen.
  --version   Show version.

Example:
  $ how-do-i-escape \u{00A7}
  css  = \"\\00A7\"
  html = §
  js   = \"\\u00A7\"
";

#[derive(RustcDecodable)]
struct Args {
    arg_grapheme: String
}

fn main() {
  let args: Args = Docopt::new(USAGE)
    .and_then(|d| d.help(true).version(Some("v0.1.0".to_string())).decode())
    .unwrap_or_else(|e| e.exit());

  let as_int = to_int(args.arg_grapheme);

  println!("css  = {}", as_css(as_int));
  println!("html = {}", as_html(as_int));
  println!("js   = {}", as_js(as_int));
}

fn to_int(grapheme: String) -> u32 {
  grapheme.chars().next().unwrap() as u32
}

fn as_css(i: u32) -> String {
  format!("\"\\{:01$X}\"", i, 4)
}

fn as_html(i: u32) -> String {
  format!("&#x{:01$X};", i, 4)
}

fn as_js(i: u32) -> String {
  format!("\"\\u{:01$X}\"", i, 4)
}
<|endoftext|>


<commit_before>extern crate chrono;
extern crate clap;
extern crate libc;
extern crate multirust;
extern crate rust_install;

extern crate rustc_bisect;

use std::process;

use chrono::{Datelike, NaiveDate};
use clap::{App, AppSettings, Arg};

use rustc_bisect::{Cmd, Error, Result, ToolchainSpec, bisect};

fn run_rust_bisect() -> Result<i32> {
    let matches = App::new("rustc-bisect")
                      .author("Kamal Marhubi <kamal@marhubi.com>")
                      .setting(AppSettings::TrailingVarArg)
                      .arg(Arg::with_name("good")
                               .long("good")
                               .takes_value(true)
                               .value_name("TOOLCHAIN")
                               .required(true))
                      .arg(Arg::with_name("bad")
                               .long("bad")
                               .takes_value(true)
                               .value_name("TOOLCHAIN")
                               .required(true))
                      .arg(Arg::with_name("COMMAND")
                               .multiple(true)
                               .required(true))
                      .get_matches();

    let cfg = try!(multirust::Cfg::from_env(rust_install::notify::SharedNotifyHandler::none()));

    use std::str::FromStr;

    let good = matches.value_of("good").expect("good");
    let good_spec = try!(ToolchainSpec::from_str(good));

    let bad = matches.value_of("bad").expect("bad");
    let bad_spec = try!(ToolchainSpec::from_str(bad));

    fn get_nightly_date(spec: ToolchainSpec) -> Result<NaiveDate> {
        match spec {
            ToolchainSpec::Nightly(date) => Ok(date),
            _ => Err(Error::from("only nightlies for now")),
        }
    }

    let good_date = try!(get_nightly_date(good_spec));
    let bad_date = try!(get_nightly_date(bad_spec));

    let cmd: Vec<_> = matches.values_of_os("COMMAND").expect("COMMAND").collect();
    let cmd = Cmd::from(&cmd[..]);

    let range = good_date.num_days_from_ce()..bad_date.num_days_from_ce();

    let res = bisect(range, |num_days| {
        let spec = ToolchainSpec::Nightly(NaiveDate::from_num_days_from_ce(num_days));
        let toolchain = cfg.get_toolchain(&spec.to_string(), false).expect("get_toolchain");

        if toolchain.install_from_dist_if_not_installed().is_err() {
            return None;
        }

        let res = cmd.succeeds_with(&toolchain).expect("run command");

        println!("command {} at {}",
                 if res {
                     "succeeded"
                 } else {
                     "failed"
                 },
                 spec);
        Some(!res)
    });

    if let Some(num_days) = res {
        println!("first failing nightly: {}",
                 ToolchainSpec::Nightly(NaiveDate::from_num_days_from_ce(num_days)));
        Ok(libc::EXIT_SUCCESS)
    } else {
        println!("bisect failed");
        Ok(libc::EXIT_FAILURE)
    }
}

fn main() {
    process::exit(run_rust_bisect().expect("something went wrong"));
}
<commit_msg>Tidy up parsing version args<commit_after>extern crate chrono;
extern crate clap;
extern crate libc;
extern crate multirust;
extern crate rust_install;

extern crate rustc_bisect;

use std::process;

use chrono::{Datelike, NaiveDate};
use clap::{App, AppSettings, Arg};

use rustc_bisect::{Cmd, Error, Result, ToolchainSpec, bisect};

fn run_rust_bisect() -> Result<i32> {
    let matches = App::new("rustc-bisect")
                      .author("Kamal Marhubi <kamal@marhubi.com>")
                      .setting(AppSettings::TrailingVarArg)
                      .arg(Arg::with_name("good")
                               .long("good")
                               .takes_value(true)
                               .value_name("TOOLCHAIN")
                               .required(true))
                      .arg(Arg::with_name("bad")
                               .long("bad")
                               .takes_value(true)
                               .value_name("TOOLCHAIN")
                               .required(true))
                      .arg(Arg::with_name("COMMAND")
                               .multiple(true)
                               .required(true))
                      .get_matches();

    let cfg = try!(multirust::Cfg::from_env(rust_install::notify::SharedNotifyHandler::none()));

    let good = matches.value_of("good").expect("clap didn't respect required arg `good`");
    let good_spec: ToolchainSpec = try!(good.parse());

    let bad = matches.value_of("bad").expect("clap didn't respect required arg `bad`");
    let bad_spec: ToolchainSpec = try!(bad.parse());

    fn get_nightly_date(spec: ToolchainSpec) -> Result<NaiveDate> {
        match spec {
            ToolchainSpec::Nightly(date) => Ok(date),
            _ => Err(Error::from("only nightlies for now")),
        }
    }

    let good_date = try!(get_nightly_date(good_spec));
    let bad_date = try!(get_nightly_date(bad_spec));

    let cmd: Vec<_> = matches.values_of_os("COMMAND").expect("COMMAND").collect();
    let cmd = Cmd::from(&cmd[..]);

    let range = good_date.num_days_from_ce()..bad_date.num_days_from_ce();

    let res = bisect(range, |num_days| {
        let spec = ToolchainSpec::Nightly(NaiveDate::from_num_days_from_ce(num_days));
        let toolchain = cfg.get_toolchain(&spec.to_string(), false).expect("get_toolchain");

        if toolchain.install_from_dist_if_not_installed().is_err() {
            return None;
        }

        let res = cmd.succeeds_with(&toolchain).expect("run command");

        println!("command {} at {}",
                 if res {
                     "succeeded"
                 } else {
                     "failed"
                 },
                 spec);
        Some(!res)
    });

    if let Some(num_days) = res {
        println!("first failing nightly: {}",
                 ToolchainSpec::Nightly(NaiveDate::from_num_days_from_ce(num_days)));
        Ok(libc::EXIT_SUCCESS)
    } else {
        println!("bisect failed");
        Ok(libc::EXIT_FAILURE)
    }
}

fn main() {
    process::exit(run_rust_bisect().expect("something went wrong"));
}
<|endoftext|>


<commit_before>extern crate combine;
extern crate nix; // As in *nix
extern crate rand;
extern crate signal;

mod commander;

// Reference: https://github.com/NixOS/nixpkgs/blob/b07051ce6c3ba3039c89b6755da279002b0c3ace/nixos/modules/virtualisation/nixos-container.pl

use nix::sys::signal::{kill, SIGTERM, SIGINT};
use std::io;
use std::io::Write;
use std::process::Command;

enum Error {
    Usage(String),
    NonZero(String, i32),
    ControlError(String),
    IoError(io::Error),
    NixError(nix::Error),
    Interrupted(commander::Signal)
}

use Error::*;

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Self { IoError(e) }
}

impl From<nix::Error> for Error {
    fn from(e: nix::Error) -> Self { NixError(e) }
}

impl From<commander::NonZero> for Error {
    fn from(e: commander::NonZero) -> Self { match e { commander::NonZero::NonZero(s, o) => NonZero(s, o), commander::NonZero::Interrupted(n) => Interrupted(n) } }
}

type Result<T> = std::result::Result<T, Error>;

// TODO: why can't this be inferred away
fn run<T>(cmd: &mut Command) -> Result<T> where T: commander::CommandOut { commander::run(cmd) }

fn probably_unique_name(length: usize) -> String {
    use rand::distributions::{IndependentSample, Range};
    let between = Range::new(0, 36);
    let mut rng = rand::thread_rng();
    unsafe {
        std::str::from_utf8_unchecked(&(0..length).map(|_| between.ind_sample(&mut rng)).map(|v| if v < 10 { v + 48 } else { v + 87 }).collect::<Vec<u8>>()).to_string()
    }
}

// Result is of the form "10.233.x"
fn unused_ip_block() -> Result<String> {
    // Can bail early anywhere here because we've got the lock
    let mut used = std::collections::HashSet::new();
    for entry in try!(std::fs::read_dir("/etc/containers")) {
        let entry = try!(entry);
        if try!(entry.file_type()).is_file() {
            let mut str = String::new();
            use std::io::Read;
            try!(try!(std::fs::File::open(entry.path())).read_to_string(&mut str));
            for line in str.lines() {
                use combine::{string, many1, choice, digit, token, eof, Parser, ParserExt};
                let ip_component = || { string("10.233.").with(many1::<String, _>(digit())).skip(token('.')).skip(many1::<String, _>(digit())) };
                if let Ok((c, _)) = choice([string("HOST_ADDRESS="), string("LOCAL_ADDRESS=")]).with(ip_component()).skip(eof()).parse(line) {
                    used.insert(c);
                }
            }
        }
    }
    (0..255).map(|p| p.to_string()).find(|s| !used.contains(s)).map(|s| format!("10.233.{}", s)).ok_or(ControlError("Out of IP addresses".to_string()))
}

fn mkpath(mode : u32, path : &str) -> Result<()> {
    use std::os::unix::fs::DirBuilderExt;
    match std::fs::DirBuilder::new().mode(mode).create(path) {
        Err(e) => if let std::io::ErrorKind::AlreadyExists = e.kind() { Ok(()) } else { Err(IoError(e)) },
        Ok(()) => Ok(())
    }
}

fn system_init() -> Result<()> {
    try!(mkpath(0o0755, "/etc/containers"));
    try!(mkpath(0o0700, "/var/lib/containers"));
    Ok(())
}

fn profile_dir(name : &str) -> String {
    format!("/nix/var/nix/profiles/per-container/{}", name)
}

fn container_root(name : &str) -> String {
    format!("/var/lib/containers/{}", name)
}

fn conf_file(name : &str) -> String {
    format!("/etc/containers/{}.conf", name)
}

fn create(config_file : &str) -> Result<String> {
    fn init_container_unsynced(name : &str) -> Result<()> {
        let ip_block = try!(unused_ip_block());
        // Use create_new to avoid clobbering existing container
        let mut f = try!(std::fs::OpenOptions::new().create_new(true).write(true).open(conf_file(name)));
        write!(&mut f, "PRIVATE_NETWORK=1\nHOST_ADDRESS={0}.1\nLOCAL_ADDRESS={0}.2\nAUTO_START=0\n", ip_block).map_err(IoError)
    }

    fn populate_container(name : &str, config_file : &str) -> Result<()> {
        use std::os::unix::fs::DirBuilderExt;
        // The per-container directory is restricted to prevent users on
        // the host from messing with guest users who happen to have the
        // same uid.
        try!(mkpath(0o0700, "/nix/var/nix/profiles/per-container"));
        let profile_dir = profile_dir(name);
        let mut dir_builder = std::fs::DirBuilder::new();
        try!(dir_builder.mode(0o0755).create(&profile_dir));

        let container_root = container_root(name);
        dir_builder.recursive(true);
        try!(dir_builder.mode(0o0755).create(format!("{}/etc/nixos", container_root)));

        // Write configuration.nix into the container fs
        let configuration_nix = format!("{}/etc/nixos/configuration.nix", container_root);
        {
            let mut f = try!(std::fs::File::create(format!("{}/etc/nixos/configuration.nix", container_root)));
            try!(write!(&mut f, "
    {{ config, lib, pkgs, ... }}:
    with lib;

    {{
        boot.isContainer = true;
        networking.hostName = mkDefault \"{}\";
        networking.useDHCP = false;
        imports = [ {} ];
    }}", name, config_file));
            // TODO: guaranteed sync at end of block?
        }

        // Build the container config
        run(Command::new("nix-env")
            .arg("-p").arg(format!("{}/system", profile_dir))
            .arg("-I").arg(format!("nixos-config={}", configuration_nix))
            .arg("-f").arg("<nixpkgs/nixos>")
            .arg("--set")
            .arg("-A").arg("system")
            .arg("--show-trace"))
    }

    let name = format!("fr{}", probably_unique_name(9));

    // Acquire lock
    use std::os::unix::io::IntoRawFd;
    let lock_fd = try!(std::fs::OpenOptions::new().create(true).append(true).open("/run/lock/nixos-container")).into_raw_fd();
    try!(nix::fcntl::flock(lock_fd, nix::fcntl::FlockArg::LockExclusive));

    let res = init_container_unsynced(&name);

    // Drop lock
    unsafe {
        let _ = nix::libc::close(lock_fd);  // If closing fails, ¯\_(ツ)_/¯
    }

    match res.and_then(|()| populate_container(&name, config_file)) {
        Ok(()) => Ok(name),
        // Don't leave a half-baked container behind
        Err(e) => { let _ = destroy(&name); Err(e) }
    }
}

fn run_test(container_name : &str, args : &[String]) -> Result<i32> {
    try!(run(Command::new("systemctl").arg("start").arg(format!("container@{}", container_name))));
    let leader_pid = try!(run(Command::new("machinectl").arg("show").arg(container_name).arg("-p").arg("Leader")).and_then(|o : String| {
        use combine::{string, many1, digit, spaces, eof, Parser, ParserExt};
        string("Leader=").with(many1::<String, _>(digit())).skip(spaces()).skip(eof()).parse(o.as_str())
            .map(|(ds, _)| ds)
            .map_err(|_| ControlError(format!("Bad machinectl output {}", o)))
    }));
    let su_cmd = args.iter().map(|s| format!("'{}'", s.replace("'", "'\\''"))).collect::<Vec<String>>().join(" ");
    match run(Command::new("nsenter")
            .arg("-t").arg(leader_pid)
            .arg("-m").arg("-u").arg("-i").arg("-n").arg("-p")
            .arg("--").arg("/var/setuid-wrappers/su").arg("root").arg("-l").arg("-c").arg(format!("exec {}", su_cmd))) {
        Ok(()) => Ok(0),
        Err(NonZero(_, code)) => Ok(code),
        Err(e) => Err(e)
    }
}

// Remove a directory while recursively unmounting all mounted filesystems within
// that directory and unmounting/removing that directory afterwards as well.
//
// Specified path shouldn't be a mountpoint.
fn safe_remove_tree(path : &str) -> Result<()> {
    if !std::path::Path::new(path).is_dir() {
        return Ok(());
    }
    try!(run(Command::new("find").arg(path)
        .arg("-mindepth").arg("1")
        .arg("-xdev")
        .arg("(")
            .arg("-type").arg("d")
            .arg("-exec").arg("mountpoint").arg("-q").arg("{}").arg(";")
        .arg(")")
        .arg("-exec").arg("umount").arg("-fR").arg("{}").arg("+")));
    try!(run(Command::new("rm").arg("--one-file-system").arg("-rf").arg(path)));

    Ok(())
}

fn stop(container_name : &str) -> Result<()> {
    match run(Command::new("systemctl").arg("stop").arg(format!("container@{}", container_name))) {
        Ok(()) => Ok(()),
        Err(Interrupted(_)) => {
            return run(Command::new("systemctl").arg("kill").arg(format!("container@{}", container_name)))
        },
        Err(e) => Err(e)
    }
}

fn destroy(container_name : &str) -> Result<()> {
    try!(stop(container_name));
    try!(safe_remove_tree(&profile_dir(container_name)));
    try!(safe_remove_tree(&format!("/nix/var/nix/gcroots/per-container/{}", container_name)));
    try!(safe_remove_tree(&container_root(container_name)));
    std::fs::remove_file(conf_file(container_name)).or_else(|e| match e.kind() {
        std::io::ErrorKind::NotFound => Ok(()),
        _ => Err(IoError(e))
    })
}

fn go() -> Result<i32> {
    // Skip program name
    let mut args = std::env::args().skip(1).peekable();

    let no_destroy = args.peek().map_or(false, |arg| arg == "--no_destroy");
    if no_destroy { args.next(); }

    let config_file = try!(args.next().ok_or(Error::Usage("Missing config file argument".to_string())));

    let test_args : Vec<String> = args.collect();
    if test_args.len() == 0 {
        return Err(Usage("Missing test args".to_string()));
    }

    let config_file = {
        let p = try!(std::fs::canonicalize(config_file).map_err(|e| Usage(format!("For config file: {}", e))));
        try!(p.to_str().ok_or(Usage("Config file path is invalid UTF-8".to_string()))).to_string()
    };

    try!(system_init());
    // We suppress signals in this process and allow the commander module to deal with child processes
    let trap = signal::trap::Trap::trap(&[SIGTERM, SIGINT]);
    let container_name = try!(create(&config_file));
    let res = match trap.wait(std::time::Instant::now()) {
        None => run_test(&container_name, &test_args),
        Some(n) => Err(Interrupted(n))
    };

    if no_destroy {
        try!(stop(&container_name));
    } else {
        try!(destroy(&container_name));
    }

    res
}

fn main() {
    std::process::exit(go().unwrap_or_else(|e| match e {
        NonZero(cmd, code) => { writeln!(io::stderr(), "Command {:?} failed with code {:?}", cmd, code).unwrap(); 2 },
        Usage(msg) => { writeln!(io::stderr(), "Usage error: {}", msg).unwrap(); 1 },
        IoError(err) => { writeln!(io::stderr(), "{}", err).unwrap(); 2 },
        NixError(err) => { writeln!(io::stderr(), "{}", err).unwrap(); 2 },
        ControlError(msg) => { writeln!(io::stderr(), "{}", msg).unwrap(); 2 },
        Interrupted(SIGINT) => { kill(nix::unistd::getpid(), SIGINT).unwrap(); loop {} }
        Interrupted(n) => 128 + n
    }));
}
<commit_msg>Ensure all safe_remove_tree calls are executed and log errors<commit_after>extern crate combine;
extern crate nix; // As in *nix
extern crate rand;
extern crate signal;

mod commander;

// Reference: https://github.com/NixOS/nixpkgs/blob/b07051ce6c3ba3039c89b6755da279002b0c3ace/nixos/modules/virtualisation/nixos-container.pl

use nix::sys::signal::{kill, SIGTERM, SIGINT};
use std::io;
use std::io::Write;
use std::process::Command;

#[derive(Debug)]
enum Error {
    Usage(String),
    NonZero(String, i32),
    ControlError(String),
    IoError(io::Error),
    NixError(nix::Error),
    Interrupted(commander::Signal)
}

use Error::*;

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Self { IoError(e) }
}

impl From<nix::Error> for Error {
    fn from(e: nix::Error) -> Self { NixError(e) }
}

impl From<commander::NonZero> for Error {
    fn from(e: commander::NonZero) -> Self { match e { commander::NonZero::NonZero(s, o) => NonZero(s, o), commander::NonZero::Interrupted(n) => Interrupted(n) } }
}

type Result<T> = std::result::Result<T, Error>;

// TODO: why can't this be inferred away
fn run<T>(cmd: &mut Command) -> Result<T> where T: commander::CommandOut { commander::run(cmd) }

fn probably_unique_name(length: usize) -> String {
    use rand::distributions::{IndependentSample, Range};
    let between = Range::new(0, 36);
    let mut rng = rand::thread_rng();
    unsafe {
        std::str::from_utf8_unchecked(&(0..length).map(|_| between.ind_sample(&mut rng)).map(|v| if v < 10 { v + 48 } else { v + 87 }).collect::<Vec<u8>>()).to_string()
    }
}

// Result is of the form "10.233.x"
fn unused_ip_block() -> Result<String> {
    // Can bail early anywhere here because we've got the lock
    let mut used = std::collections::HashSet::new();
    for entry in try!(std::fs::read_dir("/etc/containers")) {
        let entry = try!(entry);
        if try!(entry.file_type()).is_file() {
            let mut str = String::new();
            use std::io::Read;
            try!(try!(std::fs::File::open(entry.path())).read_to_string(&mut str));
            for line in str.lines() {
                use combine::{string, many1, choice, digit, token, eof, Parser, ParserExt};
                let ip_component = || { string("10.233.").with(many1::<String, _>(digit())).skip(token('.')).skip(many1::<String, _>(digit())) };
                if let Ok((c, _)) = choice([string("HOST_ADDRESS="), string("LOCAL_ADDRESS=")]).with(ip_component()).skip(eof()).parse(line) {
                    used.insert(c);
                }
            }
        }
    }
    (0..255).map(|p| p.to_string()).find(|s| !used.contains(s)).map(|s| format!("10.233.{}", s)).ok_or(ControlError("Out of IP addresses".to_string()))
}

fn mkpath(mode : u32, path : &str) -> Result<()> {
    use std::os::unix::fs::DirBuilderExt;
    match std::fs::DirBuilder::new().mode(mode).create(path) {
        Err(e) => if let std::io::ErrorKind::AlreadyExists = e.kind() { Ok(()) } else { Err(IoError(e)) },
        Ok(()) => Ok(())
    }
}

fn system_init() -> Result<()> {
    try!(mkpath(0o0755, "/etc/containers"));
    try!(mkpath(0o0700, "/var/lib/containers"));
    Ok(())
}

fn profile_dir(name : &str) -> String {
    format!("/nix/var/nix/profiles/per-container/{}", name)
}

fn container_root(name : &str) -> String {
    format!("/var/lib/containers/{}", name)
}

fn conf_file(name : &str) -> String {
    format!("/etc/containers/{}.conf", name)
}

fn create(config_file : &str) -> Result<String> {
    fn init_container_unsynced(name : &str) -> Result<()> {
        let ip_block = try!(unused_ip_block());
        // Use create_new to avoid clobbering existing container
        let mut f = try!(std::fs::OpenOptions::new().create_new(true).write(true).open(conf_file(name)));
        write!(&mut f, "PRIVATE_NETWORK=1\nHOST_ADDRESS={0}.1\nLOCAL_ADDRESS={0}.2\nAUTO_START=0\n", ip_block).map_err(IoError)
    }

    fn populate_container(name : &str, config_file : &str) -> Result<()> {
        use std::os::unix::fs::DirBuilderExt;
        // The per-container directory is restricted to prevent users on
        // the host from messing with guest users who happen to have the
        // same uid.
        try!(mkpath(0o0700, "/nix/var/nix/profiles/per-container"));
        let profile_dir = profile_dir(name);
        let mut dir_builder = std::fs::DirBuilder::new();
        try!(dir_builder.mode(0o0755).create(&profile_dir));

        let container_root = container_root(name);
        dir_builder.recursive(true);
        try!(dir_builder.mode(0o0755).create(format!("{}/etc/nixos", container_root)));

        // Write configuration.nix into the container fs
        let configuration_nix = format!("{}/etc/nixos/configuration.nix", container_root);
        {
            let mut f = try!(std::fs::File::create(format!("{}/etc/nixos/configuration.nix", container_root)));
            try!(write!(&mut f, "
    {{ config, lib, pkgs, ... }}:
    with lib;

    {{
        boot.isContainer = true;
        networking.hostName = mkDefault \"{}\";
        networking.useDHCP = false;
        imports = [ {} ];
    }}", name, config_file));
            // TODO: guaranteed sync at end of block?
        }

        // Build the container config
        run(Command::new("nix-env")
            .arg("-p").arg(format!("{}/system", profile_dir))
            .arg("-I").arg(format!("nixos-config={}", configuration_nix))
            .arg("-f").arg("<nixpkgs/nixos>")
            .arg("--set")
            .arg("-A").arg("system")
            .arg("--show-trace"))
    }

    let name = format!("fr{}", probably_unique_name(9));

    // Acquire lock
    use std::os::unix::io::IntoRawFd;
    let lock_fd = try!(std::fs::OpenOptions::new().create(true).append(true).open("/run/lock/nixos-container")).into_raw_fd();
    try!(nix::fcntl::flock(lock_fd, nix::fcntl::FlockArg::LockExclusive));

    let res = init_container_unsynced(&name);

    // Drop lock
    unsafe {
        let _ = nix::libc::close(lock_fd);  // If closing fails, ¯\_(ツ)_/¯
    }

    match res.and_then(|()| populate_container(&name, config_file)) {
        Ok(()) => Ok(name),
        // Don't leave a half-baked container behind
        Err(e) => { let _ = destroy(&name); Err(e) }
    }
}

fn run_test(container_name : &str, args : &[String]) -> Result<i32> {
    try!(run(Command::new("systemctl").arg("start").arg(format!("container@{}", container_name))));
    let leader_pid = try!(run(Command::new("machinectl").arg("show").arg(container_name).arg("-p").arg("Leader")).and_then(|o : String| {
        use combine::{string, many1, digit, spaces, eof, Parser, ParserExt};
        string("Leader=").with(many1::<String, _>(digit())).skip(spaces()).skip(eof()).parse(o.as_str())
            .map(|(ds, _)| ds)
            .map_err(|_| ControlError(format!("Bad machinectl output {}", o)))
    }));
    let su_cmd = args.iter().map(|s| format!("'{}'", s.replace("'", "'\\''"))).collect::<Vec<String>>().join(" ");
    match run(Command::new("nsenter")
            .arg("-t").arg(leader_pid)
            .arg("-m").arg("-u").arg("-i").arg("-n").arg("-p")
            .arg("--").arg("/var/setuid-wrappers/su").arg("root").arg("-l").arg("-c").arg(format!("exec {}", su_cmd))) {
        Ok(()) => Ok(0),
        Err(NonZero(_, code)) => Ok(code),
        Err(e) => Err(e)
    }
}

// Remove a directory while recursively unmounting all mounted filesystems within
// that directory and unmounting/removing that directory afterwards as well.
//
// Specified path shouldn't be a mountpoint.
fn safe_remove_tree(path : &str) -> Result<()> {
    if !std::path::Path::new(path).is_dir() {
        return Ok(());
    }
    try!(run(Command::new("find").arg(path)
        .arg("-mindepth").arg("1")
        .arg("-xdev")
        .arg("(")
            .arg("-type").arg("d")
            .arg("-exec").arg("mountpoint").arg("-q").arg("{}").arg(";")
        .arg(")")
        .arg("-exec").arg("umount").arg("-fR").arg("{}").arg("+")));
    try!(run(Command::new("rm").arg("--one-file-system").arg("-rf").arg(path)));

    Ok(())
}

fn stop(container_name : &str) -> Result<()> {
    match run(Command::new("systemctl").arg("stop").arg(format!("container@{}", container_name))) {
        Ok(()) => Ok(()),
        Err(Interrupted(_)) => {
            return run(Command::new("systemctl").arg("kill").arg(format!("container@{}", container_name)))
        },
        Err(e) => Err(e)
    }
}

fn destroy(container_name : &str) -> Result<()> {
    fn log_err(e: Error) {
        println!("Error while destroying container: {:?}", e);
    }

    safe_remove_tree(&profile_dir(container_name)).unwrap_or_else(log_err);
    safe_remove_tree(&format!("/nix/var/nix/gcroots/per-container/{}", container_name)).unwrap_or_else(log_err);
    safe_remove_tree(&container_root(container_name)).unwrap_or_else(log_err);

    std::fs::remove_file(conf_file(container_name)).or_else(|e| match e.kind() {
        std::io::ErrorKind::NotFound => Ok(()),
        _ => Err(IoError(e))
    })
}

fn go() -> Result<i32> {
    // Skip program name
    let mut args = std::env::args().skip(1).peekable();

    let no_destroy = args.peek().map_or(false, |arg| arg == "--no_destroy");
    if no_destroy { args.next(); }

    let config_file = try!(args.next().ok_or(Error::Usage("Missing config file argument".to_string())));

    let test_args : Vec<String> = args.collect();
    if test_args.len() == 0 {
        return Err(Usage("Missing test args".to_string()));
    }

    let config_file = {
        let p = try!(std::fs::canonicalize(config_file).map_err(|e| Usage(format!("For config file: {}", e))));
        try!(p.to_str().ok_or(Usage("Config file path is invalid UTF-8".to_string()))).to_string()
    };

    try!(system_init());
    // We suppress signals in this process and allow the commander module to deal with child processes
    let trap = signal::trap::Trap::trap(&[SIGTERM, SIGINT]);
    let container_name = try!(create(&config_file));
    let res = match trap.wait(std::time::Instant::now()) {
        None => run_test(&container_name, &test_args),
        Some(n) => Err(Interrupted(n))
    };

    if no_destroy {
        try!(stop(&container_name));
    } else {
        try!(destroy(&container_name));
    }

    res
}

fn main() {
    std::process::exit(go().unwrap_or_else(|e| match e {
        NonZero(cmd, code) => { writeln!(io::stderr(), "Command {:?} failed with code {:?}", cmd, code).unwrap(); 2 },
        Usage(msg) => { writeln!(io::stderr(), "Usage error: {}", msg).unwrap(); 1 },
        IoError(err) => { writeln!(io::stderr(), "{}", err).unwrap(); 2 },
        NixError(err) => { writeln!(io::stderr(), "{}", err).unwrap(); 2 },
        ControlError(msg) => { writeln!(io::stderr(), "{}", msg).unwrap(); 2 },
        Interrupted(SIGINT) => { kill(nix::unistd::getpid(), SIGINT).unwrap(); loop {} }
        Interrupted(n) => 128 + n
    }));
}
<|endoftext|>


<commit_before>
#[derive(Debug, PartialEq)]
enum Token {
    Integer(i32),
    Star, Slash,
    Plus, Minus
}

#[derive(Debug)]
struct Lexer {
    source: Vec<char>,
    position: usize,
}

impl Lexer {
    fn new(source: String) -> Lexer {
        Lexer {
            source: source.chars().collect(),
            position: 0,
        }
    }

    fn done(&self) -> bool {
        self.position >= self.source.len()
    }

    fn advance(&mut self) {
        self.position += 1;
    }

    fn string(&self, start: usize, end: usize) -> String {
        (&self.source[start..end]).to_vec().into_iter().collect()
    }

    fn next_char_is<P>(&self, pred: P) -> bool where P: Fn(char) -> bool {
        if self.position + 1 < self.source.len() {
            pred(self.source[self.position + 1])
        } else {
            false
        }
    }

    fn char(&mut self) -> Option<char> {
        if !self.done() {
            Some(self.source[self.position])
        } else {
            None
        }
    }

    fn skip_whitespace(&mut self) {
        while !self.done() && self.source[self.position].is_whitespace() {
            self.advance();
        }
    }

    fn scan_integer(&mut self) -> Token {
        let start = self.position;
        while self.next_char_is(|c| c.is_digit(10)) {
            self.advance();
        }
        Token::Integer(self.string(start, self.position + 1).parse::<i32>().unwrap())
    }

    fn get_next_token(&mut self) -> Option<Token> {
        self.skip_whitespace();
        if self.done() { return None; }
        let token = match self.char().unwrap() {
            '0' ... '9' => self.scan_integer(),
            '+'         => Token::Plus,
            '-'         => Token::Minus,
            '*'         => Token::Star,
            '/'         => Token::Slash,
             c          => panic!("Error: unexpected character: '{}'.", c),
        };
        self.advance();
        Some(token)
    }
}

#[derive(Debug)]
struct Interpreter {
    lexer: Lexer,
    current_token: Option<Token>,
}

impl Interpreter {
    fn new(source: String) -> Interpreter {
        let mut l = Lexer::new(source);
        let t = l.get_next_token();
        Interpreter {
            lexer: l,
            current_token: t,
        }
    }

    fn advance(&mut self) {
        self.current_token = self.lexer.get_next_token();
    }

    fn factor(&mut self) -> i32 {
        let result = match self.current_token {
            Some(Token::Integer(i)) => i,
            ref t => panic!("Error: expected an integer and found a '{:?}'.", t),
        };
        self.advance();
        result
    }

    fn term(&mut self) -> i32 {
        let mut result = self.factor();

        loop {
            match self.current_token {
                Some(Token::Star)  => { self.advance(); result *= self.factor(); },
                Some(Token::Slash) => { self.advance(); result /= self.factor(); },
                _ => break,
            }
        }
        result
    }

    fn expression(&mut self) -> i32 {
        let mut result = self.term();

        loop {
            match self.current_token {
                Some(Token::Plus)  => { self.advance(); result += self.term() },
                Some(Token::Minus) => { self.advance(); result -= self.term() },
                _ => break,
            }
        }
        result
    }
}

macro_rules! prompt(
    ($($arg:tt)*) => { {
        print!($($arg)*);
        stdout().flush().expect("Could not flush stdout");
    } }
);

fn main() {
    use std::io::{BufRead, stdin, stdout, Write};

    prompt!("calc> ");
    let stdin = stdin();
    for line in stdin.lock().lines() {
        let mut interpreter = Interpreter::new(line.unwrap());
        let result = interpreter.expression();
        println!("{}", result);
        prompt!("calc> ");
    }
}
<commit_msg>Finished Part 6.<commit_after>
#[derive(Debug)]
enum Token {
    Integer(i32),
    Star, Slash,
    Plus, Minus,
    LParen, RParen
}

#[derive(Debug)]
struct Lexer {
    source: Vec<char>,
    position: usize,
}

impl Lexer {
    fn new(source: String) -> Lexer {
        Lexer {
            source: source.chars().collect(),
            position: 0,
        }
    }

    fn done(&self) -> bool {
        self.position >= self.source.len()
    }

    fn advance(&mut self) {
        self.position += 1;
    }

    fn string(&self, start: usize, end: usize) -> String {
        (&self.source[start..end]).to_vec().into_iter().collect()
    }

    fn next_char_is<P>(&self, pred: P) -> bool where P: Fn(char) -> bool {
        if self.position + 1 < self.source.len() {
            pred(self.source[self.position + 1])
        } else {
            false
        }
    }

    fn char(&mut self) -> Option<char> {
        if !self.done() {
            Some(self.source[self.position])
        } else {
            None
        }
    }

    fn skip_whitespace(&mut self) {
        while !self.done() && self.source[self.position].is_whitespace() {
            self.advance();
        }
    }

    fn scan_integer(&mut self) -> Token {
        let start = self.position;
        while self.next_char_is(|c| c.is_digit(10)) {
            self.advance();
        }
        Token::Integer(self.string(start, self.position + 1).parse::<i32>().unwrap())
    }

    fn get_next_token(&mut self) -> Option<Token> {
        self.skip_whitespace();
        if self.done() { return None; }
        let token = match self.char().unwrap() {
            '0' ... '9' => self.scan_integer(),
            '+'         => Token::Plus,
            '-'         => Token::Minus,
            '*'         => Token::Star,
            '/'         => Token::Slash,
            '('         => Token::LParen,
            ')'         => Token::RParen,
             c          => panic!("Error: unexpected character: '{}'.", c),
        };
        self.advance();
        Some(token)
    }
}

#[derive(Debug)]
struct Interpreter {
    lexer: Lexer,
    current_token: Option<Token>,
}

impl Interpreter {
    fn new(source: String) -> Interpreter {
        let mut l = Lexer::new(source);
        let t = l.get_next_token();
        Interpreter {
            lexer: l,
            current_token: t,
        }
    }

    fn advance(&mut self) {
        self.current_token = self.lexer.get_next_token();
    }

    fn factor(&mut self) -> i32 {
        let result = match self.current_token {
            Some(Token::Integer(i)) => i,
            Some(Token::LParen) => {
                self.advance();
                let paren_result = self.expression();
                match self.current_token {
                    Some(Token::RParen) => paren_result,
                    ref t => panic!("Error: expected an RParen found a {:?}", t),
                }
            }
            ref t => panic!("Error: expected an integer and found a '{:?}'.", t),
        };
        self.advance();
        result
    }

    fn term(&mut self) -> i32 {
        let mut result = self.factor();

        loop {
            match self.current_token {
                Some(Token::Star)  => { self.advance(); result *= self.factor(); },
                Some(Token::Slash) => { self.advance(); result /= self.factor(); },
                _ => break,
            }
        }
        result
    }

    fn expression(&mut self) -> i32 {
        let mut result = self.term();

        loop {
            match self.current_token {
                Some(Token::Plus)  => { self.advance(); result += self.term() },
                Some(Token::Minus) => { self.advance(); result -= self.term() },
                _ => break,
            }
        }
        result
    }
}

macro_rules! prompt(
    ($($arg:tt)*) => { {
        print!($($arg)*);
        stdout().flush().expect("Could not flush stdout");
    } }
);

fn main() {
    use std::io::{BufRead, stdin, stdout, Write};

    prompt!("calc> ");
    let stdin = stdin();
    for line in stdin.lock().lines() {
        let mut interpreter = Interpreter::new(line.unwrap());
        let result = interpreter.expression();
        println!("{}", result);
        prompt!("calc> ");
    }
}
<|endoftext|>


<commit_before>use glium;
use glutin;
use std;

use scene;

#[repr(C)]
#[derive(Clone, Copy)]
pub struct RGB {
  pub r: f32,
  pub g: f32,
  pub b: f32,
}

unsafe impl Send for RGB {}

unsafe impl glium::texture::PixelValue for RGB {
  fn get_format() -> glium::texture::ClientFormat {
    glium::texture::ClientFormat::F32F32F32
  }
}

pub const WINDOW_WIDTH: u32 = 800;
pub const WINDOW_HEIGHT: u32 = 600;

pub fn main() {
  use glium::DisplayBuild;

  let window =
    glutin::WindowBuilder::new()
    .with_dimensions(WINDOW_WIDTH, WINDOW_HEIGHT)
    .build_glium()
    .unwrap();

  let mut image_data = vec!();
  for y in 0 .. WINDOW_HEIGHT {
    let mut row = vec!();
    for x in 0 .. WINDOW_WIDTH {
      row.push(
        RGB {
          r: x as f32 / WINDOW_WIDTH as f32,
          g: y as f32 / WINDOW_HEIGHT as f32,
          b: 0.0,
        }
      );
    }
    image_data.push(row);
  }

  let scene =
    scene::T {
      objects       :
        vec!(
          scene::Object { center: [ -1.0,  0.0, -4.0], radius:   1.0, color: [1.0 , 0.0 , 0.0 ], emittance: 0.0, reflectance: 1.0, transmittance: 0.0, diffuseness: 1.0  },
          scene::Object { center: [  3.0, -1.0, -6.0], radius:   2.0, color: [0.8 , 0.8 , 0.8 ], emittance: 0.0, reflectance: 1.0, transmittance: 0.0, diffuseness: 0.01 },
          scene::Object { center: [ 30.0, 10.0,  4.0], radius:  10.0, color: [1.0 , 1.0 , 1.0 ], emittance: 1.0, reflectance: 0.0, transmittance: 0.4, diffuseness: 1.0  },
          scene::Object { center: [ -1.0,  0.2, -2.0], radius:   0.4, color: [0.9 , 0.9 , 1.0 ], emittance: 0.0, reflectance: 0.1, transmittance: 0.8, diffuseness: 0.01 },
          scene::Object { center: [  0.0,  0.0,  0.0], radius: 100.0, color: [0.02, 0.02, 0.02], emittance: 1.0, reflectance: 0.0, transmittance: 0.0, diffuseness: 0.0  },
        ),
      fovy          : std::f32::consts::FRAC_PI_2,
      eye           : [0.0, 0.0,  0.0],
      look          : [0.0, 0.0, -1.0],
      up            : [0.0, 1.0,  0.0],
    };

  let mut rendered_2d = vec!();
  {
    let scale = 1 << 1;
    let w = scale * WINDOW_WIDTH;
    let h = scale * WINDOW_HEIGHT;
    let rendered = scene.render(w, h);
    for y in 0 .. h as usize {
      let mut row = vec!();
      for x in 0 .. w as usize {
        row.push(rendered[y * w as usize + x]);
      }
      rendered_2d.push(row);
    }
  }

  let texture = glium::texture::Texture2d::new(&window, rendered_2d).unwrap();

  // building the vertex buffer, which contains all the vertices that we will draw
  let vertex_buffer = {
    #[derive(Copy, Clone)]
    struct Vertex {
      position: [f32; 2],
      tex_coords: [f32; 2],
    }

    implement_vertex!(Vertex, position, tex_coords);

    glium::VertexBuffer::new(&window,
      &[
        Vertex { position: [-1.0, -1.0], tex_coords: [0.0, 0.0] },
        Vertex { position: [-1.0,  1.0], tex_coords: [0.0, 1.0] },
        Vertex { position: [ 1.0,  1.0], tex_coords: [1.0, 1.0] },
        Vertex { position: [ 1.0, -1.0], tex_coords: [1.0, 0.0] }
      ]
    ).unwrap()
  };

  // building the index buffer
  let index_buffer =
    glium::IndexBuffer::new(
      &window,
      glium::index::PrimitiveType::TriangleStrip,
      &[1 as u16, 2, 0, 3],
    ).unwrap();

  // compiling shaders and linking them together
  let program = program!(&window,
    330 => {
      vertex: "
        #version 330

        uniform mat4 matrix;

        in vec2 position;
        in vec2 tex_coords;

        out vec2 v_tex_coords;

        void main() {
          gl_Position = matrix * vec4(position, 0.0, 1.0);
          v_tex_coords = tex_coords;
        }
      ",

      fragment: "
        #version 330
        uniform sampler2D tex;
        in vec2 v_tex_coords;
        out vec4 f_color;

        void main() {
          f_color = texture(tex, v_tex_coords);
        }
      "
    },
  ).unwrap();

  loop {
    use glium::Surface;

    // building the uniforms
    let uniforms = uniform! {
      matrix: [
        [1.0, 0.0, 0.0, 0.0],
        [0.0, 1.0, 0.0, 0.0],
        [0.0, 0.0, 1.0, 0.0],
        [0.0, 0.0, 0.0, 1.0f32]
      ],
      tex: &texture,
    };

    let mut target = window.draw();
    target.clear_color(0.0, 0.0, 0.0, 0.0);
    target.draw(&vertex_buffer, &index_buffer, &program, &uniforms, &Default::default()).unwrap();
    target.finish().unwrap();

    for event in window.poll_events() {
      if let glutin::Event::Closed = event {
        return
      }
    }
  }
}
<commit_msg>render timing<commit_after>use glium;
use glutin;
use std;
use time;

use scene;

#[repr(C)]
#[derive(Clone, Copy)]
pub struct RGB {
  pub r: f32,
  pub g: f32,
  pub b: f32,
}

unsafe impl Send for RGB {}

unsafe impl glium::texture::PixelValue for RGB {
  fn get_format() -> glium::texture::ClientFormat {
    glium::texture::ClientFormat::F32F32F32
  }
}

pub const WINDOW_WIDTH: u32 = 800;
pub const WINDOW_HEIGHT: u32 = 600;

pub fn main() {
  use glium::DisplayBuild;

  let window =
    glutin::WindowBuilder::new()
    .with_dimensions(WINDOW_WIDTH, WINDOW_HEIGHT)
    .build_glium()
    .unwrap();

  let mut image_data = vec!();
  for y in 0 .. WINDOW_HEIGHT {
    let mut row = vec!();
    for x in 0 .. WINDOW_WIDTH {
      row.push(
        RGB {
          r: x as f32 / WINDOW_WIDTH as f32,
          g: y as f32 / WINDOW_HEIGHT as f32,
          b: 0.0,
        }
      );
    }
    image_data.push(row);
  }

  let scene =
    scene::T {
      objects       :
        vec!(
          scene::Object { center: [ -1.0,  0.0, -4.0], radius:   1.0, color: [1.0 , 0.0 , 0.0 ], emittance: 0.0, reflectance: 1.0, transmittance: 0.0, diffuseness: 1.0  },
          scene::Object { center: [  3.0, -1.0, -6.0], radius:   2.0, color: [0.8 , 0.8 , 0.8 ], emittance: 0.0, reflectance: 1.0, transmittance: 0.0, diffuseness: 0.01 },
          scene::Object { center: [ 30.0, 10.0,  4.0], radius:  10.0, color: [1.0 , 1.0 , 1.0 ], emittance: 1.0, reflectance: 0.0, transmittance: 0.4, diffuseness: 1.0  },
          scene::Object { center: [ -1.0,  0.2, -2.0], radius:   0.4, color: [0.9 , 0.9 , 1.0 ], emittance: 0.0, reflectance: 0.1, transmittance: 0.8, diffuseness: 0.01 },
          scene::Object { center: [  0.0,  0.0,  0.0], radius: 100.0, color: [0.02, 0.02, 0.02], emittance: 1.0, reflectance: 0.0, transmittance: 0.0, diffuseness: 0.0  },
        ),
      fovy          : std::f32::consts::FRAC_PI_2,
      eye           : [0.0, 0.0,  0.0],
      look          : [0.0, 0.0, -1.0],
      up            : [0.0, 1.0,  0.0],
    };

  let mut rendered_2d = vec!();
  {
    let scale = 1 << 0;
    let w = scale * WINDOW_WIDTH;
    let h = scale * WINDOW_HEIGHT;

    let before = time::precise_time_ns();
    let rendered = scene.render(w, h);
    let after = time::precise_time_ns();
    println!("Render took {:?}ms", (after - before) as f32 / 1_000_000.0);

    for y in 0 .. h as usize {
      let mut row = vec!();
      for x in 0 .. w as usize {
        row.push(rendered[y * w as usize + x]);
      }
      rendered_2d.push(row);
    }
  }

  let texture = glium::texture::Texture2d::new(&window, rendered_2d).unwrap();

  // building the vertex buffer, which contains all the vertices that we will draw
  let vertex_buffer = {
    #[derive(Copy, Clone)]
    struct Vertex {
      position: [f32; 2],
      tex_coords: [f32; 2],
    }

    implement_vertex!(Vertex, position, tex_coords);

    glium::VertexBuffer::new(&window,
      &[
        Vertex { position: [-1.0, -1.0], tex_coords: [0.0, 0.0] },
        Vertex { position: [-1.0,  1.0], tex_coords: [0.0, 1.0] },
        Vertex { position: [ 1.0,  1.0], tex_coords: [1.0, 1.0] },
        Vertex { position: [ 1.0, -1.0], tex_coords: [1.0, 0.0] }
      ]
    ).unwrap()
  };

  // building the index buffer
  let index_buffer =
    glium::IndexBuffer::new(
      &window,
      glium::index::PrimitiveType::TriangleStrip,
      &[1 as u16, 2, 0, 3],
    ).unwrap();

  // compiling shaders and linking them together
  let program = program!(&window,
    330 => {
      vertex: "
        #version 330

        uniform mat4 matrix;

        in vec2 position;
        in vec2 tex_coords;

        out vec2 v_tex_coords;

        void main() {
          gl_Position = matrix * vec4(position, 0.0, 1.0);
          v_tex_coords = tex_coords;
        }
      ",

      fragment: "
        #version 330
        uniform sampler2D tex;
        in vec2 v_tex_coords;
        out vec4 f_color;

        void main() {
          f_color = texture(tex, v_tex_coords);
        }
      "
    },
  ).unwrap();

  loop {
    use glium::Surface;

    // building the uniforms
    let uniforms = uniform! {
      matrix: [
        [1.0, 0.0, 0.0, 0.0],
        [0.0, 1.0, 0.0, 0.0],
        [0.0, 0.0, 1.0, 0.0],
        [0.0, 0.0, 0.0, 1.0f32]
      ],
      tex: &texture,
    };

    let mut target = window.draw();
    target.clear_color(0.0, 0.0, 0.0, 0.0);
    target.draw(&vertex_buffer, &index_buffer, &program, &uniforms, &Default::default()).unwrap();
    target.finish().unwrap();

    for event in window.poll_events() {
      if let glutin::Event::Closed = event {
        return
      }
    }
  }
}
<|endoftext|>