<commit_before>use rustc::lint::*;
use rustc::hir::*;
use rustc::ty;
use syntax::ast;
use utils::{is_adjusted, iter_input_pats, match_qpath, match_trait_method, match_type, paths, remove_blocks, snippet,
            span_help_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth};

/// **What it does:** Checks for mapping `clone()` over an iterator.
///
/// **Why is this bad?** It makes the code less readable than using the
/// `.cloned()` adapter.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// x.map(|e| e.clone());
/// ```
declare_lint! {
    pub MAP_CLONE,
    Warn,
    "using `.map(|x| x.clone())` to clone an iterator or option's contents"
}

#[derive(Copy, Clone)]
pub struct Pass;

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        // call to .map()
        if let ExprMethodCall(ref method, _, ref args) = expr.node {
            if method.name == "map" && args.len() == 2 {
                match args[1].node {
                    ExprClosure(_, ref decl, closure_eid, _, _) => {
                        let body = cx.tcx.hir.body(closure_eid);
                        let closure_expr = remove_blocks(&body.value);
                        let ty = cx.tables.pat_ty(&body.arguments[0].pat);
                        if_chain! {
                            // nothing special in the argument, besides reference bindings
                            // (e.g. .map(|&x| x) )
                            if let Some(first_arg) = iter_input_pats(decl, body).next();
                            if let Some(arg_ident) = get_arg_name(&first_arg.pat);
                            // the method is being called on a known type (option or iterator)
                            if let Some(type_name) = get_type_name(cx, expr, &args[0]);
                            then {
                                // look for derefs, for .map(|x| *x)
                                if only_derefs(cx, &*closure_expr, arg_ident) &&
                                    // .cloned() only removes one level of indirection, don't lint on more
                                    walk_ptrs_ty_depth(cx.tables.pat_ty(&first_arg.pat)).1 == 1
                                {
                                    // the argument is not an &mut T
                                    if let ty::TyRef(_, tam) = ty.sty {
                                        if tam.mutbl == MutImmutable {
                                            span_help_and_lint(cx, MAP_CLONE, expr.span, &format!(
                                                "you seem to be using .map() to clone the contents of an {}, consider \
                                                using `.cloned()`", type_name),
                                                &format!("try\n{}.cloned()", snippet(cx, args[0].span, "..")));
                                        }
                                    }
                                }
                                // explicit clone() calls ( .map(|x| x.clone()) )
                                else if let ExprMethodCall(ref clone_call, _, ref clone_args) = closure_expr.node {
                                    if clone_call.name == "clone" &&
                                        clone_args.len() == 1 &&
                                        match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) &&
                                        expr_eq_name(&clone_args[0], arg_ident)
                                    {
                                        span_help_and_lint(cx, MAP_CLONE, expr.span, &format!(
                                            "you seem to be using .map() to clone the contents of an {}, consider \
                                            using `.cloned()`", type_name),
                                            &format!("try\n{}.cloned()", snippet(cx, args[0].span, "..")));
                                    }
                                }
                            }
                        }
                    },
                    ExprPath(ref path) => if match_qpath(path, &paths::CLONE) {
                        let type_name = get_type_name(cx, expr, &args[0]).unwrap_or("_");
                        span_help_and_lint(
                            cx,
                            MAP_CLONE,
                            expr.span,
                            &format!(
                                "you seem to be using .map() to clone the contents of an \
                                 {}, consider using `.cloned()`",
                                type_name
                            ),
                            &format!("try\n{}.cloned()", snippet(cx, args[0].span, "..")),
                        );
                    },
                    _ => (),
                }
            }
        }
    }
}

fn expr_eq_name(expr: &Expr, id: ast::Name) -> bool {
    match expr.node {
        ExprPath(QPath::Resolved(None, ref path)) => {
            let arg_segment = [
                PathSegment {
                    name: id,
                    parameters: None,
                    infer_types: true,
                },
            ];
            !path.is_global() && path.segments[..] == arg_segment
        },
        _ => false,
    }
}

fn get_type_name(cx: &LateContext, expr: &Expr, arg: &Expr) -> Option<&'static str> {
    if match_trait_method(cx, expr, &paths::ITERATOR) {
        Some("iterator")
    } else if match_type(cx, walk_ptrs_ty(cx.tables.expr_ty(arg)), &paths::OPTION) {
        Some("Option")
    } else {
        None
    }
}

fn get_arg_name(pat: &Pat) -> Option<ast::Name> {
    match pat.node {
        PatKind::Binding(_, _, name, None) => Some(name.node),
        PatKind::Ref(ref subpat, _) => get_arg_name(subpat),
        _ => None,
    }
}

fn only_derefs(cx: &LateContext, expr: &Expr, id: ast::Name) -> bool {
    match expr.node {
        ExprUnary(UnDeref, ref subexpr) if !is_adjusted(cx, subexpr) => only_derefs(cx, subexpr, id),
        _ => expr_eq_name(expr, id),
    }
}

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(MAP_CLONE)
    }
}
<commit_msg>Document map_clone known problems #498<commit_after>use rustc::lint::*;
use rustc::hir::*;
use rustc::ty;
use syntax::ast;
use utils::{is_adjusted, iter_input_pats, match_qpath, match_trait_method, match_type, paths, remove_blocks, snippet,
            span_help_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth};

/// **What it does:** Checks for mapping `clone()` over an iterator.
///
/// **Why is this bad?** It makes the code less readable than using the
/// `.cloned()` adapter.
///
/// **Known problems:** Sometimes `.cloned()` requires stricter trait
/// bound than `.map(|e| e.clone())` (which works because of the coercion).
/// See [#498](https://github.com/rust-lang-nursery/rust-clippy/issues/498).
///
/// **Example:**
/// ```rust
/// x.map(|e| e.clone());
/// ```
declare_lint! {
    pub MAP_CLONE,
    Warn,
    "using `.map(|x| x.clone())` to clone an iterator or option's contents"
}

#[derive(Copy, Clone)]
pub struct Pass;

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        // call to .map()
        if let ExprMethodCall(ref method, _, ref args) = expr.node {
            if method.name == "map" && args.len() == 2 {
                match args[1].node {
                    ExprClosure(_, ref decl, closure_eid, _, _) => {
                        let body = cx.tcx.hir.body(closure_eid);
                        let closure_expr = remove_blocks(&body.value);
                        let ty = cx.tables.pat_ty(&body.arguments[0].pat);
                        if_chain! {
                            // nothing special in the argument, besides reference bindings
                            // (e.g. .map(|&x| x) )
                            if let Some(first_arg) = iter_input_pats(decl, body).next();
                            if let Some(arg_ident) = get_arg_name(&first_arg.pat);
                            // the method is being called on a known type (option or iterator)
                            if let Some(type_name) = get_type_name(cx, expr, &args[0]);
                            then {
                                // look for derefs, for .map(|x| *x)
                                if only_derefs(cx, &*closure_expr, arg_ident) &&
                                    // .cloned() only removes one level of indirection, don't lint on more
                                    walk_ptrs_ty_depth(cx.tables.pat_ty(&first_arg.pat)).1 == 1
                                {
                                    // the argument is not an &mut T
                                    if let ty::TyRef(_, tam) = ty.sty {
                                        if tam.mutbl == MutImmutable {
                                            span_help_and_lint(cx, MAP_CLONE, expr.span, &format!(
                                                "you seem to be using .map() to clone the contents of an {}, consider \
                                                using `.cloned()`", type_name),
                                                &format!("try\n{}.cloned()", snippet(cx, args[0].span, "..")));
                                        }
                                    }
                                }
                                // explicit clone() calls ( .map(|x| x.clone()) )
                                else if let ExprMethodCall(ref clone_call, _, ref clone_args) = closure_expr.node {
                                    if clone_call.name == "clone" &&
                                        clone_args.len() == 1 &&
                                        match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) &&
                                        expr_eq_name(&clone_args[0], arg_ident)
                                    {
                                        span_help_and_lint(cx, MAP_CLONE, expr.span, &format!(
                                            "you seem to be using .map() to clone the contents of an {}, consider \
                                            using `.cloned()`", type_name),
                                            &format!("try\n{}.cloned()", snippet(cx, args[0].span, "..")));
                                    }
                                }
                            }
                        }
                    },
                    ExprPath(ref path) => if match_qpath(path, &paths::CLONE) {
                        let type_name = get_type_name(cx, expr, &args[0]).unwrap_or("_");
                        span_help_and_lint(
                            cx,
                            MAP_CLONE,
                            expr.span,
                            &format!(
                                "you seem to be using .map() to clone the contents of an \
                                 {}, consider using `.cloned()`",
                                type_name
                            ),
                            &format!("try\n{}.cloned()", snippet(cx, args[0].span, "..")),
                        );
                    },
                    _ => (),
                }
            }
        }
    }
}

fn expr_eq_name(expr: &Expr, id: ast::Name) -> bool {
    match expr.node {
        ExprPath(QPath::Resolved(None, ref path)) => {
            let arg_segment = [
                PathSegment {
                    name: id,
                    parameters: None,
                    infer_types: true,
                },
            ];
            !path.is_global() && path.segments[..] == arg_segment
        },
        _ => false,
    }
}

fn get_type_name(cx: &LateContext, expr: &Expr, arg: &Expr) -> Option<&'static str> {
    if match_trait_method(cx, expr, &paths::ITERATOR) {
        Some("iterator")
    } else if match_type(cx, walk_ptrs_ty(cx.tables.expr_ty(arg)), &paths::OPTION) {
        Some("Option")
    } else {
        None
    }
}

fn get_arg_name(pat: &Pat) -> Option<ast::Name> {
    match pat.node {
        PatKind::Binding(_, _, name, None) => Some(name.node),
        PatKind::Ref(ref subpat, _) => get_arg_name(subpat),
        _ => None,
    }
}

fn only_derefs(cx: &LateContext, expr: &Expr, id: ast::Name) -> bool {
    match expr.node {
        ExprUnary(UnDeref, ref subexpr) if !is_adjusted(cx, subexpr) => only_derefs(cx, subexpr, id),
        _ => expr_eq_name(expr, id),
    }
}

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(MAP_CLONE)
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use errors::*;

/// Manages tracking of write locks
pub struct WriteLockManager {
  /// Map of port to subscription ids
  write_locks: HashMap<String, String>,
}

impl WriteLockManager {
  /// Create a new WriteLockManager instance
  pub fn new() -> WriteLockManager {
    WriteLockManager { write_locks: HashMap::new() }
  }

  /// Is the port write locked by the given sub_id
  pub fn is_port_write_locked_by(&self, port_name: &String, sub_id: &String) -> bool {
    match self.write_locks.get(port_name) {
      None => false,
      Some(sid) => sid == sub_id,
    }
  }

  /// Is the port write locked at all
  pub fn is_port_write_locked(&self, port_name: &String) -> bool {
    self.write_locks.get(port_name).is_some()
  }

  pub fn is_port_locked_by_someone_else(&self, port_name: &String, sub_id: &String) -> bool {
    match self.write_locks.get(port_name) {
      None => false,
      Some(sid) => sid != sub_id,
    }
  }

  /// Check if sub id has write lock on this port, if it doesn't,
  /// return error
  pub fn check_owns_write_lock(&self, port_name: &String, sub_id: &String) -> Result<()> {
    match self.write_locks.get(port_name) {
      None => Err(ErrorKind::NeedWriteLock(port_name.to_string()).into()),
      Some(sid) => {
        if sid != sub_id {
          Err(ErrorKind::AlreadyWriteLocked(port_name.to_string()).into())
        } else {
          Ok(())
        }
      }
    }
  }

  // Clear a write lock, without checking subscriber id
  pub fn clear_lock(&mut self, port_name: &String) {
    self.write_locks.remove(port_name);
  }

  // Release the write lock for the given port and sub id
  pub fn unlock_port(&mut self, port_name: &String, sub_id: &String) -> Result<()> {
    match self.is_port_locked_by_someone_else(port_name, sub_id) {
      false => {
        self.write_locks.remove(port_name);
        Ok(())
      }
      true => Err(ErrorKind::AlreadyWriteLocked(port_name.to_string()).into()),
    }
  }

  // Release all write locks held by this sub_id
  pub fn unlock_all_ports_for_sub(&mut self, sub_id: &String) {
    let mut to_delete = Vec::<String>::new();
    for port_name in self.write_locks.keys() {
      if self
           .write_locks
           .get(port_name)
           .map(|sid| sid == sub_id)
           .unwrap_or(false) {
        to_delete.push(port_name.to_string());
      }
    }
    for delete_port in to_delete.iter() {
      self.write_locks.remove(delete_port);
    }
  }

  // Try and lock the port
  pub fn lock_port(&mut self, port_name: &String, sub_id: &String) -> Result<()> {
    match self.is_port_locked_by_someone_else(port_name, sub_id) {
      false => {
        self
          .write_locks
          .insert(port_name.to_string(), sub_id.to_string());
        Ok(())
      }
      true => Err(ErrorKind::AlreadyWriteLocked(port_name.to_string()).into()),
    }

  }
}

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

  fn check_not_locked_by_anyone(
    wl_manager: &WriteLockManager,
    port: &String,
    sub_ids: Vec<&String>,
  ) {
    for sub_id in sub_ids.iter() {
      assert_eq!(
        false,
        wl_manager.is_port_write_locked_by(port, sub_id),
        "Port '{}' should not be locked by '{}'",
        port,
        sub_id
      );
      assert_eq!(
        false,
        wl_manager.is_port_locked_by_someone_else(port, sub_id),
        "Port '{}' should not be locked by someone else",
        port
      );

    }
  }

  fn check_locked_by_sub(
    wl_manager: &WriteLockManager,
    port: &String,
    sub_locker: &String,
    sub_ids: Vec<&String>,
  ) {
    assert_eq!(
      true,
      wl_manager.is_port_write_locked_by(port, sub_locker),
      "Port '{}' should not be locked by '{}'",
      port,
      sub_locker
    );
    for sub_id in sub_ids.iter() {
      assert_eq!(
        true,
        wl_manager.is_port_locked_by_someone_else(port, sub_id),
        "Port '{}' should not be locked by someone else",
        port
      );
    }
  }

  #[test]
  fn test_locking() {
    let wl_manager = &mut WriteLockManager::new();
    let sub_id1: String = "SUB_ID1".to_owned();
    let sub_id3: String = "SUB_ID2".to_owned();
    let sub_id2: String = "SUB_ID3".to_owned();
    let port: String = "/dev/TTY_USB".to_owned();
    // Ports should not be locked
    check_not_locked_by_anyone(&wl_manager, &port, vec![&sub_id1, &sub_id2, &sub_id3]);
    // sub_id1 locking a port should work
    assert_eq!(
      true,
      wl_manager
        .lock_port(&port, &sub_id1)
        .map(|_| true)
        .unwrap_or(false),
      "Sub_id '{}' locking port '{}' should succeed",
      sub_id1,
      port
    );
    //Port should now be locked by sub_id1
    check_locked_by_sub(&wl_manager, &port, &sub_id1, vec![&sub_id2, &sub_id3]);
    // sub_id2 should fail locking port already locked
    assert_eq!(
      true,
      wl_manager
        .lock_port(&port, &sub_id2)
        .map(|_| false)
        .unwrap_or(true),
      "Sub_id '{}' locking port '{}' should fail",
      sub_id1,
      port
    );
    // sub_id2 should fail unlocking port locked by sub_id1
    assert_eq!(
      true,
      wl_manager
        .unlock_port(&port, &sub_id2)
        .map(|_| false)
        .unwrap_or(true),
      "Sub_id '{}' unlocking port '{}' should fail",
      sub_id1,
      port
    );
    // sub_id1 should be able to unlock it
    assert_eq!(
      true,
      wl_manager
        .unlock_port(&port, &sub_id1)
        .map(|_| true)
        .unwrap_or(false),
      "Sub_id '{}' unlocking port '{}' should succeed",
      sub_id1,
      port
    );
    // Ports should not be locked
    check_not_locked_by_anyone(&wl_manager, &port, vec![&sub_id1, &sub_id2, &sub_id3]);
    // TODO: Finish testing all other methods
  }

}
<commit_msg>Added convenience method to make releasing owned write locks easy<commit_after>use std::collections::HashMap;

use errors::*;

/// Manages tracking of write locks
pub struct WriteLockManager {
  /// Map of port to subscription ids
  write_locks: HashMap<String, String>,
}

impl WriteLockManager {
  /// Create a new WriteLockManager instance
  pub fn new() -> WriteLockManager {
    WriteLockManager { write_locks: HashMap::new() }
  }

  /// Is the port write locked by the given sub_id
  pub fn is_port_write_locked_by(&self, port_name: &String, sub_id: &String) -> bool {
    match self.write_locks.get(port_name) {
      None => false,
      Some(sid) => sid == sub_id,
    }
  }

  /// Is the port write locked at all
  pub fn is_port_write_locked(&self, port_name: &String) -> bool {
    self.write_locks.get(port_name).is_some()
  }

  pub fn is_port_locked_by_someone_else(&self, port_name: &String, sub_id: &String) -> bool {
    match self.write_locks.get(port_name) {
      None => false,
      Some(sid) => sid != sub_id,
    }
  }

  /// Check if sub id has write lock on this port, if it doesn't,
  /// return error
  pub fn check_owns_write_lock(&self, port_name: &String, sub_id: &String) -> Result<()> {
    match self.write_locks.get(port_name) {
      None => Err(ErrorKind::NeedWriteLock(port_name.to_string()).into()),
      Some(sid) => {
        if sid != sub_id {
          Err(ErrorKind::AlreadyWriteLocked(port_name.to_string()).into())
        } else {
          Ok(())
        }
      }
    }
  }

  // Clear a write lock, without checking subscriber id
  pub fn clear_lock(&mut self, port_name: &String) {
    self.write_locks.remove(port_name);
  }

  // Release the write lock for the given port and sub id
  pub fn unlock_port(&mut self, port_name: &String, sub_id: &String) -> Result<()> {
    match self.is_port_locked_by_someone_else(port_name, sub_id) {
      false => {
        self.write_locks.remove(port_name);
        Ok(())
      }
      true => Err(ErrorKind::AlreadyWriteLocked(port_name.to_string()).into()),
    }
  }

  pub fn unlock_port_if_locked_by(&mut self, port_name: &String, sub_id: &String) {
    if self.is_port_write_locked_by(port_name, sub_id){
      // Should not panic since we are the one who locked it
      self.unlock_port(&port_name, sub_id).unwrap();
    }
  }

  // Release all write locks held by this sub_id
  pub fn unlock_all_ports_for_sub(&mut self, sub_id: &String) {
    let mut to_delete = Vec::<String>::new();
    for port_name in self.write_locks.keys() {
      if self
           .write_locks
           .get(port_name)
           .map(|sid| sid == sub_id)
           .unwrap_or(false) {
        to_delete.push(port_name.to_string());
      }
    }
    for delete_port in to_delete.iter() {
      self.write_locks.remove(delete_port);
    }
  }

  // Try and lock the port
  pub fn lock_port(&mut self, port_name: &String, sub_id: &String) -> Result<()> {
    match self.is_port_locked_by_someone_else(port_name, sub_id) {
      false => {
        self
          .write_locks
          .insert(port_name.to_string(), sub_id.to_string());
        Ok(())
      }
      true => Err(ErrorKind::AlreadyWriteLocked(port_name.to_string()).into()),
    }

  }
}

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

  fn check_not_locked_by_anyone(
    wl_manager: &WriteLockManager,
    port: &String,
    sub_ids: Vec<&String>,
  ) {
    for sub_id in sub_ids.iter() {
      assert_eq!(
        false,
        wl_manager.is_port_write_locked_by(port, sub_id),
        "Port '{}' should not be locked by '{}'",
        port,
        sub_id
      );
      assert_eq!(
        false,
        wl_manager.is_port_locked_by_someone_else(port, sub_id),
        "Port '{}' should not be locked by someone else",
        port
      );

    }
  }

  fn check_locked_by_sub(
    wl_manager: &WriteLockManager,
    port: &String,
    sub_locker: &String,
    sub_ids: Vec<&String>,
  ) {
    assert_eq!(
      true,
      wl_manager.is_port_write_locked_by(port, sub_locker),
      "Port '{}' should not be locked by '{}'",
      port,
      sub_locker
    );
    for sub_id in sub_ids.iter() {
      assert_eq!(
        true,
        wl_manager.is_port_locked_by_someone_else(port, sub_id),
        "Port '{}' should not be locked by someone else",
        port
      );
    }
  }

  #[test]
  fn test_locking() {
    let wl_manager = &mut WriteLockManager::new();
    let sub_id1: String = "SUB_ID1".to_owned();
    let sub_id3: String = "SUB_ID2".to_owned();
    let sub_id2: String = "SUB_ID3".to_owned();
    let port: String = "/dev/TTY_USB".to_owned();
    // Ports should not be locked
    check_not_locked_by_anyone(&wl_manager, &port, vec![&sub_id1, &sub_id2, &sub_id3]);
    // sub_id1 locking a port should work
    assert_eq!(
      true,
      wl_manager
        .lock_port(&port, &sub_id1)
        .map(|_| true)
        .unwrap_or(false),
      "Sub_id '{}' locking port '{}' should succeed",
      sub_id1,
      port
    );
    //Port should now be locked by sub_id1
    check_locked_by_sub(&wl_manager, &port, &sub_id1, vec![&sub_id2, &sub_id3]);
    // sub_id2 should fail locking port already locked
    assert_eq!(
      true,
      wl_manager
        .lock_port(&port, &sub_id2)
        .map(|_| false)
        .unwrap_or(true),
      "Sub_id '{}' locking port '{}' should fail",
      sub_id1,
      port
    );
    // sub_id2 should fail unlocking port locked by sub_id1
    assert_eq!(
      true,
      wl_manager
        .unlock_port(&port, &sub_id2)
        .map(|_| false)
        .unwrap_or(true),
      "Sub_id '{}' unlocking port '{}' should fail",
      sub_id1,
      port
    );
    // sub_id1 should be able to unlock it
    assert_eq!(
      true,
      wl_manager
        .unlock_port(&port, &sub_id1)
        .map(|_| true)
        .unwrap_or(false),
      "Sub_id '{}' unlocking port '{}' should succeed",
      sub_id1,
      port
    );
    // Ports should not be locked
    check_not_locked_by_anyone(&wl_manager, &port, vec![&sub_id1, &sub_id2, &sub_id3]);
    // TODO: Finish testing all other methods
  }

}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fmt;
use std::io;
use std::str;

use crate::wire_format::WireType;

/// `Result` alias for `ProtobufError`
pub type ProtobufResult<T> = Result<T, ProtobufError>;

/// Enum values added here for diagnostic purposes.
/// Users should not depend on specific values.
#[derive(Debug)]
pub enum WireError {
    UnexpectedEof,
    UnexpectedWireType(WireType),
    IncorrectTag(u32),
    // unused since https://github.com/stepancheg/rust-protobuf/issues/318
    IncompleteMap,
    IncorrectVarint,
    Utf8Error,
    InvalidEnumValue(i32),
    OverRecursionLimit,
    TruncatedMessage,
    Other,
}

/// Generic protobuf error
#[derive(Debug)]
pub enum ProtobufError {
    /// I/O error when reading or writing
    IoError(io::Error),
    /// Malformed input
    WireError(WireError),
    /// Protocol contains a string which is not valid UTF-8 string
    Utf8(str::Utf8Error),
    /// Not all required fields of message set.
    MessageNotInitialized(&'static str),
}

impl fmt::Display for ProtobufError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(self, f)
    }
}

impl Error for ProtobufError {
    fn description(&self) -> &str {
        match self {
            // not sure that cause should be included in message
            &ProtobufError::IoError(ref e) => e.description(),
            &ProtobufError::WireError(ref e) => match *e {
                WireError::Utf8Error => "invalid UTF-8 sequence",
                WireError::UnexpectedWireType(..) => "unexpected wire type",
                WireError::InvalidEnumValue(..) => "invalid enum value",
                WireError::IncorrectTag(..) => "incorrect tag",
                WireError::IncorrectVarint => "incorrect varint",
                WireError::IncompleteMap => "incomplete map",
                WireError::UnexpectedEof => "unexpected EOF",
                WireError::OverRecursionLimit => "over recursion limit",
                WireError::TruncatedMessage => "truncated message",
                WireError::Other => "other error",
            },
            &ProtobufError::Utf8(ref e) => &e.description(),
            &ProtobufError::MessageNotInitialized { .. } => "not all message fields set",
        }
    }

    fn cause(&self) -> Option<&dyn Error> {
        match self {
            &ProtobufError::IoError(ref e) => Some(e),
            &ProtobufError::Utf8(ref e) => Some(e),
            &ProtobufError::WireError(..) => None,
            &ProtobufError::MessageNotInitialized { .. } => None,
        }
    }
}

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

impl From<str::Utf8Error> for ProtobufError {
    fn from(err: str::Utf8Error) -> Self {
        ProtobufError::Utf8(err)
    }
}

impl From<ProtobufError> for io::Error {
    fn from(err: ProtobufError) -> Self {
        match err {
            ProtobufError::IoError(e) => e,
            ProtobufError::WireError(e) => {
                io::Error::new(io::ErrorKind::InvalidData, ProtobufError::WireError(e))
            }
            ProtobufError::MessageNotInitialized(message) => io::Error::new(
                io::ErrorKind::InvalidInput,
                ProtobufError::MessageNotInitialized(message),
            ),
            e => io::Error::new(io::ErrorKind::Other, Box::new(e)),
        }
    }
}
<commit_msg>ProtobufError: replace description with Display<commit_after>use std::error::Error;
use std::fmt;
use std::io;
use std::str;

use crate::wire_format::WireType;

/// `Result` alias for `ProtobufError`
pub type ProtobufResult<T> = Result<T, ProtobufError>;

/// Enum values added here for diagnostic purposes.
/// Users should not depend on specific values.
#[derive(Debug)]
pub enum WireError {
    UnexpectedEof,
    UnexpectedWireType(WireType),
    IncorrectTag(u32),
    // unused since https://github.com/stepancheg/rust-protobuf/issues/318
    IncompleteMap,
    IncorrectVarint,
    Utf8Error,
    InvalidEnumValue(i32),
    OverRecursionLimit,
    TruncatedMessage,
    Other,
}

impl fmt::Display for WireError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            WireError::Utf8Error => write!(f, "invalid UTF-8 sequence"),
            WireError::UnexpectedWireType(..) => write!(f, "unexpected wire type"),
            WireError::InvalidEnumValue(..) => write!(f, "invalid enum value"),
            WireError::IncorrectTag(..) => write!(f, "incorrect tag"),
            WireError::IncorrectVarint => write!(f, "incorrect varint"),
            WireError::IncompleteMap => write!(f, "incomplete map"),
            WireError::UnexpectedEof => write!(f, "unexpected EOF"),
            WireError::OverRecursionLimit => write!(f, "over recursion limit"),
            WireError::TruncatedMessage => write!(f, "truncated message"),
            WireError::Other => write!(f, "other error"),
        }
    }
}

/// Generic protobuf error
#[derive(Debug)]
pub enum ProtobufError {
    /// I/O error when reading or writing
    IoError(io::Error),
    /// Malformed input
    WireError(WireError),
    /// Protocol contains a string which is not valid UTF-8 string
    Utf8(str::Utf8Error),
    /// Not all required fields of message set.
    MessageNotInitialized(&'static str),
}

impl fmt::Display for ProtobufError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            // not sure that cause should be included in message
            &ProtobufError::IoError(ref e) => write!(f, "IO error: {}", e),
            &ProtobufError::WireError(ref e) => fmt::Display::fmt(e, f),
            &ProtobufError::Utf8(ref e) => write!(f, "{}", e),
            &ProtobufError::MessageNotInitialized { .. } => write!(f, "not all message fields set"),
        }
    }
}

impl Error for ProtobufError {
    fn cause(&self) -> Option<&dyn Error> {
        match self {
            &ProtobufError::IoError(ref e) => Some(e),
            &ProtobufError::Utf8(ref e) => Some(e),
            &ProtobufError::WireError(..) => None,
            &ProtobufError::MessageNotInitialized { .. } => None,
        }
    }
}

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

impl From<str::Utf8Error> for ProtobufError {
    fn from(err: str::Utf8Error) -> Self {
        ProtobufError::Utf8(err)
    }
}

impl From<ProtobufError> for io::Error {
    fn from(err: ProtobufError) -> Self {
        match err {
            ProtobufError::IoError(e) => e,
            ProtobufError::WireError(e) => {
                io::Error::new(io::ErrorKind::InvalidData, ProtobufError::WireError(e))
            }
            ProtobufError::MessageNotInitialized(message) => io::Error::new(
                io::ErrorKind::InvalidInput,
                ProtobufError::MessageNotInitialized(message),
            ),
            e => io::Error::new(io::ErrorKind::Other, Box::new(e)),
        }
    }
}
<|endoftext|>


<commit_before>#[cfg(feature="pkg-config")]
extern crate "pkg-config" as pkg_config;

fn main() {
    if !build_pkgconfig() {
      println!("cargo:rustc-flags=-l SDL2");
    }
}

#[cfg(not(feature="pkg-config"))]
fn build_pkgconfig() -> bool {
    false
}

#[cfg(feature="pkg-config")]
fn build_pkgconfig() -> bool {
    let opts = pkg_config::default_options("sdl2");
    if pkg_config::find_library_opts("sdl2", &opts).is_err() {
        panic!("Could not find SDL2 via pkgconfig");
    }
    true
}
<commit_msg>Use the new pkgconfig API<commit_after>#[cfg(feature="pkg-config")]
extern crate "pkg-config" as pkg_config;

fn main() {
    if !build_pkgconfig() {
      println!("cargo:rustc-flags=-l SDL2");
    }
}

#[cfg(not(feature="pkg-config"))]
fn build_pkgconfig() -> bool {
    false
}

#[cfg(feature="pkg-config")]
fn build_pkgconfig() -> bool {
    if pkg_config::find_library("sdl2").is_err() {
        panic!("Could not find SDL2 via pkgconfig");
    }
    true
}
<|endoftext|>


<commit_before>use regex::Regex;
use std::path::PathBuf;
use std::result::Result;
use subprocess::{Exec, Redirection};

const LOOP_BACKING_DEVICE_BASENAME: &'static str = "flaglock6-home";
const LOOP_DEVICE_PARTITION_LABEL: &'static str = "fl6";
const RSYNC_DOT_NET_REMOTE_DIR: &'static str = "usw-s007.rsync.net:./";

#[derive(Debug)]
pub enum AuricError {
    Invocation(String),
    Io(String),
    Subprocess(String),
    NotFound,
}

enum AuricOperationMode {
    Mount,
    Unmount,
}

impl From<subprocess::PopenError> for AuricError {
    fn from(err: subprocess::PopenError) -> AuricError {
        AuricError::Subprocess(err.to_string())
    }
}

struct SshfsManager {
    mountpoint: PathBuf,
}

struct LoopManager {
    target: Regex,
    backing: PathBuf,
}

struct LuksManager {
    device: PathBuf,
}

struct AuricImpl {
    sshfs_manager: SshfsManager,
    loop_manager: LoopManager,
    luks_manager: LuksManager,
}

impl SshfsManager {
    pub fn new() -> Result<SshfsManager, AuricError> {
        let xdg_runtime_dir = match std::env::var("XDG_RUNTIME_DIR") {
            Ok(val) => val,
            Err(e) => {
                return Err(AuricError::Invocation(format!(
                    "fatal error getting ${{XDG_RUNTIME_DIR}}: ``{}''",
                    e
                )))
            }
        };
        let mut mountpoint = PathBuf::new();
        mountpoint.push(xdg_runtime_dir);
        mountpoint.push("rsync.net-mountpoint");

        Ok(SshfsManager {
            mountpoint: mountpoint,
        })
    }

    fn ensure_mountpoint_is_dir(&self) -> Result<(), AuricError> {
        if self.mountpoint.is_dir() {
            return Ok(());
        }
        std::fs::create_dir(self.mountpoint.as_path())
            .map_err(|e| AuricError::Io(format!("bad mountpoint: ``{}''", e)))
    }

    // Does _not_ ensure that backing device exists.
    pub fn loop_backing_device_path(&self) -> PathBuf {
        let mut result: PathBuf = self.mountpoint.clone();
        result.push(LOOP_BACKING_DEVICE_BASENAME);
        result
    }

    pub fn loop_backing_device_present(&self) -> bool {
        self.loop_backing_device_path().is_file()
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        self.ensure_mountpoint_is_dir()?;
        let result = Exec::cmd("sshfs")
            .arg("-o")
            .arg("allow_other")
            .arg(RSYNC_DOT_NET_REMOTE_DIR)
            .arg(&self.mountpoint)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "sshfs failed: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("fusermount")
            .arg("-u")
            .arg(&self.mountpoint)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "fusermount -u failed: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

impl LoopManager {
    pub fn new(backing: &PathBuf) -> LoopManager {
        let target = format!(
            r"^(/dev/loop\d)(\s+\d+){{4}}\s+({})\s+",
            backing.to_str().unwrap()
        );
        LoopManager {
            target: Regex::new(&target).unwrap(),
            backing: backing.clone(),
        }
    }

    pub fn find(&self) -> Result<PathBuf, AuricError> {
        let result = Exec::cmd("losetup")
            .arg("-l")
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to list loop devices: ``{}''",
                result.stderr_str()
            )));
        }

        for line in result.stdout_str().lines() {
            if let Some(captures) = self.target.captures(line) {
                return Ok(PathBuf::from(captures.get(1).unwrap().as_str()));
            }
        }
        Err(AuricError::NotFound)
    }

    pub fn attach(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("loop-setup")
            .arg("-f")
            .arg(&self.backing)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to attach loop device: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }

    // Note that the loop device will linger if the volume remains
    // unlocked. Prefer not to call this before locking.
    pub fn detach(&self) -> Result<(), AuricError> {
        let attached_loop = match self.find() {
            Ok(path) => path,
            Err(e) => {
                if let AuricError::NotFound = e {
                    return Ok(());
                }
                return Err(e);
            }
        };
        let result = Exec::cmd("udisksctl")
            .arg("loop-delete")
            .arg("-b")
            .arg(&attached_loop)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to detach loop device: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

impl LuksManager {
    pub fn new() -> LuksManager {
        let mut device = PathBuf::from("/dev/disk/by-label");
        device.push(LOOP_DEVICE_PARTITION_LABEL);
        LuksManager { device: device }
    }

    // `locked_device` is necessary as an external argument because no
    // device mapping exists in a pre-unlock world, so we need to target
    // the loop device directly.
    pub fn unlock(&self, locked_device: &PathBuf) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("unlock")
            .arg("-b")
            .arg(locked_device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to unlock {}: ``{}''",
                locked_device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }

    // No extra argument is needed here (c.f. `self::unlock()`) because
    // we can go straight after the well-known device available in
    // /dev/disk/by-label.
    pub fn lock(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("lock")
            .arg("-b")
            .arg(&self.device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to lock {}: ``{}''",
                self.device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("mount")
            .arg("-b")
            .arg(&self.device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to mount {}: ``{}''",
                self.device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("unmount")
            .arg("-b")
            .arg(&self.device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to unmount {}: ``{}''",
                self.device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

fn get_action() -> Result<AuricOperationMode, AuricError> {
    let mut args_iter = std::env::args();
    args_iter.next();
    if let Some(action) = args_iter.next() {
        match action.as_str() {
            "mount" => return Ok(AuricOperationMode::Mount),
            "unmount" => return Ok(AuricOperationMode::Unmount),
            _ => {
                return Err(AuricError::Invocation(format!(
                    "unknown action: ``{}''",
                    action
                )))
            }
        }
    }
    Err(AuricError::Invocation("no action given".to_string()))
}

impl AuricImpl {
    pub fn new() -> Result<AuricImpl, AuricError> {
        let sshfs_manager = SshfsManager::new()?;
        let loop_backing_device_path = sshfs_manager.loop_backing_device_path();
        Ok(AuricImpl {
            sshfs_manager: sshfs_manager,
            loop_manager: LoopManager::new(&loop_backing_device_path),
            luks_manager: LuksManager::new(),
        })
    }

    fn mount(&self) -> Result<(), AuricError> {
        self.sshfs_manager.mount()?;
        self.loop_manager.attach()?;
        let loop_device = self.loop_manager.find()?;
        self.luks_manager.unlock(&loop_device)?;
        self.luks_manager.mount()
    }

    fn unmount(&self) -> Result<(), AuricError> {
        self.luks_manager.unmount()?;
        self.luks_manager.lock()?;
        self.loop_manager.detach()?;
        self.sshfs_manager.unmount()
    }

    pub fn act(&self, mode: AuricOperationMode) -> Result<(), AuricError> {
        match mode {
            AuricOperationMode::Mount => return self.mount(),
            AuricOperationMode::Unmount => return self.unmount(),
        }
    }
}

fn main_impl() -> Result<(), AuricError> {
    let action = get_action()?;
    let auric_impl = AuricImpl::new()?;
    auric_impl.act(action)
}

fn main() {
    std::process::exit(match main_impl() {
        Ok(_) => 0,
        Err(e) => {
            eprintln!("{:?}", e);
            1
        }
    });
}
<commit_msg>apricot-auric: Add graceful early failing<commit_after>use regex::Regex;
use std::path::PathBuf;
use std::result::Result;
use subprocess::{Exec, Redirection};

const LOOP_BACKING_DEVICE_BASENAME: &'static str = "flaglock6-home";
const LOOP_DEVICE_PARTITION_LABEL: &'static str = "fl6";
const RSYNC_DOT_NET_REMOTE_DIR: &'static str = "usw-s007.rsync.net:./";

#[derive(Debug)]
pub enum AuricError {
    Invocation(String),
    Io(String),
    Subprocess(String),
    NotFound,
}

enum AuricOperationMode {
    Mount,
    Unmount,
}

impl From<subprocess::PopenError> for AuricError {
    fn from(err: subprocess::PopenError) -> AuricError {
        AuricError::Subprocess(err.to_string())
    }
}

struct SshfsManager {
    mountpoint: PathBuf,
}

struct LoopManager {
    target: Regex,
    backing: PathBuf,
}

struct LuksManager {
    device: PathBuf,
}

struct AuricImpl {
    sshfs_manager: SshfsManager,
    loop_manager: LoopManager,
    luks_manager: LuksManager,
}

impl SshfsManager {
    pub fn new() -> Result<SshfsManager, AuricError> {
        let xdg_runtime_dir = match std::env::var("XDG_RUNTIME_DIR") {
            Ok(val) => val,
            Err(e) => {
                return Err(AuricError::Invocation(format!(
                    "fatal error getting ${{XDG_RUNTIME_DIR}}: ``{}''",
                    e
                )))
            }
        };
        let mut mountpoint = PathBuf::new();
        mountpoint.push(xdg_runtime_dir);
        mountpoint.push("rsync.net-mountpoint");

        Ok(SshfsManager {
            mountpoint: mountpoint,
        })
    }

    fn ensure_mountpoint_is_dir(&self) -> Result<(), AuricError> {
        if self.mountpoint.is_dir() {
            return Ok(());
        }
        std::fs::create_dir(self.mountpoint.as_path())
            .map_err(|e| AuricError::Io(format!("bad mountpoint: ``{}''", e)))
    }

    // Does _not_ ensure that backing device exists.
    pub fn loop_backing_device_path(&self) -> PathBuf {
        let mut result: PathBuf = self.mountpoint.clone();
        result.push(LOOP_BACKING_DEVICE_BASENAME);
        result
    }

    fn loop_backing_device_present(&self) -> bool {
        self.loop_backing_device_path().is_file()
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        self.ensure_mountpoint_is_dir()?;
        if self.loop_backing_device_present() {
            return Ok(());
        }

        let result = Exec::cmd("sshfs")
            .arg("-o")
            .arg("allow_other")
            .arg(RSYNC_DOT_NET_REMOTE_DIR)
            .arg(&self.mountpoint)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "sshfs failed: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        if !self.loop_backing_device_present() {
            return Ok(());
        }

        let result = Exec::cmd("fusermount")
            .arg("-u")
            .arg(&self.mountpoint)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "fusermount -u failed: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

impl LoopManager {
    pub fn new(backing: &PathBuf) -> LoopManager {
        let target = format!(
            r"^(/dev/loop\d)(\s+\d+){{4}}\s+({})\s+",
            backing.to_str().unwrap()
        );
        LoopManager {
            target: Regex::new(&target).unwrap(),
            backing: backing.clone(),
        }
    }

    pub fn find(&self) -> Result<PathBuf, AuricError> {
        let result = Exec::cmd("losetup")
            .arg("-l")
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to list loop devices: ``{}''",
                result.stderr_str()
            )));
        }

        for line in result.stdout_str().lines() {
            if let Some(captures) = self.target.captures(line) {
                return Ok(PathBuf::from(captures.get(1).unwrap().as_str()));
            }
        }
        Err(AuricError::NotFound)
    }

    pub fn attach(&self) -> Result<(), AuricError> {
        if let Ok(_attached_loop) = self.find() {
            return Ok(());
        }

        let result = Exec::cmd("udisksctl")
            .arg("loop-setup")
            .arg("-f")
            .arg(&self.backing)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to attach loop device: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }

    // Note that the loop device will linger if the volume remains
    // unlocked. Prefer not to call this before locking.
    pub fn detach(&self) -> Result<(), AuricError> {
        let attached_loop = match self.find() {
            Ok(path) => path,
            Err(e) => {
                if let AuricError::NotFound = e {
                    return Ok(());
                }
                return Err(e);
            }
        };
        let result = Exec::cmd("udisksctl")
            .arg("loop-delete")
            .arg("-b")
            .arg(&attached_loop)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to detach loop device: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

impl LuksManager {
    pub fn new() -> LuksManager {
        let mut device = PathBuf::from("/dev/disk/by-label");
        device.push(LOOP_DEVICE_PARTITION_LABEL);
        LuksManager { device: device }
    }

    // `locked_device` is necessary as an external argument because no
    // device mapping exists in a pre-unlock world, so we need to target
    // the loop device directly.
    pub fn unlock(&self, locked_device: &PathBuf) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("unlock")
            .arg("-b")
            .arg(locked_device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to unlock {}: ``{}''",
                locked_device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }

    // No extra argument is needed here (c.f. `self::unlock()`) because
    // we can go straight after the well-known device available in
    // /dev/disk/by-label.
    pub fn lock(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("lock")
            .arg("-b")
            .arg(&self.device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to lock {}: ``{}''",
                self.device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("mount")
            .arg("-b")
            .arg(&self.device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to mount {}: ``{}''",
                self.device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("unmount")
            .arg("-b")
            .arg(&self.device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to unmount {}: ``{}''",
                self.device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

fn get_action() -> Result<AuricOperationMode, AuricError> {
    let mut args_iter = std::env::args();
    args_iter.next();
    if let Some(action) = args_iter.next() {
        match action.as_str() {
            "mount" => return Ok(AuricOperationMode::Mount),
            "unmount" => return Ok(AuricOperationMode::Unmount),
            _ => {
                return Err(AuricError::Invocation(format!(
                    "unknown action: ``{}''",
                    action
                )))
            }
        }
    }
    Err(AuricError::Invocation("no action given".to_string()))
}

impl AuricImpl {
    pub fn new() -> Result<AuricImpl, AuricError> {
        let sshfs_manager = SshfsManager::new()?;
        let loop_backing_device_path = sshfs_manager.loop_backing_device_path();
        Ok(AuricImpl {
            sshfs_manager: sshfs_manager,
            loop_manager: LoopManager::new(&loop_backing_device_path),
            luks_manager: LuksManager::new(),
        })
    }

    fn mount(&self) -> Result<(), AuricError> {
        self.sshfs_manager.mount()?;
        self.loop_manager.attach()?;
        let loop_device = self.loop_manager.find()?;
        self.luks_manager.unlock(&loop_device)?;
        self.luks_manager.mount()
    }

    fn unmount(&self) -> Result<(), AuricError> {
        self.luks_manager.unmount()?;
        self.luks_manager.lock()?;
        self.loop_manager.detach()?;
        self.sshfs_manager.unmount()
    }

    pub fn act(&self, mode: AuricOperationMode) -> Result<(), AuricError> {
        match mode {
            AuricOperationMode::Mount => return self.mount(),
            AuricOperationMode::Unmount => return self.unmount(),
        }
    }
}

fn main_impl() -> Result<(), AuricError> {
    let action = get_action()?;
    let auric_impl = AuricImpl::new()?;
    auric_impl.act(action)
}

fn main() {
    std::process::exit(match main_impl() {
        Ok(_) => 0,
        Err(e) => {
            eprintln!("{:?}", e);
            1
        }
    });
}
<|endoftext|>


<commit_before>macro_rules! assert_const {
    ($len:expr) => {
        assert!($len > 0);
        debug_assert!($len < 0);
    };
}

fn main() {
    assert!(true);
    assert!(false);
    assert!(true, "true message");
    assert!(false, "false message");

    const B: bool = true;
    assert!(B);

    const C: bool = false;
    assert!(C);

    debug_assert!(true);
    assert_const!(3);
    assert_const!(-1);
}
<commit_msg>Add test for debug_assert!(false)<commit_after>macro_rules! assert_const {
    ($len:expr) => {
        assert!($len > 0);
        debug_assert!($len < 0);
    };
}

fn main() {
    assert!(true);
    assert!(false);
    assert!(true, "true message");
    assert!(false, "false message");

    const B: bool = true;
    assert!(B);

    const C: bool = false;
    assert!(C);

    debug_assert!(true);
    // Don't lint this, since there is no better way for expressing "Only panic in debug mode".
    debug_assert!(false); // #3948
    assert_const!(3);
    assert_const!(-1);
}
<|endoftext|>


<commit_before>use crate::error::Error;
use log::debug;
use std::io::{self, BufRead};
use std::net::{TcpListener, TcpStream};
use std::thread;
use std::time::{Duration, Instant};

pub struct Terminal {
    connection: TcpStream,
    data: String,
}

impl Terminal {
    pub fn create<F>(run_in_terminal: F) -> Result<Self, Error>
    where
        F: FnOnce(Vec<String>) -> Result<(), Error>,
    {
        let mut listener = TcpListener::bind("127.0.0.1:0")?;
        let addr = listener.local_addr()?;

        // Run codelldb in a terminal agent mode, which sends back the tty device name (Unix)
        // or its own process id (Windows), then waits till the socket gets closed from our end.
        let executable = std::env::current_exe()?.to_str().unwrap().into();
        let cmd = vec![executable, "terminal-agent".into(), format!("--port={}", addr.port())];
        run_in_terminal(cmd)?;

        let stream = accept_with_timeout(&mut listener, Duration::from_millis(5000))?;
        let stream2 = stream.try_clone()?;

        let mut reader = io::BufReader::new(stream);
        let mut data = String::new();
        reader.read_line(&mut data)?;

        Ok(Terminal {
            connection: stream2,
            data: data.trim().to_owned(),
        })
    }

    pub fn input_devname(&self) -> &str {
        if cfg!(windows) {
            "CONIN$"
        } else {
            &self.data
        }
    }

    pub fn output_devname(&self) -> &str {
        if cfg!(windows) {
            "CONOUT$"
        } else {
            &self.data
        }
    }

    pub fn attach<F, R>(&self, f: F) -> R
    where
        F: FnOnce() -> R,
    {
        // Windows does not have an API for launching a child process attached to another console.
        // Instead,
        #[cfg(windows)]
        {
            use winapi::um::wincon::{AttachConsole, FreeConsole};
            let pid = self.data.parse::<u32>().unwrap();
            unsafe {
                dbg!(FreeConsole());
                dbg!(AttachConsole(pid));
            }
            let result = f();
            unsafe {
                dbg!(FreeConsole());
            }
            result
        }

        #[cfg(not(windows))]
        f()
    }
}

// No set_accept_timeout() in std :(
fn accept_with_timeout(listener: &mut TcpListener, timeout: Duration) -> Result<TcpStream, Error> {
    listener.set_nonblocking(true)?;
    let timeout = Duration::from_millis(5000);
    let started = Instant::now();
    let stream = loop {
        match listener.accept() {
            Ok((stream, _addr)) => break stream,
            Err(e) => {
                if e.kind() != io::ErrorKind::WouldBlock {
                    return Err(e.into());
                } else {
                    thread::sleep(Duration::from_millis(100));
                }
            }
        }
        if started.elapsed() > timeout {
            return Err(Error::Internal("Terminal agent did not respond within the allotted time.".into()));
        }
    };
    Ok(stream)
}
<commit_msg>Reset nonblocking after accept.<commit_after>use crate::error::Error;
use log::debug;
use std::io::{self, BufRead};
use std::net::{TcpListener, TcpStream};
use std::thread;
use std::time::{Duration, Instant};

pub struct Terminal {
    connection: TcpStream,
    data: String,
}

impl Terminal {
    pub fn create<F>(run_in_terminal: F) -> Result<Self, Error>
    where
        F: FnOnce(Vec<String>) -> Result<(), Error>,
    {
        let mut listener = TcpListener::bind("127.0.0.1:0")?;
        let addr = listener.local_addr()?;

        // Run codelldb in a terminal agent mode, which sends back the tty device name (Unix)
        // or its own process id (Windows), then waits till the socket gets closed from our end.
        let executable = std::env::current_exe()?.to_str().unwrap().into();
        let cmd = vec![executable, "terminal-agent".into(), format!("--port={}", addr.port())];
        run_in_terminal(cmd)?;

        let stream = accept_with_timeout(&mut listener, Duration::from_millis(5000))?;
        let stream2 = stream.try_clone()?;

        let mut reader = io::BufReader::new(stream);
        let mut data = String::new();
        reader.read_line(&mut data)?;

        Ok(Terminal {
            connection: stream2,
            data: data.trim().to_owned(),
        })
    }

    pub fn input_devname(&self) -> &str {
        if cfg!(windows) {
            "CONIN$"
        } else {
            &self.data
        }
    }

    pub fn output_devname(&self) -> &str {
        if cfg!(windows) {
            "CONOUT$"
        } else {
            &self.data
        }
    }

    pub fn attach<F, R>(&self, f: F) -> R
    where
        F: FnOnce() -> R,
    {
        // Windows does not have an API for launching a child process attached to another console.
        // Instead,
        #[cfg(windows)]
        {
            use winapi::um::wincon::{AttachConsole, FreeConsole};
            let pid = self.data.parse::<u32>().unwrap();
            unsafe {
                dbg!(FreeConsole());
                dbg!(AttachConsole(pid));
            }
            let result = f();
            unsafe {
                dbg!(FreeConsole());
            }
            result
        }

        #[cfg(not(windows))]
        f()
    }
}

// No set_accept_timeout() in std :(
fn accept_with_timeout(listener: &mut TcpListener, timeout: Duration) -> Result<TcpStream, Error> {
    listener.set_nonblocking(true)?;
    let timeout = Duration::from_millis(5000);
    let started = Instant::now();
    let stream = loop {
        match listener.accept() {
            Ok((stream, _addr)) => break stream,
            Err(e) => {
                if e.kind() != io::ErrorKind::WouldBlock {
                    return Err(e.into());
                } else {
                    thread::sleep(Duration::from_millis(100));
                }
            }
        }
        if started.elapsed() > timeout {
            return Err(Error::Internal("Terminal agent did not respond within the allotted time.".into()));
        }
    };
    stream.set_nonblocking(false)?;
    Ok(stream)
}
<|endoftext|>


<commit_before>extern crate libc;

use std::ptr;
use libc::{c_void, size_t, c_int};
use libc::{PROT_READ, PROT_WRITE};
use libc::{MAP_PRIVATE, MAP_ANONYMOUS, MAP_GROWSDOWN, MAP_STACK};
use libc::{CLONE_NEWUSER, CLONE_NEWNS, CLONE_NEWPID, SIGCHLD};

fn mmap_stack(stack_size: size_t) -> *mut c_void {
    unsafe {
        let p = libc::mmap(ptr::null_mut(), stack_size, PROT_READ | PROT_WRITE,
                           MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN | MAP_STACK, -1, 0);
        assert!(p != libc::MAP_FAILED);
        //TODO add guard pages
        p
    }

}

// a lot copied from man USER_NAMESPACES(7)

use std::thread;

extern "C" fn child_func(args: *mut c_void) -> c_int {
    println!("I'm called from child_func");

    let args = args as *mut ChildArgs;
    let r_pipe_fd = unsafe { (*args).r_pipe_fd };
    let w_pipe_fd = unsafe { (*args).w_pipe_fd };
    println!("r_pipe_fd: {}", r_pipe_fd);

    // run everything in a new thread so exceptions bubble up to rust
    let h = thread::spawn(move || {
        // wait for parent
        println!("waiting for parent to set up mapping, ...");
        assert_eq!(unsafe { libc::close(w_pipe_fd) } , 0);
        unsafe {
            let mut buf = 0u8;
            libc::read(r_pipe_fd, (&mut buf) as *mut u8 as *mut c_void, 1);
        }

        panic!("Oops!");
    });

    match h.join() {
        Ok(_) => 0,
        Err(_) => 1,
    }
}

struct ChildArgs {
    r_pipe_fd: c_int,
    w_pipe_fd: c_int,
}

fn main() {
    println!("Hello, world!");

    let mut pipe_fd = vec![0;2];  /* Pipe used to synchronize parent and child */
    assert_eq!(unsafe { libc::pipe(pipe_fd.as_mut_ptr()) }, 0);
    println!("r pipe: {} w pipe: {}", pipe_fd[0], pipe_fd[1]);
    let ptr_child_args = {
        let child_args = Box::new(ChildArgs { r_pipe_fd: pipe_fd[0], w_pipe_fd: pipe_fd[1]});
        Box::into_raw(child_args) as *mut c_void
    };

    let stack_size = 4 * 1024 * 1024;
    let child_stack = mmap_stack(stack_size);

    // stack grows down
    let child_stack_top = unsafe { child_stack.offset(stack_size as isize) };

    let child_pid = unsafe {
        libc::clone(child_func, child_stack_top, CLONE_NEWUSER|CLONE_NEWNS|CLONE_NEWPID|SIGCHLD, ptr_child_args)
    };
    assert!(child_pid != -1);

    let mut child_status: c_int = 0;
    assert!(unsafe { libc::waitpid(child_pid, &mut child_status, 0) } != -1);
    println!("Child terminated: {}", child_status);

    unsafe {
        drop(Box::from_raw(ptr_child_args));
    }

    //TODO munmap child_stack
}
<commit_msg>setting up mapping for child<commit_after>extern crate libc;

use std::ptr;
use libc::{c_void, size_t, c_int};
use libc::{PROT_READ, PROT_WRITE};
use libc::{MAP_PRIVATE, MAP_ANONYMOUS, MAP_GROWSDOWN, MAP_STACK};
use libc::{CLONE_NEWUSER, CLONE_NEWNS, CLONE_NEWPID, SIGCHLD};
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};

fn mmap_stack(stack_size: size_t) -> *mut c_void {
    unsafe {
        let p = libc::mmap(ptr::null_mut(), stack_size, PROT_READ | PROT_WRITE,
                           MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN | MAP_STACK, -1, 0);
        assert!(p != libc::MAP_FAILED);
        //TODO add guard pages
        p
    }

}

// a lot copied from man USER_NAMESPACES(7)

use std::thread;

extern "C" fn child_func(args: *mut c_void) -> c_int {
    println!("I'm called from child_func");

    let args = args as *mut ChildArgs;
    let r_pipe_fd = unsafe { (*args).r_pipe_fd };
    let w_pipe_fd = unsafe { (*args).w_pipe_fd };
    println!("r_pipe_fd: {}", r_pipe_fd);

    // run everything in a new thread so exceptions bubble up to rust
    let h = thread::spawn(move || {
        // wait for parent
        println!("waiting for parent to set up mapping, ...");
        assert_eq!(unsafe { libc::close(w_pipe_fd) } , 0);
        unsafe {
            let mut buf = 0u8;
            libc::read(r_pipe_fd, (&mut buf) as *mut u8 as *mut c_void, 1);
        }

        {
            println!("Child status");
            let mut fstatus = File::open("/proc/self/status").expect("open status");
            let mut contents = String::new();
            fstatus.read_to_string(&mut contents).expect("read status");
            for l in contents.lines(){
                if l.starts_with("Seccomp") || l.starts_with("Cap"){
                    println!("{}", l);
                }
            }
        }

        panic!("Oops!");
    });

    match h.join() {
        Ok(_) => 0,
        Err(_) => 1,
    }
}

struct ChildArgs {
    r_pipe_fd: c_int,
    w_pipe_fd: c_int,
}

fn write(path: &String, content: &[u8]) {
    println!("Writing to {}", path);
    let mut file = OpenOptions::new().read(false).write(true).create(false).open(path).expect("open");
    // needs to happen in one write call
    assert_eq!(file.write(content).expect("write"), content.len());
    //file gets dropped
}
fn main() {
    println!("Hello, world!");

    let mut pipe_fd = vec![0;2];  /* Pipe used to synchronize parent and child */
    assert_eq!(unsafe { libc::pipe(pipe_fd.as_mut_ptr()) }, 0);
    println!("r pipe: {} w pipe: {}", pipe_fd[0], pipe_fd[1]);
    let ptr_child_args = {
        let child_args = Box::new(ChildArgs { r_pipe_fd: pipe_fd[0], w_pipe_fd: pipe_fd[1]});
        Box::into_raw(child_args) as *mut c_void
    };

    let stack_size = 4 * 1024 * 1024;
    let child_stack = mmap_stack(stack_size);

    // stack grows down
    let child_stack_top = unsafe { child_stack.offset(stack_size as isize) };

    let child_pid = unsafe {
        libc::clone(child_func, child_stack_top, CLONE_NEWUSER|CLONE_NEWNS|CLONE_NEWPID|SIGCHLD, ptr_child_args)
    };
    assert!(child_pid != -1);

    let uid = unsafe { libc::getuid() };
    write(&format!("/proc/{}/uid_map", child_pid), &format!("0 {} 1", uid).into_bytes());

    //proc_setgroups_write man user_namespaces
    write(&format!("/proc/{}/setgroups", child_pid), &format!("deny").into_bytes());

    let gid = unsafe { libc::getgid() };
    write(&format!("/proc/{}/gid_map", child_pid), &format!("0 {} 1", gid).into_bytes());

    //unleash child
    assert_eq!(unsafe { libc::close(pipe_fd[1]) }, 0);

    let mut child_status: c_int = 0;
    assert!(unsafe { libc::waitpid(child_pid, &mut child_status, 0) } != -1);
    println!("Child terminated: {}", child_status);

    unsafe {
        drop(Box::from_raw(ptr_child_args));
    }

    //TODO munmap child_stack
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(clippy)]

#![deny(warnings)]
#![allow(dead_code)]
#![allow(unused_variables)] // Temporary fix for rustc false positive. To be removed.

use std::hash::{Hash, Hasher};

#[derive(PartialEq, Hash)]
struct Foo;

impl PartialEq<u64> for Foo {
    fn eq(&self, _: &u64) -> bool { true }
}

#[derive(Hash)]
//~^ ERROR you are deriving `Hash` but have implemented `PartialEq` explicitly
struct Bar;

impl PartialEq for Bar {
    fn eq(&self, _: &Bar) -> bool { true }
}

#[derive(Hash)]
//~^ ERROR you are deriving `Hash` but have implemented `PartialEq` explicitly
struct Baz;

impl PartialEq<Baz> for Baz {
    fn eq(&self, _: &Baz) -> bool { true }
}

#[derive(PartialEq)]
struct Bah;

impl Hash for Bah {
//~^ ERROR you are implementing `Hash` explicitly but have derived `PartialEq`
    fn hash<H: Hasher>(&self, _: &mut H) {}
}

#[derive(Copy)]
struct Qux;

impl Clone for Qux {
//~^ ERROR you are implementing `Clone` explicitly on a `Copy` type
    fn clone(&self) -> Self { Qux }
}

// See #666
#[derive(Copy)]
struct Lt<'a> {
    a: &'a u8,
}

impl<'a> Clone for Lt<'a> {
//~^ ERROR you are implementing `Clone` explicitly on a `Copy` type
    fn clone(&self) -> Self { unimplemented!() }
}

// Ok, `Clone` cannot be derived because of the big array
#[derive(Copy)]
struct BigArray {
    a: [u8; 65],
}

impl Clone for BigArray {
    fn clone(&self) -> Self { unimplemented!() }
}

// Ok, function pointers are not always Clone
#[derive(Copy)]
struct FnPtr {
    a: fn() -> !,
}

impl Clone for FnPtr {
    fn clone(&self) -> Self { unimplemented!() }
}

// Ok, generics
#[derive(Copy)]
struct Generic<T> {
    a: T,
}

impl<T> Clone for Generic<T> {
    fn clone(&self) -> Self { unimplemented!() }
}

fn main() {}
<commit_msg>Remove temporary fix now unneeded<commit_after>#![feature(plugin)]
#![plugin(clippy)]

#![deny(warnings)]
#![allow(dead_code)]

use std::hash::{Hash, Hasher};

#[derive(PartialEq, Hash)]
struct Foo;

impl PartialEq<u64> for Foo {
    fn eq(&self, _: &u64) -> bool { true }
}

#[derive(Hash)]
//~^ ERROR you are deriving `Hash` but have implemented `PartialEq` explicitly
struct Bar;

impl PartialEq for Bar {
    fn eq(&self, _: &Bar) -> bool { true }
}

#[derive(Hash)]
//~^ ERROR you are deriving `Hash` but have implemented `PartialEq` explicitly
struct Baz;

impl PartialEq<Baz> for Baz {
    fn eq(&self, _: &Baz) -> bool { true }
}

#[derive(PartialEq)]
struct Bah;

impl Hash for Bah {
//~^ ERROR you are implementing `Hash` explicitly but have derived `PartialEq`
    fn hash<H: Hasher>(&self, _: &mut H) {}
}

#[derive(Copy)]
struct Qux;

impl Clone for Qux {
//~^ ERROR you are implementing `Clone` explicitly on a `Copy` type
    fn clone(&self) -> Self { Qux }
}

// See #666
#[derive(Copy)]
struct Lt<'a> {
    a: &'a u8,
}

impl<'a> Clone for Lt<'a> {
//~^ ERROR you are implementing `Clone` explicitly on a `Copy` type
    fn clone(&self) -> Self { unimplemented!() }
}

// Ok, `Clone` cannot be derived because of the big array
#[derive(Copy)]
struct BigArray {
    a: [u8; 65],
}

impl Clone for BigArray {
    fn clone(&self) -> Self { unimplemented!() }
}

// Ok, function pointers are not always Clone
#[derive(Copy)]
struct FnPtr {
    a: fn() -> !,
}

impl Clone for FnPtr {
    fn clone(&self) -> Self { unimplemented!() }
}

// Ok, generics
#[derive(Copy)]
struct Generic<T> {
    a: T,
}

impl<T> Clone for Generic<T> {
    fn clone(&self) -> Self { unimplemented!() }
}

fn main() {}
<|endoftext|>


<commit_before>use std::fs;
use std::str;
use std::io::Read;
use std::io::Write;
use std::collections::HashSet;
use serde_json;
use mio::{PollOpt,Ready,Token};
use nom::{HexDisplay,IResult,Offset};

use sozu::messages::Order;
use sozu::network::ProxyOrder;
use sozu::network::buffer::Buffer;
use sozu_command::config::Config;
use sozu_command::data::{AnswerData,ConfigCommand,ConfigMessage,ConfigMessageAnswer,ConfigMessageStatus,PROTOCOL_VERSION,RunState,WorkerInfo};

use super::{CommandServer,FrontToken,ProxyConfiguration,StoredProxy};
use super::client::parse;
use worker::start_worker;

impl CommandServer {
  pub fn handle_client_message(&mut self, token: FrontToken, message: &ConfigMessage) {
    let config_command = message.data.clone();
    match config_command {
      ConfigCommand::SaveState(path) => {
        if let Ok(mut f) = fs::File::create(&path) {
          let mut seen = HashSet::new();
          let mut stored_proxies: Vec<StoredProxy> = Vec::new();

          for &(ref tag, ref proxy) in  self.proxies.values() {
            if !seen.contains(&tag) {
              seen.insert(tag);
              stored_proxies.push( StoredProxy::from_proxy(&proxy) );
            }
          }

          let mut counter = 0usize;
          for proxy in stored_proxies {
            for command in proxy.state.generate_orders() {
              let message = ConfigMessage::new(
                format!("SAVE-{}", counter),
                ConfigCommand::ProxyConfiguration(command),
                Some(proxy.tag.to_string()),
                None
              );
              f.write_all(&serde_json::to_string(&message).map(|s| s.into_bytes()).unwrap_or(vec!()));
              f.write_all(&b"\n\0"[..]);
              counter += 1;
            }
            f.sync_all();
          }
          self.conns[token].write_message(&ConfigMessageAnswer::new(
            message.id.clone(),
            ConfigMessageStatus::Ok,
            format!("saved to {}", path),
            None
          ));
        } else {
          error!("could not open file: {}", &path);
          self.conns[token].write_message(&ConfigMessageAnswer::new(
            message.id.clone(),
            ConfigMessageStatus::Error,
            "could not open file".to_string(),
            None
          ));
        }
      },
      ConfigCommand::DumpState => {
        let mut seen = HashSet::new();
        let mut stored_proxies: Vec<StoredProxy> = Vec::new();

        for &(ref tag, ref proxy) in  self.proxies.values() {
          if !seen.contains(&tag) {
            seen.insert(tag);
            stored_proxies.push( StoredProxy::from_proxy(&proxy) );
          }
        }

        let conf = ProxyConfiguration {
          id:      message.id.clone(),
          proxies: stored_proxies,
        };
        //let encoded = serde_json::to_string(&conf).map(|s| s.into_bytes()).unwrap_or(vec!());
        self.conns[token].write_message(&ConfigMessageAnswer::new(
          message.id.clone(),
          ConfigMessageStatus::Ok,
          serde_json::to_string(&conf).unwrap_or(String::new()),
          None
        ));
        //self.conns[token].write_message(&encoded);
      },
      ConfigCommand::LoadState(path) => {
        self.load_state(&message.id, &path);
        self.conns[token].write_message(&ConfigMessageAnswer::new(
          message.id.clone(),
          ConfigMessageStatus::Ok,
          "loaded the configuration".to_string(),
          None
        ));
      },
      ConfigCommand::ListWorkers => {
        let workers: Vec<WorkerInfo> = self.proxies.values().map(|&(ref tag, ref proxy)| {
          WorkerInfo {
            tag:        proxy.tag.clone(),
            id:         proxy.id,
            proxy_type: proxy.proxy_type,
            pid:        proxy.pid,
            run_state:  proxy.run_state.clone(),
          }
        }).collect();
        self.conns[token].write_message(&ConfigMessageAnswer::new(
          message.id.clone(),
          ConfigMessageStatus::Ok,
          "".to_string(),
          Some(AnswerData::Workers(workers))
        ));
      },
      ConfigCommand::LaunchWorker(tag) => {
        info!("received LaunchWorker with tag \"{}\"", tag);

        let id = self.next_ids.get(&tag).unwrap_or(&0) + 1;
        if let Some(mut worker) = self.config.proxies.get(&tag).and_then(|config| start_worker(&tag, config, id)) {
          self.conns[token].write_message(&ConfigMessageAnswer::new(
            message.id.clone(),
            ConfigMessageStatus::Processing,
            "sending configuration orders".to_string(),
            None
          ));
          info!("created new worker");

          *self.next_ids.get_mut(&tag).unwrap() += 1;

          let worker_token = self.token_count + 1;
          self.token_count = worker_token;
          worker.token     = Some(Token(worker_token));
          self.poll.register(&worker.channel.sock, Token(worker_token), Ready::all(), PollOpt::edge()).unwrap();

          if let Some(&(_, ref previous)) = self.proxies.values().filter(|&&(ref ptag, ref proxy)| {
            ptag == &tag && proxy.run_state == RunState::Running
          }).next() {
            worker.channel.set_blocking(true);

            let mut counter = 0u32;
            for order in previous.state.generate_orders() {
              let message_id = format!("LAUNCH-CONF-{}", counter);
              worker.inflight.insert(message_id.clone(), order.clone());
              let o = order.clone();
              //info!("sending to new worker({}-{}): {} ->  {:?}", tag, worker.id, message_id, order);
              self.conns[token].add_message_id(message_id.clone());
              worker.state.handle_order(&o);
              if !worker.channel.write_message(&ProxyOrder { id: message_id.clone(), order: o }) {
                error!("could not send to new worker({}-{}): {}", tag, worker.id, message_id);
              }

              //worker.channel.run();
              counter += 1;
            }
            worker.channel.set_blocking(false);
          }

          self.proxies.insert(Token(worker_token), (tag, worker));

          self.conns[token].write_message(&ConfigMessageAnswer::new(
            message.id.clone(),
            ConfigMessageStatus::Ok,
            "".to_string(),
            None
          ));
        } else {
          self.conns[token].write_message(&ConfigMessageAnswer::new(
            message.id.clone(),
            ConfigMessageStatus::Error,
            "failed creating worker".to_string(),
            None
          ));
        }
      },
      ConfigCommand::ProxyConfiguration(order) => {
        if let Some(ref tag) = message.proxy {
          if let &Order::AddTlsFront(ref data) = &order {
            info!("received AddTlsFront(TlsFront {{ app_id: {}, hostname: {}, path_begin: {} }}) with tag {:?}",
            data.app_id, data.hostname, data.path_begin, tag);
          } else {
            info!("received {:?} with tag {:?}", order, tag);
          }

          let mut found = false;
          for &mut (ref proxy_tag, ref mut proxy) in self.proxies.values_mut() {
            if tag == proxy_tag {
              if let Some(id) = message.proxy_id {
                if id != proxy.id {
                  continue;
                }
              }

              if order == Order::SoftStop || order == Order::HardStop {
                proxy.run_state = RunState::Stopping;
              }

              proxy.inflight.insert(message.id.clone(), order.clone());
              let o = order.clone();
              self.conns[token].add_message_id(message.id.clone());
              proxy.state.handle_order(&o);
              proxy.channel.write_message(&ProxyOrder { id: message.id.clone(), order: o });
              proxy.channel.run();
              found = true;
            }
          }

          if !found {
            // FIXME: should send back error here
            error!("no proxy found for tag: {}", tag);
          }

        } else {
          // FIXME: should send back error here
          error!("expecting proxy tag");
        }
      }
    }
  }

  pub fn load_state(&mut self, message_id: &str, path: &str) {
    match fs::File::open(&path) {
      Err(e)   => error!("cannot open file at path '{}': {:?}", path, e),
      Ok(mut file) => {
        //let mut data = vec!();
        let mut buffer = Buffer::with_capacity(16384);
        loop {
          let previous = buffer.available_data();
          //FIXME: we should read in streaming here
          if let Ok(sz) = file.read(buffer.space()) {
            buffer.fill(sz);
          } else {
            error!("error reading state file");
            break;
          }

          if buffer.available_data() == 0 {
            break;
          }

          let mut offset = 0;
          match parse(buffer.data()) {
            IResult::Done(i, o) => {
              if i.len() > 0 {
                info!("could not parse {} bytes", i.len());
                if previous == buffer.available_data() {
                  break;
                }
              }
              offset = buffer.data().offset(i);

              for message in o {
                if let ConfigCommand::ProxyConfiguration(order) = message.data {
                  if let Some(ref tag) = message.proxy {
                    if let &Order::AddTlsFront(ref data) = &order {
                      info!("received AddTlsFront(TlsFront {{ app_id: {}, hostname: {}, path_begin: {} }}) with tag {:?}",
                      data.app_id, data.hostname, data.path_begin, tag);
                    } else {
                      info!("received {:?} with tag {:?}", order, tag);
                    }
                    let mut found = false;
                    for &mut (ref proxy_tag, ref mut proxy) in self.proxies.values_mut() {
                      if tag == proxy_tag {
                        let o = order.clone();
                        proxy.state.handle_order(&o);
                        proxy.channel.write_message(&ProxyOrder { id: message.id.clone(), order: o });
                        proxy.channel.run();
                        found = true;
                      }
                    }

                    if !found {
                      // FIXME: should send back error here
                      error!("no proxy found for tag: {}", tag);
                    }

                  } else {
                    // FIXME: should send back error here
                     error!("expecting proxy tag");
                  }
                }
              }
            },
            IResult::Incomplete(_) => {
              if buffer.available_data() == buffer.capacity() {
                error!("message too big, stopping parsing:\n{}", buffer.data().to_hex(16));
                break;
              }
            }
            IResult::Error(e) => {
              error!("saved state parse error: {:?}", e);
              break;
            },
          }
          buffer.consume(offset);
        }
      }
    }
  }

  pub fn load_static_application_configuration(&mut self) {
    //FIXME: too many loops, this could be cleaner
    for message in self.config.generate_config_messages() {
      if let ConfigCommand::ProxyConfiguration(order) = message.data {
        if let Some(ref tag) = message.proxy {
          if let &Order::AddTlsFront(ref data) = &order {
            info!("received AddTlsFront(TlsFront {{ app_id: {}, hostname: {}, path_begin: {} }}) with tag {:?}",
            data.app_id, data.hostname, data.path_begin, tag);
          } else {
            info!("received {:?} with tag {:?}", order, tag);
          }
          let mut found = false;
          for &mut (ref proxy_tag, ref mut proxy) in self.proxies.values_mut() {
            if tag == proxy_tag {
              let o = order.clone();
              proxy.state.handle_order(&o);
              proxy.channel.write_message(&ProxyOrder { id: message.id.clone(), order: o });
              proxy.channel.run();
              found = true;
            }
          }

          if !found {
            // FIXME: should send back error here
            error!("no proxy found for tag: {}", tag);
          }

        } else {
          // FIXME: should send back error here
          error!("expecting proxy tag");
        }
      }
    }
  }
}
<commit_msg>move worker registration<commit_after>use std::fs;
use std::str;
use std::io::Read;
use std::io::Write;
use std::collections::HashSet;
use serde_json;
use mio::{PollOpt,Ready,Token};
use nom::{HexDisplay,IResult,Offset};

use sozu::messages::Order;
use sozu::network::ProxyOrder;
use sozu::network::buffer::Buffer;
use sozu_command::config::Config;
use sozu_command::data::{AnswerData,ConfigCommand,ConfigMessage,ConfigMessageAnswer,ConfigMessageStatus,PROTOCOL_VERSION,RunState,WorkerInfo};

use super::{CommandServer,FrontToken,ProxyConfiguration,StoredProxy};
use super::client::parse;
use worker::start_worker;

impl CommandServer {
  pub fn handle_client_message(&mut self, token: FrontToken, message: &ConfigMessage) {
    let config_command = message.data.clone();
    match config_command {
      ConfigCommand::SaveState(path) => {
        if let Ok(mut f) = fs::File::create(&path) {
          let mut seen = HashSet::new();
          let mut stored_proxies: Vec<StoredProxy> = Vec::new();

          for &(ref tag, ref proxy) in  self.proxies.values() {
            if !seen.contains(&tag) {
              seen.insert(tag);
              stored_proxies.push( StoredProxy::from_proxy(&proxy) );
            }
          }

          let mut counter = 0usize;
          for proxy in stored_proxies {
            for command in proxy.state.generate_orders() {
              let message = ConfigMessage::new(
                format!("SAVE-{}", counter),
                ConfigCommand::ProxyConfiguration(command),
                Some(proxy.tag.to_string()),
                None
              );
              f.write_all(&serde_json::to_string(&message).map(|s| s.into_bytes()).unwrap_or(vec!()));
              f.write_all(&b"\n\0"[..]);
              counter += 1;
            }
            f.sync_all();
          }
          self.conns[token].write_message(&ConfigMessageAnswer::new(
            message.id.clone(),
            ConfigMessageStatus::Ok,
            format!("saved to {}", path),
            None
          ));
        } else {
          error!("could not open file: {}", &path);
          self.conns[token].write_message(&ConfigMessageAnswer::new(
            message.id.clone(),
            ConfigMessageStatus::Error,
            "could not open file".to_string(),
            None
          ));
        }
      },
      ConfigCommand::DumpState => {
        let mut seen = HashSet::new();
        let mut stored_proxies: Vec<StoredProxy> = Vec::new();

        for &(ref tag, ref proxy) in  self.proxies.values() {
          if !seen.contains(&tag) {
            seen.insert(tag);
            stored_proxies.push( StoredProxy::from_proxy(&proxy) );
          }
        }

        let conf = ProxyConfiguration {
          id:      message.id.clone(),
          proxies: stored_proxies,
        };
        //let encoded = serde_json::to_string(&conf).map(|s| s.into_bytes()).unwrap_or(vec!());
        self.conns[token].write_message(&ConfigMessageAnswer::new(
          message.id.clone(),
          ConfigMessageStatus::Ok,
          serde_json::to_string(&conf).unwrap_or(String::new()),
          None
        ));
        //self.conns[token].write_message(&encoded);
      },
      ConfigCommand::LoadState(path) => {
        self.load_state(&message.id, &path);
        self.conns[token].write_message(&ConfigMessageAnswer::new(
          message.id.clone(),
          ConfigMessageStatus::Ok,
          "loaded the configuration".to_string(),
          None
        ));
      },
      ConfigCommand::ListWorkers => {
        let workers: Vec<WorkerInfo> = self.proxies.values().map(|&(ref tag, ref proxy)| {
          WorkerInfo {
            tag:        proxy.tag.clone(),
            id:         proxy.id,
            proxy_type: proxy.proxy_type,
            pid:        proxy.pid,
            run_state:  proxy.run_state.clone(),
          }
        }).collect();
        self.conns[token].write_message(&ConfigMessageAnswer::new(
          message.id.clone(),
          ConfigMessageStatus::Ok,
          "".to_string(),
          Some(AnswerData::Workers(workers))
        ));
      },
      ConfigCommand::LaunchWorker(tag) => {
        info!("received LaunchWorker with tag \"{}\"", tag);

        let id = self.next_ids.get(&tag).unwrap_or(&0) + 1;
        if let Some(mut worker) = self.config.proxies.get(&tag).and_then(|config| start_worker(&tag, config, id)) {
          self.conns[token].write_message(&ConfigMessageAnswer::new(
            message.id.clone(),
            ConfigMessageStatus::Processing,
            "sending configuration orders".to_string(),
            None
          ));
          info!("created new worker");

          *self.next_ids.get_mut(&tag).unwrap() += 1;

          let worker_token = self.token_count + 1;
          self.token_count = worker_token;
          worker.token     = Some(Token(worker_token));

          if let Some(&(_, ref previous)) = self.proxies.values().filter(|&&(ref ptag, ref proxy)| {
            ptag == &tag && proxy.run_state == RunState::Running
          }).next() {
            worker.channel.set_blocking(true);

            let mut counter = 0u32;
            for order in previous.state.generate_orders() {
              let message_id = format!("LAUNCH-CONF-{}", counter);
              worker.inflight.insert(message_id.clone(), order.clone());
              let o = order.clone();
              //info!("sending to new worker({}-{}): {} ->  {:?}", tag, worker.id, message_id, order);
              self.conns[token].add_message_id(message_id.clone());
              worker.state.handle_order(&o);
              if !worker.channel.write_message(&ProxyOrder { id: message_id.clone(), order: o }) {
                error!("could not send to new worker({}-{}): {}", tag, worker.id, message_id);
              }

              let received = worker.channel.read_message();
              info!("worker ({}-{}) sent: {:?}", tag, worker.id, received);
              //worker.channel.run();
              counter += 1;
            }
            worker.channel.set_blocking(false);
          }

          info!("registering new sock {:?} at token {:?} for tag {} and id {} (sock error: {:?})", worker.channel.sock,
            worker_token, tag, worker.id, worker.channel.sock.take_error());
          self.poll.register(&worker.channel.sock, Token(worker_token), Ready::all(), PollOpt::edge()).unwrap();
          self.proxies.insert(Token(worker_token), (tag, worker));

          self.conns[token].write_message(&ConfigMessageAnswer::new(
            message.id.clone(),
            ConfigMessageStatus::Ok,
            "".to_string(),
            None
          ));
        } else {
          self.conns[token].write_message(&ConfigMessageAnswer::new(
            message.id.clone(),
            ConfigMessageStatus::Error,
            "failed creating worker".to_string(),
            None
          ));
        }
      },
      ConfigCommand::ProxyConfiguration(order) => {
        if let Some(ref tag) = message.proxy {
          if let &Order::AddTlsFront(ref data) = &order {
            info!("received AddTlsFront(TlsFront {{ app_id: {}, hostname: {}, path_begin: {} }}) with tag {:?}",
            data.app_id, data.hostname, data.path_begin, tag);
          } else {
            info!("received client order {:?} with tag {:?}", order, tag);
          }

          let mut found = false;
          for &mut (ref proxy_tag, ref mut proxy) in self.proxies.values_mut() {
            if tag == proxy_tag {
              if let Some(id) = message.proxy_id {
                if id != proxy.id {
                  continue;
                }
              }

              if order == Order::SoftStop || order == Order::HardStop {
                proxy.run_state = RunState::Stopping;
              }

              proxy.inflight.insert(message.id.clone(), order.clone());
              let o = order.clone();
              self.conns[token].add_message_id(message.id.clone());
              proxy.state.handle_order(&o);
              proxy.channel.write_message(&ProxyOrder { id: message.id.clone(), order: o });
              proxy.channel.run();
              found = true;
            }
          }

          if !found {
            // FIXME: should send back error here
            error!("no proxy found for tag: {}", tag);
          }

        } else {
          // FIXME: should send back error here
          error!("expecting proxy tag");
        }
      }
    }
  }

  pub fn load_state(&mut self, message_id: &str, path: &str) {
    match fs::File::open(&path) {
      Err(e)   => error!("cannot open file at path '{}': {:?}", path, e),
      Ok(mut file) => {
        //let mut data = vec!();
        let mut buffer = Buffer::with_capacity(16384);
        loop {
          let previous = buffer.available_data();
          //FIXME: we should read in streaming here
          if let Ok(sz) = file.read(buffer.space()) {
            buffer.fill(sz);
          } else {
            error!("error reading state file");
            break;
          }

          if buffer.available_data() == 0 {
            break;
          }

          let mut offset = 0;
          match parse(buffer.data()) {
            IResult::Done(i, o) => {
              if i.len() > 0 {
                info!("could not parse {} bytes", i.len());
                if previous == buffer.available_data() {
                  break;
                }
              }
              offset = buffer.data().offset(i);

              for message in o {
                if let ConfigCommand::ProxyConfiguration(order) = message.data {
                  if let Some(ref tag) = message.proxy {
                    if let &Order::AddTlsFront(ref data) = &order {
                      info!("received AddTlsFront(TlsFront {{ app_id: {}, hostname: {}, path_begin: {} }}) with tag {:?}",
                      data.app_id, data.hostname, data.path_begin, tag);
                    } else {
                      info!("received {:?} with tag {:?}", order, tag);
                    }
                    let mut found = false;
                    for &mut (ref proxy_tag, ref mut proxy) in self.proxies.values_mut() {
                      if tag == proxy_tag {
                        let o = order.clone();
                        proxy.state.handle_order(&o);
                        proxy.channel.write_message(&ProxyOrder { id: message.id.clone(), order: o });
                        proxy.channel.run();
                        found = true;
                      }
                    }

                    if !found {
                      // FIXME: should send back error here
                      error!("no proxy found for tag: {}", tag);
                    }

                  } else {
                    // FIXME: should send back error here
                     error!("expecting proxy tag");
                  }
                }
              }
            },
            IResult::Incomplete(_) => {
              if buffer.available_data() == buffer.capacity() {
                error!("message too big, stopping parsing:\n{}", buffer.data().to_hex(16));
                break;
              }
            }
            IResult::Error(e) => {
              error!("saved state parse error: {:?}", e);
              break;
            },
          }
          buffer.consume(offset);
        }
      }
    }
  }

  pub fn load_static_application_configuration(&mut self) {
    //FIXME: too many loops, this could be cleaner
    for message in self.config.generate_config_messages() {
      if let ConfigCommand::ProxyConfiguration(order) = message.data {
        if let Some(ref tag) = message.proxy {
          if let &Order::AddTlsFront(ref data) = &order {
            info!("received AddTlsFront(TlsFront {{ app_id: {}, hostname: {}, path_begin: {} }}) with tag {:?}",
            data.app_id, data.hostname, data.path_begin, tag);
          } else {
            info!("received {:?} with tag {:?}", order, tag);
          }
          let mut found = false;
          for &mut (ref proxy_tag, ref mut proxy) in self.proxies.values_mut() {
            if tag == proxy_tag {
              let o = order.clone();
              proxy.state.handle_order(&o);
              proxy.channel.write_message(&ProxyOrder { id: message.id.clone(), order: o });
              proxy.channel.run();
              found = true;
            }
          }

          if !found {
            // FIXME: should send back error here
            error!("no proxy found for tag: {}", tag);
          }

        } else {
          // FIXME: should send back error here
          error!("expecting proxy tag");
        }
      }
    }
  }
}
<|endoftext|>