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

#![allow(dead_code, unused_imports)]

use client::connect_with_retry;
use commands::{
    DEFAULT_PORT,
    request_stats,
    which,
};
use env_logger;
use log::LogLevel::Trace;
use std::collections::HashMap;
use std::env;
use std::ffi::{OsStr,OsString};
use std::fs;
use std::io::{
    self,
    Write,
};
use std::path::{Path,PathBuf};
use std::process::{
    Command,
    Output,
    Stdio,
};
use tempdir::TempDir;
use test::utils::*;

// Test GCC + clang on non-OS X platforms.
#[cfg(all(unix, not(target_os="macos")))]
const COMPILERS: &'static [&'static str] = &["gcc", "clang"];

// OS X ships a `gcc` that's just a clang wrapper, so only test clang there.
#[cfg(target_os="macos")]
const COMPILERS: &'static [&'static str] = &["clang"];

// Test MSVC when targeting MSVC.
#[cfg(target_env="msvc")]
const COMPILERS: &'static[&'static str] = &["cl.exe"];

//TODO: could test gcc when targeting mingw.

fn do_run<T: AsRef<OsStr>>(exe: &Path, args: &[T], cwd: &Path) -> Output {
    let mut cmd = Command::new(exe);
    cmd.args(args)
        .current_dir(cwd)
        .stdout(Stdio::piped())
        .stderr(Stdio::piped());
    trace!("do_run: {:?}", cmd);
    cmd.spawn()
        .unwrap_or_else(|e| { panic!("failed to execute child: {}", e) })
        .wait_with_output()
        .unwrap()
}

fn run<T: AsRef<OsStr>>(exe: &Path, args: &[T], cwd: &Path) -> bool {
    let output = do_run(exe, args, cwd);
    if output.status.success() {
        true
    } else {
        let va = args.iter().map(|a| a.as_ref().to_str().unwrap()).collect::<Vec<_>>();
        println!("Process `{:?} {}` failed:", exe, va.join(" "));
        print!("stdout: `");
        io::stdout().write(&output.stdout).unwrap();
        println!("`");
        print!("stderr: `");
        io::stdout().write(&output.stderr).unwrap();
        println!("`");
        false
    }
}

macro_rules! vec_from {
    ( $t:ty, $( $x:expr ),* ) => {
        vec!($( Into::<$t>::into(&$x), )*)
    };
}

fn compile_cmdline<T: AsRef<OsStr>>(compiler: &str, exe: T, input: &str, output: &str) -> Vec<OsString> {
    match compiler {
        "gcc" | "clang" => vec_from!(OsString, exe.as_ref(), "-c", input, "-o", output),
        "cl.exe" => vec_from!(OsString, exe, "-c", input, format!("-Fo{}", output)),
        _ => panic!("Unsupported compiler: {}", compiler),
    }
}

fn print_stats(stats: &HashMap<String, CacheStat>) {
    for (k, v) in stats.iter() {
        trace!("  {}: {:?}", k, v);
    }
}

fn run_sccache_command_test<T: AsRef<OsStr>>(sccache: &Path, compiler: &str, exe: T, tempdir: &Path) {
    // Ensure there's no existing sccache server running.
    trace!("stop server");
    do_run(sccache, &["--stop-server"], tempdir);
    // Create a subdir for the cache.
    let cache = tempdir.join("cache");
    fs::create_dir_all(&cache).unwrap();
    // Start a server.
    trace!("start server");
    // Don't run this with run() because on Windows `wait_with_output`
    // will hang because the internal server process is not detached.
    assert!(Command::new(sccache)
            .arg("--start-server")
            .current_dir(tempdir)
            .env("SCCACHE_DIR", &cache)
            .status()
            .unwrap()
            .success());
    trace!("run_sccache_command_test: {}", compiler);
    // Compile a source file.
    let original_source_file = Path::new(file!()).parent().unwrap().join("test.c");
    // Copy the source file into the tempdir so we can compile with relative paths, since the commandline winds up in the hash key.
    let source_file = tempdir.join("test.c");
    trace!("fs::copy({:?}, {:?})", original_source_file, source_file);
    fs::copy(&original_source_file, &source_file).unwrap();
    let out_file = tempdir.join("test.o");
    let input = source_file.file_name().unwrap().to_str().unwrap();
    let output = out_file.file_name().unwrap().to_str().unwrap();
    trace!("compile");
    assert_eq!(true, run(sccache, &compile_cmdline(compiler, exe.as_ref(), &input, &output), tempdir));
    assert_eq!(true, fs::metadata(&out_file).and_then(|m| Ok(m.len() > 0)).unwrap());
    trace!("connect");
    let conn = connect_with_retry(DEFAULT_PORT).unwrap();
    trace!("request stats");
    let stats = cache_stats_map(request_stats(conn).unwrap());
    if log_enabled!(Trace) {
        trace!("stats:");
        print_stats(&stats);
    }
    assert_eq!(&CacheStat::Count(1), stats.get("Compile requests").unwrap());
    assert_eq!(&CacheStat::Count(1), stats.get("Compile requests executed").unwrap());
    assert_eq!(&CacheStat::Count(0), stats.get("Cache hits").unwrap());
    assert_eq!(&CacheStat::Count(1), stats.get("Cache misses").unwrap());
    trace!("compile");
    fs::remove_file(&out_file).unwrap();
    assert_eq!(true, run(sccache, &compile_cmdline(compiler, exe.as_ref(), &input, &output), tempdir));
    assert_eq!(true, fs::metadata(&out_file).and_then(|m| Ok(m.len() > 0)).unwrap());
    trace!("connect");
    let conn = connect_with_retry(DEFAULT_PORT).unwrap();
    trace!("request stats");
    let stats = cache_stats_map(request_stats(conn).unwrap());
    if log_enabled!(Trace) {
        trace!("stats:");
        print_stats(&stats);
    }
    assert_eq!(&CacheStat::Count(2), stats.get("Compile requests").unwrap());
    assert_eq!(&CacheStat::Count(2), stats.get("Compile requests executed").unwrap());
    assert_eq!(&CacheStat::Count(1), stats.get("Cache hits").unwrap());
    assert_eq!(&CacheStat::Count(1), stats.get("Cache misses").unwrap());
    trace!("stop server");
    assert_eq!(true, run(sccache, &["--stop-server"], tempdir));
}

fn find_compilers() -> Vec<(&'static str, OsString)> {
    let cwd = env::current_dir().unwrap();
    COMPILERS.iter()
        .filter_map(|c| {
            match which(c, env::var_os("PATH"), &cwd) {
                Some(full_path) => Some((*c, full_path)),
                None => None,
            }
        })
        .collect::<Vec<_>>()
}

#[test]
#[cfg(any(unix, target_env="msvc"))]
fn test_sccache_command() {
    match env_logger::init() {
        Ok(_) => {},
        Err(_) => {},
    }
    let tempdir = TempDir::new("sccache_system_test").unwrap();
    let sccache = env::current_exe().unwrap().parent().unwrap().join("sccache").with_extension(env::consts::EXE_EXTENSION);
    match fs::metadata(&sccache) {
        Ok(_) => {},
        Err(_) => panic!("Error: sccache binary not found at `{:?}. Do you need to run `cargo build`?", sccache),
    }
    let compilers = find_compilers();
    if compilers.is_empty() {
        assert!(true, "No compilers found, skipping test");
    } else {
        for (compiler, path) in compilers {
            run_sccache_command_test(&sccache, compiler, path, tempdir.path())
        }
    }
}
<commit_msg>fix test compile on mingw<commit_after>// Copyright 2016 Mozilla Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![allow(dead_code, unused_imports)]

use client::connect_with_retry;
use commands::{
    DEFAULT_PORT,
    request_stats,
    which,
};
use env_logger;
use log::LogLevel::Trace;
use std::collections::HashMap;
use std::env;
use std::ffi::{OsStr,OsString};
use std::fs;
use std::io::{
    self,
    Write,
};
use std::path::{Path,PathBuf};
use std::process::{
    Command,
    Output,
    Stdio,
};
use tempdir::TempDir;
use test::utils::*;

// Test GCC + clang on non-OS X platforms.
#[cfg(all(unix, not(target_os="macos")))]
const COMPILERS: &'static [&'static str] = &["gcc", "clang"];

// OS X ships a `gcc` that's just a clang wrapper, so only test clang there.
#[cfg(target_os="macos")]
const COMPILERS: &'static [&'static str] = &["clang"];

// Test MSVC when targeting MSVC.
#[cfg(target_env="msvc")]
const COMPILERS: &'static[&'static str] = &["cl.exe"];

//TODO: could test gcc when targeting mingw.

fn do_run<T: AsRef<OsStr>>(exe: &Path, args: &[T], cwd: &Path) -> Output {
    let mut cmd = Command::new(exe);
    cmd.args(args)
        .current_dir(cwd)
        .stdout(Stdio::piped())
        .stderr(Stdio::piped());
    trace!("do_run: {:?}", cmd);
    cmd.spawn()
        .unwrap_or_else(|e| { panic!("failed to execute child: {}", e) })
        .wait_with_output()
        .unwrap()
}

fn run<T: AsRef<OsStr>>(exe: &Path, args: &[T], cwd: &Path) -> bool {
    let output = do_run(exe, args, cwd);
    if output.status.success() {
        true
    } else {
        let va = args.iter().map(|a| a.as_ref().to_str().unwrap()).collect::<Vec<_>>();
        println!("Process `{:?} {}` failed:", exe, va.join(" "));
        print!("stdout: `");
        io::stdout().write(&output.stdout).unwrap();
        println!("`");
        print!("stderr: `");
        io::stdout().write(&output.stderr).unwrap();
        println!("`");
        false
    }
}

macro_rules! vec_from {
    ( $t:ty, $( $x:expr ),* ) => {
        vec!($( Into::<$t>::into(&$x), )*)
    };
}

fn compile_cmdline<T: AsRef<OsStr>>(compiler: &str, exe: T, input: &str, output: &str) -> Vec<OsString> {
    match compiler {
        "gcc" | "clang" => vec_from!(OsString, exe.as_ref(), "-c", input, "-o", output),
        "cl.exe" => vec_from!(OsString, exe, "-c", input, format!("-Fo{}", output)),
        _ => panic!("Unsupported compiler: {}", compiler),
    }
}

fn print_stats(stats: &HashMap<String, CacheStat>) {
    for (k, v) in stats.iter() {
        trace!("  {}: {:?}", k, v);
    }
}

fn run_sccache_command_test<T: AsRef<OsStr>>(sccache: &Path, compiler: &str, exe: T, tempdir: &Path) {
    // Ensure there's no existing sccache server running.
    trace!("stop server");
    do_run(sccache, &["--stop-server"], tempdir);
    // Create a subdir for the cache.
    let cache = tempdir.join("cache");
    fs::create_dir_all(&cache).unwrap();
    // Start a server.
    trace!("start server");
    // Don't run this with run() because on Windows `wait_with_output`
    // will hang because the internal server process is not detached.
    assert!(Command::new(sccache)
            .arg("--start-server")
            .current_dir(tempdir)
            .env("SCCACHE_DIR", &cache)
            .status()
            .unwrap()
            .success());
    trace!("run_sccache_command_test: {}", compiler);
    // Compile a source file.
    let original_source_file = Path::new(file!()).parent().unwrap().join("test.c");
    // Copy the source file into the tempdir so we can compile with relative paths, since the commandline winds up in the hash key.
    let source_file = tempdir.join("test.c");
    trace!("fs::copy({:?}, {:?})", original_source_file, source_file);
    fs::copy(&original_source_file, &source_file).unwrap();
    let out_file = tempdir.join("test.o");
    let input = source_file.file_name().unwrap().to_str().unwrap();
    let output = out_file.file_name().unwrap().to_str().unwrap();
    trace!("compile");
    assert_eq!(true, run(sccache, &compile_cmdline(compiler, exe.as_ref(), &input, &output), tempdir));
    assert_eq!(true, fs::metadata(&out_file).and_then(|m| Ok(m.len() > 0)).unwrap());
    trace!("connect");
    let conn = connect_with_retry(DEFAULT_PORT).unwrap();
    trace!("request stats");
    let stats = cache_stats_map(request_stats(conn).unwrap());
    if log_enabled!(Trace) {
        trace!("stats:");
        print_stats(&stats);
    }
    assert_eq!(&CacheStat::Count(1), stats.get("Compile requests").unwrap());
    assert_eq!(&CacheStat::Count(1), stats.get("Compile requests executed").unwrap());
    assert_eq!(&CacheStat::Count(0), stats.get("Cache hits").unwrap());
    assert_eq!(&CacheStat::Count(1), stats.get("Cache misses").unwrap());
    trace!("compile");
    fs::remove_file(&out_file).unwrap();
    assert_eq!(true, run(sccache, &compile_cmdline(compiler, exe.as_ref(), &input, &output), tempdir));
    assert_eq!(true, fs::metadata(&out_file).and_then(|m| Ok(m.len() > 0)).unwrap());
    trace!("connect");
    let conn = connect_with_retry(DEFAULT_PORT).unwrap();
    trace!("request stats");
    let stats = cache_stats_map(request_stats(conn).unwrap());
    if log_enabled!(Trace) {
        trace!("stats:");
        print_stats(&stats);
    }
    assert_eq!(&CacheStat::Count(2), stats.get("Compile requests").unwrap());
    assert_eq!(&CacheStat::Count(2), stats.get("Compile requests executed").unwrap());
    assert_eq!(&CacheStat::Count(1), stats.get("Cache hits").unwrap());
    assert_eq!(&CacheStat::Count(1), stats.get("Cache misses").unwrap());
    trace!("stop server");
    assert_eq!(true, run(sccache, &["--stop-server"], tempdir));
}

#[cfg(any(unix, target_env="msvc"))]
fn find_compilers() -> Vec<(&'static str, OsString)> {
    let cwd = env::current_dir().unwrap();
    COMPILERS.iter()
        .filter_map(|c| {
            match which(c, env::var_os("PATH"), &cwd) {
                Some(full_path) => Some((*c, full_path)),
                None => None,
            }
        })
        .collect::<Vec<_>>()
}

#[test]
#[cfg(any(unix, target_env="msvc"))]
fn test_sccache_command() {
    match env_logger::init() {
        Ok(_) => {},
        Err(_) => {},
    }
    let tempdir = TempDir::new("sccache_system_test").unwrap();
    let sccache = env::current_exe().unwrap().parent().unwrap().join("sccache").with_extension(env::consts::EXE_EXTENSION);
    match fs::metadata(&sccache) {
        Ok(_) => {},
        Err(_) => panic!("Error: sccache binary not found at `{:?}. Do you need to run `cargo build`?", sccache),
    }
    let compilers = find_compilers();
    if compilers.is_empty() {
        assert!(true, "No compilers found, skipping test");
    } else {
        for (compiler, path) in compilers {
            run_sccache_command_test(&sccache, compiler, path, tempdir.path())
        }
    }
}
<|endoftext|>


<commit_before>#![feature(core)]
#![feature(old_io)]
#![feature(libc)]
#![feature(old_path)]
#![feature(std_misc)]
#![allow(dead_code)]

extern crate core;
extern crate getopts;
extern crate libc;
extern crate mdo;
extern crate pam;
extern crate qrcode;
extern crate toml;
extern crate tozny_auth;
extern crate url;

use libc::{c_char, c_int};
use mdo::result::{bind};
use pam::{constants, module};
use pam::conv::{PamConv};
use pam::constants::*;
use std::{ffi, fmt, num, str};
use std::old_io::timer::sleep;
use std::old_io::{Writer};
use std::time::Duration;
use tozny_auth::{login, protocol, question, user};
use tozny_auth::protocol::{Newtype};

use config::{Config, ConfigError};

mod config;
#[macro_use] mod my_mdo;
mod presence;
mod qr_term;

#[no_mangle]
pub extern fn pam_sm_authenticate(pamh: &module::PamHandleT, flags: PamFlag,
                                  argc: c_int, argv: *const *const c_char
                                  ) -> PamResultCode {
    let args = unsafe { translate_args(argc, argv) };
    let decision = mdo! {
        user   =<< module::get_user(pamh, None).map_err(AuthError::PamResult);
        config =<< Config::build(user.as_slice(), args.as_slice())
            .map_err(AuthError::ConfigError);
        conv   =<< unsafe { module::get_item::<PamConv>(pamh) }.map_err(AuthError::PamResult);
        login  =<< authenticate(&config, user.as_slice(), &conv);
        ign show_info(conv, flags, &format!("Authenticated as {}", login.user_display));
        ret Ok(constants::PAM_SUCCESS)
    };

    decision.unwrap_or_else(|e| {
        if flags & constants::PAM_SILENT == 0 {
            show_err(pamh, &e)
        }
        error_code(&e)
    })
}

#[allow(unused_variables)]
#[no_mangle]
pub extern fn pam_sm_setcred(pamh: *mut module::PamHandleT, flags: PamFlag,
                             argc: c_int, argv: *const *const c_char
                             ) -> PamResultCode {
    constants::PAM_SUCCESS
}

#[derive(Debug)]
enum AuthError {
    ConfigError(ConfigError),
    NotAuthorized,
    PamResult(PamResultCode),
    TimedOut,
    QuestionError(question::QuestionError),
}

fn authenticate(config: &config::Config,
                user: &str,
                conv: &PamConv,
                ) -> Result<login::Login, AuthError> {
    let user_api = config.get_user_api();
    let q        = AuthError::QuestionError;

    user_api.login_challenge().map_err(q)
    .and_then(|challenge| {
        let did_push = if config.presence {
            let d = push_notification(&user_api, config, &challenge.session_id);
            let _ = presence::save_presence(user, &config.home_dir, &challenge.presence);
            d
        } else { false };
        if did_push { show_push(config, conv) } else { show_qr(config, conv, &challenge) }
        .and_then(|_| {
            poll_session_status(&user_api, &challenge, 100)  // TODO: configurable TTL
        })
    })
    .and_then(|question| {
        question::unpack::<login::Login>(&question.signed_data).map_err(q)
    })
    .and_then(|login| {
        if config.is_authorized(&login) { Ok(login) } else { Err(AuthError::NotAuthorized) }
    })
}

fn push_notification(user_api: &user::UserApi,
                     config: &config::Config,
                     session_id: &protocol::SessionId,
                     ) -> bool {
    match presence::get_presence(&config.home_dir) {
        Some(presence) => {
            user_api.push(session_id, &presence)
            .map(|_| true)
            .unwrap_or(false)
        },
        None => false,
    }
}

fn show_push(config: &config::Config, conv: &PamConv) -> Result<(), AuthError> {
    interact(config, conv, |writer| {
        let _ = writer.write_str("Check your phone for a push notification from Tozny.");
    })
}

fn show_qr(config: &config::Config, conv: &PamConv, challenge: &user::LoginChallenge
           ) -> Result<(), AuthError> {
    interact(config, conv, |writer| {
        if config.qr {
            show_inline_qr(challenge, writer)
        }
        else {
            show_qr_url(challenge, writer)
        }
        if config.mobile_url {
            let _ = writer.write_fmt(format_args!(
                "\n\nIf you are on your mobile device, use this URL to invoke the Tozny app:\n{}",
                challenge.mobile_url.to_string()));
        }
    })
}

fn show_inline_qr(challenge: &user::LoginChallenge, writer: &mut Vec<u8>) {
    let qr = build_qr(challenge);
    let _ = writer.write_str("\n");
    qr_term::output_unicode(&qr, "        ", writer);
    let _ = writer.write_fmt(format_args!(
        "\nScan the code above with the Tozny app. \
        Or if the code does not display correctly, open this URL:\n{}",
        challenge.qr_url.to_string()));
}

fn show_qr_url(challenge: &user::LoginChallenge, writer: &mut Vec<u8>) {
    let _ = writer.write_fmt(format_args!(
        "Open this URL, and scan the QR code with the Tozny app:\n{}",
        challenge.qr_url.to_string()));
}

fn interact<F>(config: &config::Config, conv: &PamConv, f: F) -> Result<(), AuthError>
        where F: Fn(&mut Vec<u8>) {
    let mut writer = Vec::new();
    f(&mut writer);
    if config.prompt {
        let _ = writer.write_str("\n\nPress Enter at any time:");
    }
    let mtype = if config.prompt { PAM_PROMPT_ECHO_OFF } else { PAM_TEXT_INFO };
    conv.send(mtype, str::from_utf8(writer.as_slice()).unwrap())
    .map_err(AuthError::PamResult)
    .and(Ok(()))
}

fn poll_session_status(api: &user::UserApi, challenge: &user::LoginChallenge, ttl: usize
                       ) -> Result<question::Question, AuthError> {
    api.check_session_status(&challenge.session_id)
        .map_err(AuthError::QuestionError)
    .and_then(|res| {
        match res {
            Some(question) => Ok(question),
            None => {
                if ttl > 0 {
                    sleep(Duration::seconds(1));
                    poll_session_status(api, challenge, ttl - 1)
                }
                else {
                    Err(AuthError::TimedOut)
                }
            }
        }
    })
}

fn error_code(err: &AuthError) -> PamResultCode {
    match err {
        &AuthError::ConfigError(_)         => PAM_AUTHINFO_UNAVAIL,
        &AuthError::NotAuthorized          => PAM_PERM_DENIED,
        &AuthError::PamResult(code)        => code,
        &AuthError::TimedOut               => PAM_AUTHINFO_UNAVAIL,
        &AuthError::QuestionError(ref err) => match err {
            &question::QuestionError::InvalidSignature => PAM_AUTH_ERR,
            _                                          => PAM_SERVICE_ERR,
        },
    }
}

impl fmt::Display for AuthError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            &AuthError::ConfigError(ref err) => err.fmt(f),
            &AuthError::NotAuthorized        => {
                f.write_str("You are not authorized to access this account.")
            }
            &AuthError::PamResult(_)         => Ok(()),
            &AuthError::TimedOut             => {
                f.write_str("Timed out waiting for user to authenticate.")
            }
            &AuthError::QuestionError(ref err) => err.fmt(f),
        }
    }
}

fn show_info<E>(conv: &PamConv, flags: PamFlag, info: &str) -> Result<(), E> {
    if flags & constants::PAM_SILENT == 0 {
        let _ = conv.send(PAM_TEXT_INFO, info);
    }
    Ok(())
}

fn show_err(pamh: &module::PamHandleT, err: &AuthError) {
    for conv in unsafe { module::get_item::<PamConv>(pamh) }.iter() {
        let _ = conv.send(PAM_ERROR_MSG, &format!("{}", err));
    }
}

fn build_qr(challenge: &user::LoginChallenge) -> qrcode::QrCode {
    qrcode::QrCode::new(challenge.mobile_url.to_string().as_bytes()).unwrap()
}

unsafe fn translate_args(argc: c_int, argv: *const *const c_char) -> Vec<String> {
    let v = Vec::<*const c_char>::from_raw_buf(argv, num::cast(argc).unwrap());
    v.into_iter().filter_map(|arg| {
        let bytes = ffi::CStr::from_ptr(arg).to_bytes();
        String::from_utf8(bytes.to_vec()).ok()
    })
    .collect()
}
<commit_msg>Removes unnecessary `unsafe` blocks.<commit_after>#![feature(core)]
#![feature(old_io)]
#![feature(libc)]
#![feature(old_path)]
#![feature(std_misc)]
#![allow(dead_code)]

extern crate core;
extern crate getopts;
extern crate libc;
extern crate mdo;
extern crate pam;
extern crate qrcode;
extern crate toml;
extern crate tozny_auth;
extern crate url;

use libc::{c_char, c_int};
use mdo::result::{bind};
use pam::{constants, module};
use pam::conv::{PamConv};
use pam::constants::*;
use std::{ffi, fmt, num, str};
use std::old_io::timer::sleep;
use std::old_io::{Writer};
use std::time::Duration;
use tozny_auth::{login, protocol, question, user};
use tozny_auth::protocol::{Newtype};

use config::{Config, ConfigError};

mod config;
#[macro_use] mod my_mdo;
mod presence;
mod qr_term;

#[no_mangle]
pub extern fn pam_sm_authenticate(pamh: &module::PamHandleT, flags: PamFlag,
                                  argc: c_int, argv: *const *const c_char
                                  ) -> PamResultCode {
    let args = unsafe { translate_args(argc, argv) };
    let decision = mdo! {
        user   =<< module::get_user(pamh, None).map_err(AuthError::PamResult);
        config =<< Config::build(user.as_slice(), args.as_slice())
            .map_err(AuthError::ConfigError);
        conv   =<< module::get_item::<PamConv>(pamh).map_err(AuthError::PamResult);
        login  =<< authenticate(&config, user.as_slice(), &conv);
        ign show_info(conv, flags, &format!("Authenticated as {}", login.user_display));
        ret Ok(constants::PAM_SUCCESS)
    };

    decision.unwrap_or_else(|e| {
        if flags & constants::PAM_SILENT == 0 {
            show_err(pamh, &e)
        }
        error_code(&e)
    })
}

#[allow(unused_variables)]
#[no_mangle]
pub extern fn pam_sm_setcred(pamh: *mut module::PamHandleT, flags: PamFlag,
                             argc: c_int, argv: *const *const c_char
                             ) -> PamResultCode {
    constants::PAM_SUCCESS
}

#[derive(Debug)]
enum AuthError {
    ConfigError(ConfigError),
    NotAuthorized,
    PamResult(PamResultCode),
    TimedOut,
    QuestionError(question::QuestionError),
}

fn authenticate(config: &config::Config,
                user: &str,
                conv: &PamConv,
                ) -> Result<login::Login, AuthError> {
    let user_api = config.get_user_api();
    let q        = AuthError::QuestionError;

    user_api.login_challenge().map_err(q)
    .and_then(|challenge| {
        let did_push = if config.presence {
            let d = push_notification(&user_api, config, &challenge.session_id);
            let _ = presence::save_presence(user, &config.home_dir, &challenge.presence);
            d
        } else { false };
        if did_push { show_push(config, conv) } else { show_qr(config, conv, &challenge) }
        .and_then(|_| {
            poll_session_status(&user_api, &challenge, 100)  // TODO: configurable TTL
        })
    })
    .and_then(|question| {
        question::unpack::<login::Login>(&question.signed_data).map_err(q)
    })
    .and_then(|login| {
        if config.is_authorized(&login) { Ok(login) } else { Err(AuthError::NotAuthorized) }
    })
}

fn push_notification(user_api: &user::UserApi,
                     config: &config::Config,
                     session_id: &protocol::SessionId,
                     ) -> bool {
    match presence::get_presence(&config.home_dir) {
        Some(presence) => {
            user_api.push(session_id, &presence)
            .map(|_| true)
            .unwrap_or(false)
        },
        None => false,
    }
}

fn show_push(config: &config::Config, conv: &PamConv) -> Result<(), AuthError> {
    interact(config, conv, |writer| {
        let _ = writer.write_str("Check your phone for a push notification from Tozny.");
    })
}

fn show_qr(config: &config::Config, conv: &PamConv, challenge: &user::LoginChallenge
           ) -> Result<(), AuthError> {
    interact(config, conv, |writer| {
        if config.qr {
            show_inline_qr(challenge, writer)
        }
        else {
            show_qr_url(challenge, writer)
        }
        if config.mobile_url {
            let _ = writer.write_fmt(format_args!(
                "\n\nIf you are on your mobile device, use this URL to invoke the Tozny app:\n{}",
                challenge.mobile_url.to_string()));
        }
    })
}

fn show_inline_qr(challenge: &user::LoginChallenge, writer: &mut Vec<u8>) {
    let qr = build_qr(challenge);
    let _ = writer.write_str("\n");
    qr_term::output_unicode(&qr, "        ", writer);
    let _ = writer.write_fmt(format_args!(
        "\nScan the code above with the Tozny app. \
        Or if the code does not display correctly, open this URL:\n{}",
        challenge.qr_url.to_string()));
}

fn show_qr_url(challenge: &user::LoginChallenge, writer: &mut Vec<u8>) {
    let _ = writer.write_fmt(format_args!(
        "Open this URL, and scan the QR code with the Tozny app:\n{}",
        challenge.qr_url.to_string()));
}

fn interact<F>(config: &config::Config, conv: &PamConv, f: F) -> Result<(), AuthError>
        where F: Fn(&mut Vec<u8>) {
    let mut writer = Vec::new();
    f(&mut writer);
    if config.prompt {
        let _ = writer.write_str("\n\nPress Enter at any time:");
    }
    let mtype = if config.prompt { PAM_PROMPT_ECHO_OFF } else { PAM_TEXT_INFO };
    conv.send(mtype, str::from_utf8(writer.as_slice()).unwrap())
    .map_err(AuthError::PamResult)
    .and(Ok(()))
}

fn poll_session_status(api: &user::UserApi, challenge: &user::LoginChallenge, ttl: usize
                       ) -> Result<question::Question, AuthError> {
    api.check_session_status(&challenge.session_id)
        .map_err(AuthError::QuestionError)
    .and_then(|res| {
        match res {
            Some(question) => Ok(question),
            None => {
                if ttl > 0 {
                    sleep(Duration::seconds(1));
                    poll_session_status(api, challenge, ttl - 1)
                }
                else {
                    Err(AuthError::TimedOut)
                }
            }
        }
    })
}

fn error_code(err: &AuthError) -> PamResultCode {
    match err {
        &AuthError::ConfigError(_)         => PAM_AUTHINFO_UNAVAIL,
        &AuthError::NotAuthorized          => PAM_PERM_DENIED,
        &AuthError::PamResult(code)        => code,
        &AuthError::TimedOut               => PAM_AUTHINFO_UNAVAIL,
        &AuthError::QuestionError(ref err) => match err {
            &question::QuestionError::InvalidSignature => PAM_AUTH_ERR,
            _                                          => PAM_SERVICE_ERR,
        },
    }
}

impl fmt::Display for AuthError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            &AuthError::ConfigError(ref err) => err.fmt(f),
            &AuthError::NotAuthorized        => {
                f.write_str("You are not authorized to access this account.")
            }
            &AuthError::PamResult(_)         => Ok(()),
            &AuthError::TimedOut             => {
                f.write_str("Timed out waiting for user to authenticate.")
            }
            &AuthError::QuestionError(ref err) => err.fmt(f),
        }
    }
}

fn show_info<E>(conv: &PamConv, flags: PamFlag, info: &str) -> Result<(), E> {
    if flags & constants::PAM_SILENT == 0 {
        let _ = conv.send(PAM_TEXT_INFO, info);
    }
    Ok(())
}

fn show_err(pamh: &module::PamHandleT, err: &AuthError) {
    for conv in module::get_item::<PamConv>(pamh).iter() {
        let _ = conv.send(PAM_ERROR_MSG, &format!("{}", err));
    }
}

fn build_qr(challenge: &user::LoginChallenge) -> qrcode::QrCode {
    qrcode::QrCode::new(challenge.mobile_url.to_string().as_bytes()).unwrap()
}

unsafe fn translate_args(argc: c_int, argv: *const *const c_char) -> Vec<String> {
    let v = Vec::<*const c_char>::from_raw_buf(argv, num::cast(argc).unwrap());
    v.into_iter().filter_map(|arg| {
        let bytes = ffi::CStr::from_ptr(arg).to_bytes();
        String::from_utf8(bytes.to_vec()).ok()
    })
    .collect()
}
<|endoftext|>


<commit_before>use std::io::prelude::*;
use std::fs::File;

fn main() {
    println!("sokan");
    let mut fileResult :std::fs::File;
    match File::open("default.yaml") {
        Ok(result) => fileResult = result,
        Err(error) => {
            println!("Error occured while accessing default.yaml: {:?}", error);
            return;
        }
    }

    let mut yaml_file = String::new();
    match fileResult.read_to_string(&mut yaml_file) {
        Err(error) => println!("fail"),
        _ => { }
    }
}
<commit_msg>pass file to configuration reader<commit_after>use std::io::prelude::*;
use std::fs::File;
mod configuration;

fn main() {
    println!("sokan");
    let mut fileResult :std::fs::File;
    match File::open("default.yaml") {
        Ok(result) => fileResult = result,
        Err(error) => {
            println!("Error occured while accessing default.yaml: {:?}", error);
            return;
        }
    }

    let mut yaml_file = String::new();
    match fileResult.read_to_string(&mut yaml_file) {
        Err(error) => println!("fail"),
        _ => { }
    }

    let commands = configuration::from_yaml(yaml_file.to_string());
    println!("{:?}", commands);
}
<|endoftext|>


<commit_before>extern crate iron;
#[macro_use]
extern crate router;
extern crate dotenv;
extern crate sbb_telegram_bot;

use dotenv::dotenv;
use iron::prelude::*;
use sbb_telegram_bot::route;
use sbb_telegram_bot::util::{telegram, twitter};
use std::thread;
use std::time::Duration;

fn main() {
    dotenv().ok();
    thread::spawn(broadcast_loop);
    let router = router!(telegram: post "/sbb/telegram" => route::telegram);
    Iron::new(router).http("localhost:3001").unwrap();
}

fn broadcast_loop() {
    loop {
        thread::sleep(Duration::from_secs(10));
        let tweets = twitter::user_timeline("SBB_Delay_Bot", 1).unwrap();
        let tweet = &tweets[0];
        if tweet.id != twitter::read_last_tweet_id() {
            twitter::write_last_tweet_id(tweet.id);
            let mut txt = tweet.text.clone();
            if let Some(pos) = txt.find("http") {
                txt = txt[..pos].to_owned();
            }
            telegram::broadcast(&txt).unwrap();
        }
    }
}
<commit_msg>Set twitter poll back to railinfo<commit_after>extern crate iron;
#[macro_use]
extern crate router;
extern crate dotenv;
extern crate sbb_telegram_bot;

use dotenv::dotenv;
use iron::prelude::*;
use sbb_telegram_bot::route;
use sbb_telegram_bot::util::{telegram, twitter};
use std::thread;
use std::time::Duration;

fn main() {
    dotenv().ok();
    thread::spawn(broadcast_loop);
    let router = router!(telegram: post "/sbb/telegram" => route::telegram);
    Iron::new(router).http("localhost:3001").unwrap();
}

fn broadcast_loop() {
    loop {
        thread::sleep(Duration::from_secs(10));
        let tweets = twitter::user_timeline("railinfo_sbb", 1).unwrap();
        let tweet = &tweets[0];
        if tweet.id != twitter::read_last_tweet_id() {
            twitter::write_last_tweet_id(tweet.id);
            let mut txt = tweet.text.clone();
            if let Some(pos) = txt.find("http") {
                txt = txt[..pos].to_owned();
            }
            telegram::broadcast(&txt).unwrap();
        }
    }
}
<|endoftext|>


<commit_before>#![recursion_limit = "1024"] // error_chain

extern crate websocket;
extern crate env_logger;
#[macro_use]
extern crate log;
#[macro_use]
extern crate error_chain;
extern crate url;
extern crate clap;

const BUFSIZ : usize = 8192;
const QSIZ : usize = 1;

error_chain! {
    foreign_links {
        Io(::std::io::Error);
        Log(log::SetLoggerError);
        Url(::url::ParseError);
        Ws(::websocket::result::WebSocketError);
        VarError(::std::env::VarError);
        RE(std::sync::mpsc::RecvError);
    }
}

// Initialize logger with default "info" log level:
fn init_logger() -> Result<()> {
    let mut builder = env_logger::LogBuilder::new();
    builder.filter(None, log::LogLevelFilter::Info);
    if ::std::env::var("RUST_LOG").is_ok() {
       builder.parse(&::std::env::var("RUST_LOG")?);
    }
    builder.init()?;
    Ok(())
}

fn try_main() -> Result<()> {
    //env_logger::init()?;
    init_logger()?;

    use std::thread;
    use std::sync::mpsc::sync_channel;
    use std::io::{stdin,stdout};

    use websocket::{Message, Sender, Receiver};
    use websocket::message::Type;
    use websocket::client::request::Url;
    use websocket::Client;

    use std::io::{Read};

    // setup command line arguments
    let matches = ::clap::App::new("WS Command Line Client")
        .version("0.1")
        .author("Vitaly \"_Vi\" Shukela <vi0oss@gmail.com>")
        .about("Send binary data from stdin to a WebSocket and back to stdout.")
        .arg(::clap::Arg::with_name("URL")
             .help("The URL of the WebSocket server.")
             .required(true)
             .index(1)).get_matches();

    let url = Url::parse(matches.value_of("URL").ok_or("no URL")?)?;

    info!("Connecting to {}", url);

    let request = Client::connect(url)?;

    let response = request.send()?; // Send the request and retrieve a response

    info!("Validating response...");

    response.validate()?; // Validate the response

    info!("Successfully connected");

    let (mut sender, mut receiver) = response.begin().split();

    let (tx, rx) = sync_channel(QSIZ);

    let tx_1 = tx.clone();

    let send_loop = thread::spawn(move || {
        if let Err(er) = (|| -> Result<()> {
            loop {
                // Send loop
                let message: Message = rx.recv()?;
                if message.opcode == Type::Close { break; }
                sender.send_message(&message)?;
            }
            Ok(())
        })() {
            error!("Receive loop: {}", er);
        }
        let _ = sender.send_message(&Message::close());
    });

    let receive_loop = thread::spawn(move || {
        if let Err(x) = (|| -> Result<()> {
            for m in receiver.incoming_messages() {
                let message : websocket::Message = try!(m);
                match message.opcode {
                    Type::Close => {
                        return Ok(());
                    }
                    Type::Ping => {
                        tx_1.send(
                            Message::pong(
                                message.payload
                            )).map_err(|_|"Failed pong")?
                    }
                    // Say what we received
                    _ => {
                        use std::borrow::Borrow;
                        use std::io::Write;
                        let msgpayload : &[u8] = message.payload.borrow();
                        debug!("Received message of {} bytes", msgpayload.len());
                        stdout().write_all(msgpayload)?;
                        stdout().flush()?;
                    }
                }
            }
            Ok(())
        })() {
            error!("Error on receive loop: {}", x);
        }
        let _ = tx_1.send(Message::close());

        // Receive loop
    });

    let mut buffer : [u8; BUFSIZ] = [0; BUFSIZ];

    loop {
        let data : Vec<u8> = match stdin().read(&mut buffer) {
            Ok(0) => break,
            Ok(ret) => {
                debug!("Sending {} bytes of data", ret);
                buffer[0..ret].to_vec()
            }
            Err(ref e) if e.kind() == ::std::io::ErrorKind::Interrupted => continue,
            Err(ref e) if e.kind() == ::std::io::ErrorKind::WouldBlock  => continue,
            Err(e) => {
                error!("Read error: {}", e);
                break;
            }
        };

        let message = Message::binary(data);

        match tx.send(message) {
            Ok(()) => (),
            Err(e) => {
                error!("Main Loop: {:?}", e);
                break;
            }
        }
    }
    let _ = tx.send(Message::close());

    // We're exiting

    info!("Waiting for child threads to exit");

    let _ = send_loop.join();
    let _ = receive_loop.join();

    info!("Exited");
    Ok(())
}

fn main() {
    if let Err(x) = try_main() {
        use std::io::Write;
        let _ = writeln!(::std::io::stderr(), "{:?}", x);
    }
}

<commit_msg>Abstract away receiver loop<commit_after>#![recursion_limit = "1024"] // error_chain

extern crate websocket;
extern crate env_logger;
#[macro_use]
extern crate log;
#[macro_use]
extern crate error_chain;
extern crate url;
extern crate clap;

const BUFSIZ : usize = 8192;
const QSIZ : usize = 1;

use std::thread;
use std::sync::mpsc::sync_channel;
use std::io::{stdin,stdout};

use websocket::{Message, Sender, Receiver};
use websocket::message::Type;
use websocket::client::request::Url;
use websocket::Client;

use std::io::{Read,Error as IoError,ErrorKind as IoErrorKind,Write};

error_chain! {
    foreign_links {
        Io(::std::io::Error);
        Log(log::SetLoggerError);
        Url(::url::ParseError);
        Ws(::websocket::result::WebSocketError);
        VarError(::std::env::VarError);
        RE(std::sync::mpsc::RecvError);
    }
}

// Initialize logger with default "info" log level:
fn init_logger() -> Result<()> {
    let mut builder = env_logger::LogBuilder::new();
    builder.filter(None, log::LogLevelFilter::Info);
    if ::std::env::var("RUST_LOG").is_ok() {
       builder.parse(&::std::env::var("RUST_LOG")?);
    }
    builder.init()?;
    Ok(())
}

struct SenderWrapper<T: Sender> (T);

impl<T: Sender> ::std::io::Write for SenderWrapper<T> {
    fn write(&mut self, buf: &[u8]) -> ::std::io::Result<usize> {
        let message = Message::binary(buf);
        let ret;
        if buf.len() > 0 {
            ret = self.0.send_message(&message);
        } else {
            // Interpret zero length buffer is request
            // to close communication
            ret = self.0.send_message(&Message::close());
        }
        ret.map_err(|e|IoError::new(IoErrorKind::BrokenPipe, e))?;
        Ok(buf.len())
    }
    fn flush(&mut self) -> ::std::io::Result<()> {
        Ok(())
    }
}

fn try_main() -> Result<()> {
    //env_logger::init()?;
    init_logger()?;

    // setup command line arguments
    let matches = ::clap::App::new("WS Command Line Client")
        .version("0.1")
        .author("Vitaly \"_Vi\" Shukela <vi0oss@gmail.com>")
        .about("Send binary data from stdin to a WebSocket and back to stdout.")
        .arg(::clap::Arg::with_name("URL")
             .help("The URL of the WebSocket server.")
             .required(true)
             .index(1)).get_matches();

    let url = Url::parse(matches.value_of("URL").ok_or("no URL")?)?;

    info!("Connecting to {}", url);

    let request = Client::connect(url)?;

    let response = request.send()?; // Send the request and retrieve a response

    info!("Validating response...");

    response.validate()?; // Validate the response

    info!("Successfully connected");

    let (mut sender, mut receiver) = response.begin().split();

    //let (tx, rx) = sync_channel(QSIZ);

    //let tx_1 = tx.clone();

    /*let send_loop = thread::spawn(move || {
        if let Err(er) = (|| -> Result<()> {
            loop {
                // Send loop
                let message: Message = rx.recv()?;
                if message.opcode == Type::Close {
                    break;
                }
                sender.send_message(&message)?;
            }
            Ok(())
        })() {
            error!("Receive loop: {}", er);
        }
        let _ = sender.send_message(&Message::close());
    });*/

    let receive_loop = thread::spawn(move || -> Result<()> {
        if let Err(x) = (|| -> Result<()> {
            for m in receiver.incoming_messages() {
                let message : websocket::Message = try!(m);
                match message.opcode {
                    Type::Close => {
                        return Ok(());
                    }
                    Type::Ping => {
                        /*tx_1.send(
                            Message::pong(
                                message.payload
                            )).map_err(|_|"Failed pong")?*/
                    }
                    // Say what we received
                    _ => {
                        use std::borrow::Borrow;
                        use std::io::Write;
                        let msgpayload : &[u8] = message.payload.borrow();
                        debug!("Received message of {} bytes", msgpayload.len());
                        stdout().write_all(msgpayload)?;
                        stdout().flush()?;
                    }
                }
            }
            Ok(())
        })() {
            error!("Error on receive loop: {}", x);
        }
        //let _ = tx_1.send(Message::close());
        Ok(())
    });

    //let mut buffer : [u8; BUFSIZ] = [0; BUFSIZ];

    /*loop {
        let data : Vec<u8> = match stdin().read(&mut buffer) {
            Ok(0) => break,
            Ok(ret) => {
                debug!("Sending {} bytes of data", ret);
                buffer[0..ret].to_vec()
            }
            Err(ref e) if e.kind() == IoErrorKind::Interrupted => continue,
            Err(ref e) if e.kind() == IoErrorKind::WouldBlock  => continue,
            Err(e) => {
                error!("Read error: {}", e);
                break;
            }
        };

        let message = Message::binary(data);

        match tx.send(message) {
            Ok(()) => (),
            Err(e) => {
                error!("Main Loop: {:?}", e);
                break;
            }
        }
    }
    let _ = tx.send(Message::close());*/

    // We're exiting

    let mut wr = SenderWrapper(sender);

    // Actual data transfer happens here
    ::std::io::copy(&mut stdin(), &mut wr)?;

    wr.write(b"")?; // Signal close

    info!("Waiting for child threads to exit");

    //let _ = send_loop.join();
    let _ = receive_loop.join();

    info!("Exited");
    Ok(())
}

fn main() {
    if let Err(x) = try_main() {
        let _ = writeln!(::std::io::stderr(), "{:?}", x);
    }
}

<|endoftext|>


<commit_before>//! rs-collector: An scollector compatible telemetry collector written in Rust.

extern crate clap;
extern crate env_logger;
#[macro_use] extern crate log;
extern crate rs_collector;

use clap::{Arg, ArgMatches, App};
use std::error::Error;
use std::path::Path;

use rs_collector::config::Config;

static VERSION: &'static str = env!("CARGO_PKG_VERSION");
static DEFAULT_CONFIG_FILE: &'static str = "/etc/rs-collector.conf";

fn main() {
    if env_logger::init().is_err() {
        exit_with_error("Could not initialize logger", -1);
    }

    let app = App::new("rs-collector")
        .version(VERSION)
        .after_help("Two modes are supported, i.e., sending a datum with meta data or \
                               sending only meta data. The modes are controlled whether a value \
                               `--value` is passed or not. Please mind that in both cases the \
                               meta data is required.")
        .arg(Arg::with_name("configfile")
            .short("c")
            .long("config")
            .value_name("FILE")
            .help("Sets a custom config file")
            .takes_value(true))
        .arg(Arg::with_name("show-config")
            .long("show-config")
            .help("Prints config"));
    let cli_args = app.get_matches();

    let verbose: bool = cli_args.is_present("verbose");
    let config: Config = match parse_args(&cli_args) {
        Ok(config) => config,
        Err(err) => {
            exit_with_error(&format!("Failed to parse configuration, because {}.", err), -2);
        }
    };
    if cli_args.is_present("show-config") {
        println!("config: {:?}", config);
    }

    run(&config, verbose);
}

fn parse_args(cli_args: &ArgMatches) -> Result<Config, Box<Error>> {
    let config_file_path = Path::new(cli_args.value_of("configfile").unwrap_or(DEFAULT_CONFIG_FILE));
    let mut config: Config = if config_file_path.exists() {
        let config = try!(Config::load_from_rs_collector_config(&config_file_path));
        config
    } else {
        Default::default()
    };

    Ok(config)
}

fn run(config: &Config, verbose: bool) {

    let collectors = rs_collector::collectors::create_collectors(config);
    rs_collector::scheduler::run(collectors);
}

fn exit_with_error(msg: &str, exit_code: i32) -> ! {
    println!("{}", msg);
    std::process::exit(exit_code);
}

#[cfg(test)]
mod tests {
}
<commit_msg>Add todo to main.rs<commit_after>//! rs-collector: An scollector compatible telemetry collector written in Rust.

extern crate clap;
extern crate env_logger;
#[macro_use] extern crate log;
extern crate rs_collector;

use clap::{Arg, ArgMatches, App};
use std::error::Error;
use std::path::Path;

use rs_collector::config::Config;

static VERSION: &'static str = env!("CARGO_PKG_VERSION");
static DEFAULT_CONFIG_FILE: &'static str = "/etc/rs-collector.conf";

fn main() {
    if env_logger::init().is_err() {
        exit_with_error("Could not initialize logger", -1);
    }

    let app = App::new("rs-collector")
        .version(VERSION)
        .after_help("Two modes are supported, i.e., sending a datum with meta data or \
                               sending only meta data. The modes are controlled whether a value \
                               `--value` is passed or not. Please mind that in both cases the \
                               meta data is required.")
        .arg(Arg::with_name("configfile")
            .short("c")
            .long("config")
            .value_name("FILE")
            .help("Sets a custom config file")
            .takes_value(true))
        .arg(Arg::with_name("show-config")
            .long("show-config")
            .help("Prints config"));
    let cli_args = app.get_matches();

    let verbose: bool = cli_args.is_present("verbose");
    let config: Config = match parse_args(&cli_args) {
        Ok(config) => config,
        Err(err) => {
            exit_with_error(&format!("Failed to parse configuration, because {}.", err), -2);
        }
    };
    // TODO a config must have been parsed; if not exit!
    if cli_args.is_present("show-config") {
        println!("config: {:?}", config);
    }

    run(&config, verbose);
}

fn parse_args(cli_args: &ArgMatches) -> Result<Config, Box<Error>> {
    let config_file_path = Path::new(cli_args.value_of("configfile").unwrap_or(DEFAULT_CONFIG_FILE));
    let mut config: Config = if config_file_path.exists() {
        let config = try!(Config::load_from_rs_collector_config(&config_file_path));
        config
    } else {
        Default::default()
    };

    Ok(config)
}

fn run(config: &Config, verbose: bool) {

    let collectors = rs_collector::collectors::create_collectors(config);
    rs_collector::scheduler::run(collectors);
}

fn exit_with_error(msg: &str, exit_code: i32) -> ! {
    println!("{}", msg);
    std::process::exit(exit_code);
}

#[cfg(test)]
mod tests {
}
<|endoftext|>


<commit_before>//
// main.rs
// Copyright (C) 2017 Szymon Urbaś <szymon.urbas@aol.com>
// Distributed under terms of the BSD (2-clause) license.
//
// Created on: 13 Mar 2017 21:03:41 +0100 (CET)
//

extern crate rand;
extern crate cursive;

#[cfg(feature = "go-slow")]
use std::thread;
#[cfg(feature = "go-slow")]
use std::time;

mod process;
mod scheduler;
mod fcfs;
mod round_robin;
mod tui;

use scheduler::*;
use tui::*;

/// Length of a single system clock tick (frequency of actual, real time)
#[cfg(feature = "go-slow")]
const CLOCK_HZ: u64 = 1000;
/// Switch context every <value> clock ticks
const SYSTEM_HZ: usize = 8;

struct SimulationResult {
  average_waiting_time: f64,
  context_switch_num: usize,
}

fn main() {
  let mut tui = tui::new();

  let scenarios = {
    (0..20).map(|_| (0..64).map(|_| {
      let upper_limit = rand::random::<usize>() % (SYSTEM_HZ * 4) + 1;
      let burst_time = rand::random::<usize>() % upper_limit + SYSTEM_HZ;

      burst_time + 1 // + 1 so there's no chance of having a process with 0 burst time
    }).collect::<Vec<usize>>()).collect::<Vec<Vec<usize>>>()
  };

  for (idx, ref scenario) in scenarios.iter().enumerate() {
    tui.add_scenario(format!("{}", idx), scenario);
  }

  macro_rules! run_simulation_suite {
    ($scheduler:ident) => ({
      let mut average_waiting_time = 0f64;
      let mut total_context_switches = 0usize;
      let mut average_context_switches = 0f64;

      for (run, scenario) in scenarios.iter().enumerate() {
        let scheduler = $scheduler::new();
        let result = run_simulation(&mut tui, scheduler, scenario.clone());
        let avg = result.average_waiting_time;
        let ctx_swtch_num = result.context_switch_num;

        tui.add_result(format!("{}: #{:02}: Average waiting time: {:02.2}", stringify!($scheduler), run, avg));
        tui.add_result(format!("{}: #{:02}: Context switches: {:02}", stringify!($scheduler), run, ctx_swtch_num));

        average_waiting_time -= average_waiting_time / (run + 1) as f64;
        average_waiting_time += avg / (run + 1) as f64;
        total_context_switches += ctx_swtch_num;

        average_context_switches -= average_context_switches / (run + 1) as f64;
        average_context_switches += ctx_swtch_num as f64 / (run + 1) as f64;
      }

      tui.add_result(format!("{}: -- Summary:", stringify!($scheduler)));
      tui.add_result(format!("{}: Average waiting time: {:02.2}", stringify!($scheduler), average_waiting_time));
      tui.add_result(format!("{}: Total # of context switches: {:02}", stringify!($scheduler), total_context_switches));
      tui.add_result(format!("{}: Average # of context switches: {:02.2}", stringify!($scheduler), average_context_switches));
    })
  }

  run_simulation_suite!(fcfs);
  run_simulation_suite!(round_robin);

  tui.update();

  loop {}
}

fn run_simulation<S>(mut tui: &mut Tui, mut scheduler: S, mut process_list: Vec<usize>) -> SimulationResult
where S: Scheduler {
  let mut clock_tick = 0;
  let mut process_spawner = process::new_spawner();
  let mut average_waiting_time = 0f64;
  let mut num_of_spawned_procs = 1;

  tui.set_header(format!("Using algorithm: {}", scheduler.name()));
  tui.debug(format!("{:05}: Starting simulation using {}", clock_tick, scheduler.name()));

  // vectors only support the pop (from the back) operation so
  // reverse it (so we pop from the front)
  process_list.reverse();

  // Add the equivalent of the 'init' process
  scheduler.add_process(process_spawner.spawn(process_list.pop().unwrap()));

  loop {
    // update the process view
    scheduler.list_processes(&mut tui);

    if cfg!(feature = "go-slow") || clock_tick % (SYSTEM_HZ * 2) == 0 {
      // update all the views
      tui.update();
    }

    if !scheduler.has_processes() {
      // if there's no more processes - end the simulation
      // if a process can only be created by another process - there's
      // nothing else to do
      break;
    }

    // simulate executing the current process
    tui.debug(format!("{:05}: Executing {}", clock_tick, scheduler.current_proc().unwrap().name));
    scheduler.current_proc_mut().unwrap().record_execution();

    // Simulate other processes creating new processes
    // Assume that you can't create new processes, if there's none already
    if clock_tick % SYSTEM_HZ == 0 {
      if let Some(burst_time) = process_list.pop() {
        let new_proc = process_spawner.spawn(burst_time);
        tui.debug(format!("{:05}: Spawning {}", clock_tick, new_proc.name));
        scheduler.add_process(new_proc);
        num_of_spawned_procs += 1;
      }
    }

    if scheduler.current_proc().unwrap().done_executing() {
      tui.debug(format!("{:05}: Killing process {}", clock_tick, scheduler.current_proc().unwrap().name));
      scheduler.kill_current_proc();
    }

    if scheduler.has_processes() {
      if clock_tick % SYSTEM_HZ == SYSTEM_HZ - 1 {
        let prev_proc_name = scheduler.current_proc().unwrap().name.clone();
        scheduler.schedule();
        tui.debug(format!("{:05}: Switching context: {} -> {}", clock_tick, prev_proc_name, scheduler.current_proc().unwrap().name));
        tui.debug(format!("{:05}: {} was waiting {} clock ticks", clock_tick, scheduler.current_proc().unwrap().name, scheduler.current_proc().unwrap().waiting_time));

        average_waiting_time -= average_waiting_time / num_of_spawned_procs as f64;
        average_waiting_time += scheduler.current_proc().unwrap().waiting_time as f64 / num_of_spawned_procs as f64;
      }

      // increase the waiting time for every process
      scheduler.increase_waiting_times();
    }

    #[cfg(feature = "go-slow")]
    thread::sleep(time::Duration::from_millis(1000 / CLOCK_HZ));

    clock_tick += 1;
  }

  SimulationResult {
    average_waiting_time: average_waiting_time,
    context_switch_num: scheduler.context_switch_num(),
  }
}

/*
 * vi: ts=2 sw=2 expandtab
 */

<commit_msg>Make the number of spawned processes configurable<commit_after>//
// main.rs
// Copyright (C) 2017 Szymon Urbaś <szymon.urbas@aol.com>
// Distributed under terms of the BSD (2-clause) license.
//
// Created on: 13 Mar 2017 21:03:41 +0100 (CET)
//

extern crate rand;
extern crate cursive;

#[cfg(feature = "go-slow")]
use std::thread;
#[cfg(feature = "go-slow")]
use std::time;

mod process;
mod scheduler;
mod fcfs;
mod round_robin;
mod tui;

use scheduler::*;
use tui::*;

/// Length of a single system clock tick (frequency of actual, real time)
#[cfg(feature = "go-slow")]
const CLOCK_HZ: u64 = 1000;
/// Switch context every <value> clock ticks
const SYSTEM_HZ: usize = 8;

/// Spawn this many processes in each scenario
const SPAWNED_PROCESS_NUM: usize = 64;

struct SimulationResult {
  average_waiting_time: f64,
  context_switch_num: usize,
}

fn main() {
  let mut tui = tui::new();

  let scenarios = {
    (0..20).map(|_| (0..SPAWNED_PROCESS_NUM).map(|_| {
      let upper_limit = rand::random::<usize>() % (SYSTEM_HZ * 4) + 1;
      let burst_time = rand::random::<usize>() % upper_limit + SYSTEM_HZ;

      burst_time + 1 // + 1 so there's no chance of having a process with 0 burst time
    }).collect::<Vec<usize>>()).collect::<Vec<Vec<usize>>>()
  };

  for (idx, ref scenario) in scenarios.iter().enumerate() {
    tui.add_scenario(format!("{}", idx), scenario);
  }

  macro_rules! run_simulation_suite {
    ($scheduler:ident) => ({
      let mut average_waiting_time = 0f64;
      let mut total_context_switches = 0usize;
      let mut average_context_switches = 0f64;

      for (run, scenario) in scenarios.iter().enumerate() {
        let scheduler = $scheduler::new();
        let result = run_simulation(&mut tui, scheduler, scenario.clone());
        let avg = result.average_waiting_time;
        let ctx_swtch_num = result.context_switch_num;

        tui.add_result(format!("{}: #{:02}: Average waiting time: {:02.2}", stringify!($scheduler), run, avg));
        tui.add_result(format!("{}: #{:02}: Context switches: {:02}", stringify!($scheduler), run, ctx_swtch_num));

        average_waiting_time -= average_waiting_time / (run + 1) as f64;
        average_waiting_time += avg / (run + 1) as f64;
        total_context_switches += ctx_swtch_num;

        average_context_switches -= average_context_switches / (run + 1) as f64;
        average_context_switches += ctx_swtch_num as f64 / (run + 1) as f64;
      }

      tui.add_result(format!("{}: -- Summary:", stringify!($scheduler)));
      tui.add_result(format!("{}: Average waiting time: {:02.2}", stringify!($scheduler), average_waiting_time));
      tui.add_result(format!("{}: Total # of context switches: {:02}", stringify!($scheduler), total_context_switches));
      tui.add_result(format!("{}: Average # of context switches: {:02.2}", stringify!($scheduler), average_context_switches));
    })
  }

  run_simulation_suite!(fcfs);
  run_simulation_suite!(round_robin);

  tui.update();

  loop {}
}

fn run_simulation<S>(mut tui: &mut Tui, mut scheduler: S, mut process_list: Vec<usize>) -> SimulationResult
where S: Scheduler {
  let mut clock_tick = 0;
  let mut process_spawner = process::new_spawner();
  let mut average_waiting_time = 0f64;
  let mut num_of_spawned_procs = 1;

  tui.set_header(format!("Using algorithm: {}", scheduler.name()));
  tui.debug(format!("{:05}: Starting simulation using {}", clock_tick, scheduler.name()));

  // vectors only support the pop (from the back) operation so
  // reverse it (so we pop from the front)
  process_list.reverse();

  // Add the equivalent of the 'init' process
  scheduler.add_process(process_spawner.spawn(process_list.pop().unwrap()));

  loop {
    // update the process view
    scheduler.list_processes(&mut tui);

    if cfg!(feature = "go-slow") || clock_tick % (SYSTEM_HZ * 2) == 0 {
      // update all the views
      tui.update();
    }

    if !scheduler.has_processes() {
      // if there's no more processes - end the simulation
      // if a process can only be created by another process - there's
      // nothing else to do
      break;
    }

    // simulate executing the current process
    tui.debug(format!("{:05}: Executing {}", clock_tick, scheduler.current_proc().unwrap().name));
    scheduler.current_proc_mut().unwrap().record_execution();

    // Simulate other processes creating new processes
    // Assume that you can't create new processes, if there's none already
    if clock_tick % SYSTEM_HZ == 0 {
      if let Some(burst_time) = process_list.pop() {
        let new_proc = process_spawner.spawn(burst_time);
        tui.debug(format!("{:05}: Spawning {}", clock_tick, new_proc.name));
        scheduler.add_process(new_proc);
        num_of_spawned_procs += 1;
      }
    }

    if scheduler.current_proc().unwrap().done_executing() {
      tui.debug(format!("{:05}: Killing process {}", clock_tick, scheduler.current_proc().unwrap().name));
      scheduler.kill_current_proc();
    }

    if scheduler.has_processes() {
      if clock_tick % SYSTEM_HZ == SYSTEM_HZ - 1 {
        let prev_proc_name = scheduler.current_proc().unwrap().name.clone();
        scheduler.schedule();
        tui.debug(format!("{:05}: Switching context: {} -> {}", clock_tick, prev_proc_name, scheduler.current_proc().unwrap().name));
        tui.debug(format!("{:05}: {} was waiting {} clock ticks", clock_tick, scheduler.current_proc().unwrap().name, scheduler.current_proc().unwrap().waiting_time));

        average_waiting_time -= average_waiting_time / num_of_spawned_procs as f64;
        average_waiting_time += scheduler.current_proc().unwrap().waiting_time as f64 / num_of_spawned_procs as f64;
      }

      // increase the waiting time for every process
      scheduler.increase_waiting_times();
    }

    #[cfg(feature = "go-slow")]
    thread::sleep(time::Duration::from_millis(1000 / CLOCK_HZ));

    clock_tick += 1;
  }

  SimulationResult {
    average_waiting_time: average_waiting_time,
    context_switch_num: scheduler.context_switch_num(),
  }
}

/*
 * vi: ts=2 sw=2 expandtab
 */

<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate rocket;
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_codegen;
extern crate dotenv;
extern crate serde_json;
#[macro_use]
extern crate lazy_static;
extern crate rocket_contrib;
#[macro_use]
extern crate serde_derive;
extern crate r2d2;
extern crate r2d2_diesel;

mod schema;
mod db;
mod note;
mod models;
mod error;

use db::DB;
use note::{get_notes, get_note, create_note, delete_note, update_note};
use models::*;
use rocket_contrib::JSON;
use rocket::response::status::NoContent;
use error::Error as ApiError;

#[get("/notes", format = "application/json")]
fn notes_get(db: DB) -> Result<JSON<Vec<Note>>, ApiError> {
    match get_notes(db.conn()) {
        Ok(notes) => Ok(JSON(notes)),
        Err(err) => Err(ApiError::from(err)),
    }
}

#[get("/notes/<id>", format = "application/json")]
fn note_get(db: DB, id: i32) -> Result<JSON<Note>, ApiError> {
    match get_note(db.conn(), id) {
        Ok(note) => Ok(JSON(note)),
        Err(err) => Err(ApiError::from(err)),
    }
}

#[post("/notes", format = "application/json", data = "<note>")]
fn note_create(db: DB, note: NoteData) -> Result<JSON<Note>, ApiError> {
    match create_note(db.conn(), note) {
        Ok(note) => Ok(JSON(note)),
        Err(err) => Err(ApiError::from(err)),
    }
}

#[patch("/notes/<id>", format = "application/json", data = "<note>")]
fn note_edit(db: DB, id: i32, note: NoteData) -> Result<JSON<Note>, ApiError> {
    match update_note(db.conn(), id, note) {
        Ok(note) => Ok(JSON(note)),
        Err(err) => Err(ApiError::from(err)),
    }
}

#[delete("/notes/<id>")]
fn note_delete(db: DB, id: i32) -> Result<NoContent, ApiError> {
    match delete_note(db.conn(), id) {
        Ok(_) => Ok(NoContent),
        Err(err) => Err(ApiError::from(err)),
    }
}

fn main() {
    rocket::ignite()
        .mount("/",
               routes![note_create, notes_get, note_delete, note_edit, note_get])
        .launch();
}
<commit_msg>Return proper 201 Created status for created notes, thanks to sedrik https://github.com/fanderzon/rocket-api/commit/83b32684475e238f1bde95ce6a06b94d0c0d0ef2<commit_after>#![feature(plugin)]
#![plugin(rocket_codegen)]
extern crate rocket;
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_codegen;
extern crate dotenv;
extern crate serde_json;
#[macro_use]
extern crate lazy_static;
extern crate rocket_contrib;
#[macro_use]
extern crate serde_derive;
extern crate r2d2;
extern crate r2d2_diesel;

mod schema;
mod db;
mod note;
mod models;
mod error;

use db::DB;
use note::{get_notes, get_note, create_note, delete_note, update_note};
use models::*;
use rocket_contrib::JSON;
use rocket::response::status::{Created, NoContent};
use error::Error as ApiError;

#[get("/notes", format = "application/json")]
fn notes_get(db: DB) -> Result<JSON<Vec<Note>>, ApiError> {
    match get_notes(db.conn()) {
        Ok(notes) => Ok(JSON(notes)),
        Err(err) => Err(ApiError::from(err)),
    }
}

#[get("/notes/<id>", format = "application/json")]
fn note_get(db: DB, id: i32) -> Result<JSON<Note>, ApiError> {
    match get_note(db.conn(), id) {
        Ok(note) => Ok(JSON(note)),
        Err(err) => Err(ApiError::from(err)),
    }
}

#[post("/notes", format = "application/json", data = "<note>")]
fn note_create(db: DB, note: NoteData) -> Result<Created<JSON<Note>>, ApiError> {
    match create_note(db.conn(), note) {
        Ok(note) => {
            let url = format!("/note/{}", note.id);
            Ok(Created(url, Some(JSON(note))))
        }
        Err(err) => Err(ApiError::from(err)),
    }
}

#[patch("/notes/<id>", format = "application/json", data = "<note>")]
fn note_edit(db: DB, id: i32, note: NoteData) -> Result<JSON<Note>, ApiError> {
    match update_note(db.conn(), id, note) {
        Ok(note) => Ok(JSON(note)),
        Err(err) => Err(ApiError::from(err)),
    }
}

#[delete("/notes/<id>")]
fn note_delete(db: DB, id: i32) -> Result<NoContent, ApiError> {
    match delete_note(db.conn(), id) {
        Ok(_) => Ok(NoContent),
        Err(err) => Err(ApiError::from(err)),
    }
}

fn main() {
    rocket::ignite()
        .mount("/",
               routes![note_create, notes_get, note_delete, note_edit, note_get])
        .launch();
}
<|endoftext|>


<commit_before>extern crate mio;
extern crate bytes;
extern crate log4rs;
#[macro_use]
extern crate log;

use mio::tcp::*;
use mio::{Sender,EventLoop};
use mio::{TryRead,TryWrite};
use mio::util::Slab;
use std::thread;
use std::sync::mpsc::{self,channel};
use std::io::Cursor;

use bytes::{Buf, Take};

const SERVER: mio::Token = mio::Token(0);

#[derive(Debug)]
enum MiChatCommand {
    Set(String),
    Get(mio::Token),
}

#[derive(Debug)]
struct MiChatIoEvent (mio::Token, String);

struct MiChat {
    listener: TcpListener,
    connections: Slab<Connection>,
    commands: mpsc::Sender<MiChatCommand>
}

impl MiChat {
    fn new(listener: TcpListener, commands: mpsc::Sender<MiChatCommand>) -> MiChat {
        MiChat {
            listener: listener,
            connections: Slab::new_starting_at(mio::Token(1), 1024),
            commands: commands
        }
    }
}

struct Connection {
    socket: TcpStream,
    token: mio::Token,
    state: Option<Vec<u8>>,
    write_buf: Vec<u8>,
    commands: mpsc::Sender<MiChatCommand>,
}

impl Connection {
    fn new(socket: TcpStream, token: mio::Token, commands: mpsc::Sender<MiChatCommand>) -> Connection {
        Connection {
            socket: socket,
            token: token,
            state: Some(Vec::with_capacity(1024)),
            write_buf: Vec::new(),
            commands: commands,
        }
    }
    fn ready(&mut self, event_loop: &mut mio::EventLoop<MiChat>, events: mio::EventSet) {
        info!("Connection::ready: {:?}; {:?}", self.socket.peer_addr(), events);
        if events.is_readable() {
            self.read(event_loop)
        }
        if events.is_writable() {
            self.write(event_loop)
        }
    }

    fn read(&mut self, event_loop: &mut mio::EventLoop<MiChat>) {
        let mut abuf = Vec::new();
        match self.socket.try_read_buf(&mut abuf) {
            Ok(Some(0)) => {
                info!("{:?}: EOF!", self.socket.peer_addr());
                self.finish()
            },
            Ok(Some(n)) => {
                info!("{:?}: Read {}bytes", self.socket.peer_addr(), n);
                self.ingest(abuf);
                self.reregister(event_loop)
            },
            Ok(None) => {
                info!("{:?}: Noop!", self.socket.peer_addr());
                self.reregister(event_loop)
            },
            Err(e) => panic!("got an error trying to read; err={:?}", e)
        }
    }

    fn notify(&mut self, event_loop: &mut mio::EventLoop<MiChat>, s: String) {
        self.write_buf.extend(s.bytes());
        self.write_buf.push('\n' as u8);
        self.reregister(event_loop);
    }

    fn write(&mut self, event_loop: &mut mio::EventLoop<MiChat>) {
        match self.socket.try_write(&mut self.write_buf) {
            Ok(Some(n)) => {
                info!("Wrote {} of {} in buffer", n, self.write_buf.len());
                self.reregister(event_loop);
                self.write_buf = self.write_buf[n..].to_vec();
            },
            Ok(None) => {
                info!("Write unready");
                self.reregister(event_loop);
            },
            Err(e) => {
                panic!("got an error trying to write; err={:?}", e);
            }
        }
    }

    fn reregister(&mut self, event_loop: &mut mio::EventLoop<MiChat>) {
        let mut flags = mio::EventSet::readable();
        if !self.write_buf.is_empty() {
            flags = flags | mio::EventSet::writable();
        }

        event_loop.reregister(
                &self.socket,
                self.token,
                flags,
                mio::PollOpt::oneshot()).unwrap()

    }
    fn ingest(&mut self, inbuf: Vec<u8>) {
        match self.state {
            Some(ref mut buf)  => {
                let mut startpos = 0;
                while let Some(n) = inbuf.iter().skip(startpos).position(|e| *e == '\n' as u8) {
                    Self::buffer_slice(buf, &inbuf[startpos..startpos+n]);
                    let s = String::from_utf8_lossy(buf).into_owned();
                    let cmd = if s.is_empty() { MiChatCommand::Get(self.token) } else { MiChatCommand::Set(s) };
                    self.commands.send(cmd).unwrap();
                    startpos += n+1;
                    buf.clear()
                }
                info!("Remainder: {}", String::from_utf8_lossy(&inbuf[startpos..]));
                Self::buffer_slice(buf, &inbuf[startpos..])
            },
            None => panic!("Ingest on closed connection?")
        }
    }

    fn finish(&mut self) {
        match self.state {
            Some(ref mut buf)  => info!("Remainder: {}", String::from_utf8_lossy(buf)),
            _ => ()
        }
        self.state = None

    }

    fn buffer_slice(buf: &mut Vec<u8>, slice: &[u8]) {
        buf.extend(slice.iter().cloned());
    }

    fn is_closed(&self) -> bool {
        self.state.is_none() && self.write_buf.is_empty()
    }
}

impl mio::Handler for MiChat {
    type Timeout = ();
    type Message = MiChatIoEvent;
    fn ready(&mut self, event_loop: &mut mio::EventLoop<Self>, token: mio::Token, events: mio::EventSet) {
        info!("{:?}: {:?}", token, events);
        match token {
            SERVER => {
                // Only receive readable events
                assert!(events.is_readable());

                info!("the listener socket is ready to accept a connection");
                match self.listener.accept() {
                    Ok(Some(socket)) => {
                        let commands = self.commands.clone();
                        let token = self.connections
                            .insert_with(|token| Connection::new(socket, token, commands))
                            .unwrap();

                        event_loop.register_opt(
                            &self.connections[token].socket,
                            token,
                            mio::EventSet::readable(),
                            mio::PollOpt::edge() | mio::PollOpt::oneshot()).unwrap();
                    }
                    Ok(None) => {
                        info!("the listener socket wasn't actually ready");
                    }
                    Err(e) => {
                        info!("listener.accept() errored: {}", e);
                        event_loop.shutdown();
                    }
                }
            }
            _ => {
                self.connections[token].ready(event_loop, events);
                if self.connections[token].is_closed() {
                    self.connections.remove(token);
                }
            }
        }
    }

    fn notify(&mut self, event_loop: &mut EventLoop<Self>, msg: MiChatIoEvent) {
        info!("Notify: {:?}", msg);
        let MiChatIoEvent(token, s) = msg;
        if let Some(conn) = self.connections.get_mut(token) {
            conn.notify(event_loop, s);
        } else {
            warn!("No connection for token: {:?}", token);
        }
    }
}

#[derive(Debug)]
struct Model {
    value: String
}

impl Model {
    fn new() -> Model {
        Model { value: "".to_string() }
    }

    fn process_from(&mut self, rx: mpsc::Receiver<MiChatCommand>, replies: Sender<MiChatIoEvent>) {
        loop {
            let msg = rx.recv().unwrap();
            info!("{:?}; got {:?}", self, msg);
            match msg {
                MiChatCommand::Set(s) => {
                    self.value = s;
                },
                MiChatCommand::Get(tok) => {
                    replies.send(MiChatIoEvent(tok, self.value.clone())).unwrap();
                }
            }
        }
    }
}

const LOG_FILE: &'static str = "log.toml";

fn main() {
    if let Err(e) = log4rs::init_file(LOG_FILE, Default::default()) {
        panic!("Could not init logger from file {}: {}", LOG_FILE, e);
    }

    let address = "0.0.0.0:6567".parse().unwrap();
    let listener = TcpListener::bind(&address).unwrap();

    let mut event_loop = mio::EventLoop::new().unwrap();
    event_loop.register(&listener, SERVER).unwrap();

    info!("running michat listener at: {:?}", address);

    let (command_tx, command_rx) = channel();
    let replies = event_loop.channel();
    let t = thread::Builder::new().name("Model".to_owned()).spawn(move|| {
        let mut model = Model::new();
        model.process_from(command_rx, replies);
    }).unwrap();

    let mut service = MiChat::new(listener, command_tx);
    event_loop.run(&mut service).unwrap();
    t.join().unwrap();
}
<commit_msg>Write responses immediately on receipt when possible.<commit_after>extern crate mio;
extern crate bytes;
extern crate log4rs;
#[macro_use]
extern crate log;

use mio::tcp::*;
use mio::{Sender,EventLoop};
use mio::{TryRead,TryWrite};
use mio::util::Slab;
use std::thread;
use std::sync::mpsc::{self,channel};
use std::io::Cursor;

use bytes::{Buf, Take};

const SERVER: mio::Token = mio::Token(0);

#[derive(Debug)]
enum MiChatCommand {
    Set(String),
    Get(mio::Token),
}

#[derive(Debug)]
struct MiChatIoEvent (mio::Token, String);

struct MiChat {
    listener: TcpListener,
    connections: Slab<Connection>,
    commands: mpsc::Sender<MiChatCommand>
}

impl MiChat {
    fn new(listener: TcpListener, commands: mpsc::Sender<MiChatCommand>) -> MiChat {
        MiChat {
            listener: listener,
            connections: Slab::new_starting_at(mio::Token(1), 1024),
            commands: commands
        }
    }
}

struct Connection {
    socket: TcpStream,
    token: mio::Token,
    state: Option<Vec<u8>>,
    write_buf: Vec<u8>,
    commands: mpsc::Sender<MiChatCommand>,
}

impl Connection {
    fn new(socket: TcpStream, token: mio::Token, commands: mpsc::Sender<MiChatCommand>) -> Connection {
        Connection {
            socket: socket,
            token: token,
            state: Some(Vec::with_capacity(1024)),
            write_buf: Vec::new(),
            commands: commands,
        }
    }
    fn ready(&mut self, event_loop: &mut mio::EventLoop<MiChat>, events: mio::EventSet) {
        info!("Connection::ready: {:?}; {:?}", self.socket.peer_addr(), events);
        if events.is_readable() {
            self.read(event_loop)
        }
        if events.is_writable() {
            self.write(event_loop)
        }
    }

    fn read(&mut self, event_loop: &mut mio::EventLoop<MiChat>) {
        let mut abuf = Vec::new();
        match self.socket.try_read_buf(&mut abuf) {
            Ok(Some(0)) => {
                info!("{:?}: EOF!", self.socket.peer_addr());
                self.finish()
            },
            Ok(Some(n)) => {
                info!("{:?}: Read {}bytes", self.socket.peer_addr(), n);
                self.ingest(abuf);
                self.reregister(event_loop)
            },
            Ok(None) => {
                info!("{:?}: Noop!", self.socket.peer_addr());
                self.reregister(event_loop)
            },
            Err(e) => panic!("got an error trying to read; err={:?}", e)
        }
    }

    fn notify(&mut self, event_loop: &mut mio::EventLoop<MiChat>, s: String) {
        self.write_buf.extend(s.bytes());
        self.write_buf.push('\n' as u8);
        self.write(event_loop);
        self.reregister(event_loop);
    }

    fn write(&mut self, event_loop: &mut mio::EventLoop<MiChat>) {
        match self.socket.try_write(&mut self.write_buf) {
            Ok(Some(n)) => {
                info!("Wrote {} of {} in buffer", n, self.write_buf.len());
                self.write_buf = self.write_buf[n..].to_vec();
                self.reregister(event_loop);
            },
            Ok(None) => {
                info!("Write unready");
                self.reregister(event_loop);
            },
            Err(e) => {
                panic!("got an error trying to write; err={:?}", e);
            }
        }
    }

    fn reregister(&mut self, event_loop: &mut mio::EventLoop<MiChat>) {
        let mut flags = mio::EventSet::readable();
        if !self.write_buf.is_empty() {
            flags = flags | mio::EventSet::writable();
        }

        event_loop.reregister(
                &self.socket,
                self.token,
                flags,
                mio::PollOpt::oneshot()).unwrap()

    }
    fn ingest(&mut self, inbuf: Vec<u8>) {
        match self.state {
            Some(ref mut buf)  => {
                let mut startpos = 0;
                while let Some(n) = inbuf.iter().skip(startpos).position(|e| *e == '\n' as u8) {
                    Self::buffer_slice(buf, &inbuf[startpos..startpos+n]);
                    let s = String::from_utf8_lossy(buf).into_owned();
                    let cmd = if s.is_empty() { MiChatCommand::Get(self.token) } else { MiChatCommand::Set(s) };
                    self.commands.send(cmd).unwrap();
                    startpos += n+1;
                    buf.clear()
                }
                info!("Remainder: {}", String::from_utf8_lossy(&inbuf[startpos..]));
                Self::buffer_slice(buf, &inbuf[startpos..])
            },
            None => panic!("Ingest on closed connection?")
        }
    }

    fn finish(&mut self) {
        match self.state {
            Some(ref mut buf)  => info!("Remainder: {}", String::from_utf8_lossy(buf)),
            _ => ()
        }
        self.state = None

    }

    fn buffer_slice(buf: &mut Vec<u8>, slice: &[u8]) {
        buf.extend(slice.iter().cloned());
    }

    fn is_closed(&self) -> bool {
        self.state.is_none() && self.write_buf.is_empty()
    }
}

impl mio::Handler for MiChat {
    type Timeout = ();
    type Message = MiChatIoEvent;
    fn ready(&mut self, event_loop: &mut mio::EventLoop<Self>, token: mio::Token, events: mio::EventSet) {
        info!("{:?}: {:?}", token, events);
        match token {
            SERVER => {
                // Only receive readable events
                assert!(events.is_readable());

                info!("the listener socket is ready to accept a connection");
                match self.listener.accept() {
                    Ok(Some(socket)) => {
                        let commands = self.commands.clone();
                        let token = self.connections
                            .insert_with(|token| Connection::new(socket, token, commands))
                            .unwrap();

                        event_loop.register_opt(
                            &self.connections[token].socket,
                            token,
                            mio::EventSet::readable(),
                            mio::PollOpt::edge() | mio::PollOpt::oneshot()).unwrap();
                    }
                    Ok(None) => {
                        info!("the listener socket wasn't actually ready");
                    }
                    Err(e) => {
                        info!("listener.accept() errored: {}", e);
                        event_loop.shutdown();
                    }
                }
            }
            _ => {
                self.connections[token].ready(event_loop, events);
                if self.connections[token].is_closed() {
                    self.connections.remove(token);
                }
            }
        }
    }

    fn notify(&mut self, event_loop: &mut EventLoop<Self>, msg: MiChatIoEvent) {
        info!("Notify: {:?}", msg);
        let MiChatIoEvent(token, s) = msg;
        if let Some(conn) = self.connections.get_mut(token) {
            conn.notify(event_loop, s);
        } else {
            warn!("No connection for token: {:?}", token);
        }
    }
}

#[derive(Debug)]
struct Model {
    value: String
}

impl Model {
    fn new() -> Model {
        Model { value: "".to_string() }
    }

    fn process_from(&mut self, rx: mpsc::Receiver<MiChatCommand>, replies: Sender<MiChatIoEvent>) {
        loop {
            let msg = rx.recv().unwrap();
            info!("{:?}; got {:?}", self, msg);
            match msg {
                MiChatCommand::Set(s) => {
                    self.value = s;
                },
                MiChatCommand::Get(tok) => {
                    replies.send(MiChatIoEvent(tok, self.value.clone())).unwrap();
                }
            }
        }
    }
}

const LOG_FILE: &'static str = "log.toml";

fn main() {
    if let Err(e) = log4rs::init_file(LOG_FILE, Default::default()) {
        panic!("Could not init logger from file {}: {}", LOG_FILE, e);
    }

    let address = "0.0.0.0:6567".parse().unwrap();
    let listener = TcpListener::bind(&address).unwrap();

    let mut event_loop = mio::EventLoop::new().unwrap();
    event_loop.register(&listener, SERVER).unwrap();

    info!("running michat listener at: {:?}", address);

    let (command_tx, command_rx) = channel();
    let replies = event_loop.channel();
    let t = thread::Builder::new().name("Model".to_owned()).spawn(move|| {
        let mut model = Model::new();
        model.process_from(command_rx, replies);
    }).unwrap();

    let mut service = MiChat::new(listener, command_tx);
    event_loop.run(&mut service).unwrap();
    t.join().unwrap();
}
<|endoftext|>


<commit_before>#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone)]
enum Suit {
    Clubs,
    Diamonds,
    Hearts,
    Spades,
}

#[derive(Debug, Eq, PartialEq, PartialOrd, Copy, Clone)]
struct Card {
    suit: Suit,
    value: char,
}

impl Card {
    fn new(suit: Suit, value: char) -> Card {
        Card { suit: suit, value: value }
    }
}

fn print_card(card: Card) {
    println!("My card: {:?}", card);
}

fn main() {
    let card = Card::new(Suit::Hearts, 'Q');
    print_card(card);
}
<commit_msg>implement Ord for Card<commit_after>#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone)]
enum Suit {
    Clubs,
    Diamonds,
    Hearts,
    Spades,
}

#[derive(Debug, Eq, PartialEq, PartialOrd, Copy, Clone)]
struct Card {
    suit: Suit,
    value: char,
}

impl Card {
    fn new(suit: Suit, value: char) -> Card {
        Card { suit: suit, value: value }
    }
}

impl Ord for Card {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.value.cmp(&other.value)
    }
}

fn print_card(card: Card) {
    println!("My card: {:?}", card);
}

fn main() {
    let card = Card::new(Suit::Hearts, 'Q');
    print_card(card);
}
<|endoftext|>