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

//! Mining plugin manager, using the cuckoo-miner crate to provide
//! a mining worker implementation
//!

use chrono::prelude::Utc;
use chrono::Duration;
use std::io;
use std::net::{SocketAddr, ToSocketAddrs};
use std::str;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{mpsc, Arc};
use std::thread;
use std::time;

use api;

use p2p;
use pool::DandelionConfig;
use util::LOGGER;

const SEEDS_URL: &'static str = "http://grin-tech.org/seeds.txt";
// DNS Seeds with contact email associated
const DNS_SEEDS: &'static [&'static str] = &[
	"t3.seed.grin-tech.org", // igno.peverell@protonmail.com
];

pub fn connect_and_monitor(
	p2p_server: Arc<p2p::Server>,
	capabilities: p2p::Capabilities,
	dandelion_config: DandelionConfig,
	seed_list: Box<Fn() -> Vec<SocketAddr> + Send>,
	preferred_peers: Option<Vec<SocketAddr>>,
	stop: Arc<AtomicBool>,
) {
	let _ = thread::Builder::new()
		.name("seed".to_string())
		.spawn(move || {
			let peers = p2p_server.peers.clone();

			// open a channel with a listener that connects every peer address sent below
			// max peer count
			let (tx, rx) = mpsc::channel();

			// check seeds first
			connect_to_seeds_and_preferred_peers(
				peers.clone(),
				tx.clone(),
				seed_list,
				preferred_peers.clone(),
			);

			let mut prev = Utc::now() - Duration::seconds(60);
			let mut start_attempt = 0;
			let start_wait_base: i64 = 2;
			loop {
				let current_time = Utc::now();

				// make several attempts to get peers as quick as possible with
				// exponential backoff
				if (peers.peer_count() < p2p_server.config.peer_min_preferred_count()
					&& current_time - prev > Duration::seconds(start_wait_base.pow(start_attempt)))
					|| current_time - prev > Duration::seconds(20)
				{
					// try to connect to any address sent to the channel
					listen_for_addrs(peers.clone(), p2p_server.clone(), capabilities, &rx);

					// monitor additional peers if we need to add more
					monitor_peers(
						peers.clone(),
						p2p_server.config.clone(),
						capabilities,
						tx.clone(),
						preferred_peers.clone(),
					);

					update_dandelion_relay(peers.clone(), dandelion_config.clone());

					prev = current_time;
					if start_attempt < 6 {
						start_attempt += 1;
					}
				}

				thread::sleep(time::Duration::from_secs(1));

				if stop.load(Ordering::Relaxed) {
					break;
				}
			}
		});
}

fn monitor_peers(
	peers: Arc<p2p::Peers>,
	config: p2p::P2PConfig,
	capabilities: p2p::Capabilities,
	tx: mpsc::Sender<SocketAddr>,
	preferred_peers_list: Option<Vec<SocketAddr>>,
) {
	// regularly check if we need to acquire more peers  and if so, gets
	// them from db
	let total_count = peers.all_peers().len();
	let mut healthy_count = 0;
	let mut banned_count = 0;
	let mut defunct_count = 0;
	for x in peers.all_peers() {
		match x.flags {
			p2p::State::Banned => {
				let interval = Utc::now().timestamp() - x.last_banned;
				// Unban peer
				if interval >= config.ban_window() {
					peers.unban_peer(&x.addr);
					debug!(
						LOGGER,
						"monitor_peers: unbanned {} after {} seconds", x.addr, interval
					);
				} else {
					banned_count += 1;
				}
			}
			p2p::State::Healthy => healthy_count += 1,
			p2p::State::Defunct => defunct_count += 1,
		}
	}

	debug!(
		LOGGER,
		"monitor_peers: on {}:{}, {} connected ({} most_work). \
		 all {} = {} healthy + {} banned + {} defunct",
		config.host,
		config.port,
		peers.peer_count(),
		peers.most_work_peers().len(),
		total_count,
		healthy_count,
		banned_count,
		defunct_count,
	);

	// maintenance step first, clean up p2p server peers
	peers.clean_peers(config.peer_max_count() as usize);

	// not enough peers, getting more from db
	if peers.peer_count() >= config.peer_min_preferred_count() {
		return;
	}

	// loop over connected peers
	// ask them for their list of peers
	let mut connected_peers: Vec<SocketAddr> = vec![];
	for p in peers.connected_peers() {
		if let Ok(p) = p.try_read() {
			debug!(
				LOGGER,
				"monitor_peers: {}:{} ask {} for more peers", config.host, config.port, p.info.addr,
			);
			let _ = p.send_peer_request(capabilities);
			connected_peers.push(p.info.addr)
		} else {
			warn!(LOGGER, "monitor_peers: failed to get read lock on peer");
		}
	}

	// Attempt to connect to preferred peers if there is some
	match preferred_peers_list {
		Some(preferred_peers) => {
			for mut p in preferred_peers {
				if !connected_peers.is_empty() {
					if !connected_peers.contains(&p) {
						tx.send(p).unwrap();
					}
				} else {
					tx.send(p).unwrap();
				}
			}
		}
		None => debug!(LOGGER, "monitor_peers: no preferred peers"),
	}

	// find some peers from our db
	// and queue them up for a connection attempt
	let new_peers = peers.find_peers(
		p2p::State::Healthy,
		p2p::Capabilities::UNKNOWN,
		config.peer_max_count() as usize,
	);
	for p in new_peers.iter().filter(|p| !peers.is_known(&p.addr)) {
		debug!(
			LOGGER,
			"monitor_peers: on {}:{}, queue to soon try {}", config.host, config.port, p.addr,
		);
		tx.send(p.addr).unwrap();
	}
}

fn update_dandelion_relay(peers: Arc<p2p::Peers>, dandelion_config: DandelionConfig) {
	// Dandelion Relay Updater
	let dandelion_relay = peers.get_dandelion_relay();
	if dandelion_relay.is_empty() {
		debug!(LOGGER, "monitor_peers: no dandelion relay updating");
		peers.update_dandelion_relay();
	} else {
		for last_added in dandelion_relay.keys() {
			let dandelion_interval = Utc::now().timestamp() - last_added;
			if dandelion_interval >= dandelion_config.relay_secs.unwrap() as i64 {
				debug!(LOGGER, "monitor_peers: updating expired dandelion relay");
				peers.update_dandelion_relay();
			}
		}
	}
}

// Check if we have any pre-existing peer in db. If so, start with those,
// otherwise use the seeds provided.
fn connect_to_seeds_and_preferred_peers(
	peers: Arc<p2p::Peers>,
	tx: mpsc::Sender<SocketAddr>,
	seed_list: Box<Fn() -> Vec<SocketAddr>>,
	peers_preferred_list: Option<Vec<SocketAddr>>,
) {
	// check if we have some peers in db
	let peers = peers.find_peers(p2p::State::Healthy, p2p::Capabilities::FULL_HIST, 100);

	// if so, get their addresses, otherwise use our seeds
	let mut peer_addrs = if peers.len() > 3 {
		peers.iter().map(|p| p.addr).collect::<Vec<_>>()
	} else {
		seed_list()
	};

	// If we have preferred peers add them to the connection
	match peers_preferred_list {
		Some(mut peers_preferred) => peer_addrs.append(&mut peers_preferred),
		None => debug!(LOGGER, "No preferred peers"),
	};

	if peer_addrs.len() == 0 {
		warn!(LOGGER, "No seeds were retrieved.");
	}

	// connect to this first set of addresses
	for addr in peer_addrs {
		tx.send(addr).unwrap();
	}
}

/// Regularly poll a channel receiver for new addresses and initiate a
/// connection if the max peer count isn't exceeded. A request for more
/// peers is also automatically sent after connection.
fn listen_for_addrs(
	peers: Arc<p2p::Peers>,
	p2p: Arc<p2p::Server>,
	capab: p2p::Capabilities,
	rx: &mpsc::Receiver<SocketAddr>,
) {
	let pc = peers.peer_count();
	for addr in rx.try_iter() {
		if pc < p2p.config.peer_max_count() {
			let peers_c = peers.clone();
			let p2p_c = p2p.clone();
			let _ = thread::Builder::new()
				.name("peer_connect".to_string())
				.spawn(move || {
					let mut connect_retry_count = 0;
					loop {
						let connect_peer = p2p_c.connect(&addr);
						match connect_peer {
							Ok(p) => {
								trace!(
									LOGGER,
									"connect_and_req: on {}:{}. connect to {} ok. attempting send_peer_request",
									p2p_c.config.host,
									p2p_c.config.port,
									addr
								);
								if let Ok(p) = p.try_read() {
									let _ = p.send_peer_request(capab);
								}
								let _ = peers_c.update_state(addr, p2p::State::Healthy);
								break;
							}
							Err(e) => {
								debug!(
									LOGGER,
									"connect_and_req: on {}:{}. connect to {} is Defunct. {:?}",
									p2p_c.config.host,
									p2p_c.config.port,
									addr,
									e,
								);
								let _ = peers_c.update_state(addr, p2p::State::Defunct);

								// don't retry if connection refused or PeerWithSelf
								match e {
									p2p::Error::Connection(io_err) => {
										if io::ErrorKind::ConnectionRefused == io_err.kind() {
											break;
										}
									}
									p2p::Error::PeerWithSelf => break,
									_ => continue,
								}
							}
						}

						// retry for 3 times
						thread::sleep(time::Duration::from_secs(1));
						connect_retry_count += 1;
						if connect_retry_count >= 3 {
							break;
						}
						debug!(
							LOGGER,
							"connect_and_req: on {}:{}. connect to {} retrying {}",
							p2p_c.config.host,
							p2p_c.config.port,
							addr,
							connect_retry_count,
						);
					}
				});
		}
	}
}

pub fn dns_seeds() -> Box<Fn() -> Vec<SocketAddr> + Send> {
	Box::new(|| {
		let mut addresses: Vec<SocketAddr> = vec![];
		for dns_seed in DNS_SEEDS {
			let temp_addresses = addresses.clone();
			debug!(LOGGER, "Retrieving seed nodes from dns {}", dns_seed);
			match (dns_seed.to_owned(), 0).to_socket_addrs() {
				Ok(addrs) => addresses.append(
					&mut (addrs
						.map(|mut addr| {
							addr.set_port(13414);
							addr
						})
						.filter(|addr| !temp_addresses.contains(addr))
						.collect()),
				),
				Err(e) => debug!(
					LOGGER,
					"Failed to resolve seed {:?} got error {:?}", dns_seed, e
				),
			}
		}
		addresses
	})
}

/// Extract the list of seeds from a pre-defined text file available through
/// http. Easy method until we have a set of DNS names we can rely on.
pub fn web_seeds() -> Box<Fn() -> Vec<SocketAddr> + Send> {
	Box::new(|| {
		let text: String = api::client::get(SEEDS_URL).expect("Failed to resolve seeds");
		let addrs = text
			.split_whitespace()
			.map(|s| s.parse().unwrap())
			.collect::<Vec<_>>();
		debug!(LOGGER, "Retrieved seed addresses: {:?}", addrs);
		addrs
	})
}

/// Convenience function when the seed list is immediately known. Mostly used
/// for tests.
pub fn predefined_seeds(addrs_str: Vec<String>) -> Box<Fn() -> Vec<SocketAddr> + Send> {
	Box::new(move || {
		addrs_str
			.iter()
			.map(|s| s.parse().unwrap())
			.collect::<Vec<_>>()
	})
}

/// Convenience function when the seed list is immediately known. Mostly used
/// for tests.
pub fn preferred_peers(addrs_str: Vec<String>) -> Option<Vec<SocketAddr>> {
	if addrs_str.is_empty() {
		None
	} else {
		Some(
			addrs_str
				.iter()
				.map(|s| s.parse().unwrap())
				.collect::<Vec<_>>(),
		)
	}
}
<commit_msg>Add 2 more dns seeds (#1496)<commit_after>// Copyright 2018 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Mining plugin manager, using the cuckoo-miner crate to provide
//! a mining worker implementation
//!

use chrono::prelude::Utc;
use chrono::Duration;
use std::io;
use std::net::{SocketAddr, ToSocketAddrs};
use std::str;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{mpsc, Arc};
use std::thread;
use std::time;

use api;

use p2p;
use pool::DandelionConfig;
use util::LOGGER;

const SEEDS_URL: &'static str = "http://grin-tech.org/seeds.txt";
// DNS Seeds with contact email associated
const DNS_SEEDS: &'static [&'static str] = &[
	"t3.seed.grin-tech.org",    // igno.peverell@protonmail.com
	"seed.grin.lesceller.com",  // q.lesceller@gmail.com
	"t3.grin-seed.prokapi.com", // info@prokapi.com
];

pub fn connect_and_monitor(
	p2p_server: Arc<p2p::Server>,
	capabilities: p2p::Capabilities,
	dandelion_config: DandelionConfig,
	seed_list: Box<Fn() -> Vec<SocketAddr> + Send>,
	preferred_peers: Option<Vec<SocketAddr>>,
	stop: Arc<AtomicBool>,
) {
	let _ = thread::Builder::new()
		.name("seed".to_string())
		.spawn(move || {
			let peers = p2p_server.peers.clone();

			// open a channel with a listener that connects every peer address sent below
			// max peer count
			let (tx, rx) = mpsc::channel();

			// check seeds first
			connect_to_seeds_and_preferred_peers(
				peers.clone(),
				tx.clone(),
				seed_list,
				preferred_peers.clone(),
			);

			let mut prev = Utc::now() - Duration::seconds(60);
			let mut start_attempt = 0;
			let start_wait_base: i64 = 2;
			loop {
				let current_time = Utc::now();

				// make several attempts to get peers as quick as possible with
				// exponential backoff
				if (peers.peer_count() < p2p_server.config.peer_min_preferred_count()
					&& current_time - prev > Duration::seconds(start_wait_base.pow(start_attempt)))
					|| current_time - prev > Duration::seconds(20)
				{
					// try to connect to any address sent to the channel
					listen_for_addrs(peers.clone(), p2p_server.clone(), capabilities, &rx);

					// monitor additional peers if we need to add more
					monitor_peers(
						peers.clone(),
						p2p_server.config.clone(),
						capabilities,
						tx.clone(),
						preferred_peers.clone(),
					);

					update_dandelion_relay(peers.clone(), dandelion_config.clone());

					prev = current_time;
					if start_attempt < 6 {
						start_attempt += 1;
					}
				}

				thread::sleep(time::Duration::from_secs(1));

				if stop.load(Ordering::Relaxed) {
					break;
				}
			}
		});
}

fn monitor_peers(
	peers: Arc<p2p::Peers>,
	config: p2p::P2PConfig,
	capabilities: p2p::Capabilities,
	tx: mpsc::Sender<SocketAddr>,
	preferred_peers_list: Option<Vec<SocketAddr>>,
) {
	// regularly check if we need to acquire more peers  and if so, gets
	// them from db
	let total_count = peers.all_peers().len();
	let mut healthy_count = 0;
	let mut banned_count = 0;
	let mut defunct_count = 0;
	for x in peers.all_peers() {
		match x.flags {
			p2p::State::Banned => {
				let interval = Utc::now().timestamp() - x.last_banned;
				// Unban peer
				if interval >= config.ban_window() {
					peers.unban_peer(&x.addr);
					debug!(
						LOGGER,
						"monitor_peers: unbanned {} after {} seconds", x.addr, interval
					);
				} else {
					banned_count += 1;
				}
			}
			p2p::State::Healthy => healthy_count += 1,
			p2p::State::Defunct => defunct_count += 1,
		}
	}

	debug!(
		LOGGER,
		"monitor_peers: on {}:{}, {} connected ({} most_work). \
		 all {} = {} healthy + {} banned + {} defunct",
		config.host,
		config.port,
		peers.peer_count(),
		peers.most_work_peers().len(),
		total_count,
		healthy_count,
		banned_count,
		defunct_count,
	);

	// maintenance step first, clean up p2p server peers
	peers.clean_peers(config.peer_max_count() as usize);

	// not enough peers, getting more from db
	if peers.peer_count() >= config.peer_min_preferred_count() {
		return;
	}

	// loop over connected peers
	// ask them for their list of peers
	let mut connected_peers: Vec<SocketAddr> = vec![];
	for p in peers.connected_peers() {
		if let Ok(p) = p.try_read() {
			debug!(
				LOGGER,
				"monitor_peers: {}:{} ask {} for more peers", config.host, config.port, p.info.addr,
			);
			let _ = p.send_peer_request(capabilities);
			connected_peers.push(p.info.addr)
		} else {
			warn!(LOGGER, "monitor_peers: failed to get read lock on peer");
		}
	}

	// Attempt to connect to preferred peers if there is some
	match preferred_peers_list {
		Some(preferred_peers) => {
			for mut p in preferred_peers {
				if !connected_peers.is_empty() {
					if !connected_peers.contains(&p) {
						tx.send(p).unwrap();
					}
				} else {
					tx.send(p).unwrap();
				}
			}
		}
		None => debug!(LOGGER, "monitor_peers: no preferred peers"),
	}

	// find some peers from our db
	// and queue them up for a connection attempt
	let new_peers = peers.find_peers(
		p2p::State::Healthy,
		p2p::Capabilities::UNKNOWN,
		config.peer_max_count() as usize,
	);
	for p in new_peers.iter().filter(|p| !peers.is_known(&p.addr)) {
		debug!(
			LOGGER,
			"monitor_peers: on {}:{}, queue to soon try {}", config.host, config.port, p.addr,
		);
		tx.send(p.addr).unwrap();
	}
}

fn update_dandelion_relay(peers: Arc<p2p::Peers>, dandelion_config: DandelionConfig) {
	// Dandelion Relay Updater
	let dandelion_relay = peers.get_dandelion_relay();
	if dandelion_relay.is_empty() {
		debug!(LOGGER, "monitor_peers: no dandelion relay updating");
		peers.update_dandelion_relay();
	} else {
		for last_added in dandelion_relay.keys() {
			let dandelion_interval = Utc::now().timestamp() - last_added;
			if dandelion_interval >= dandelion_config.relay_secs.unwrap() as i64 {
				debug!(LOGGER, "monitor_peers: updating expired dandelion relay");
				peers.update_dandelion_relay();
			}
		}
	}
}

// Check if we have any pre-existing peer in db. If so, start with those,
// otherwise use the seeds provided.
fn connect_to_seeds_and_preferred_peers(
	peers: Arc<p2p::Peers>,
	tx: mpsc::Sender<SocketAddr>,
	seed_list: Box<Fn() -> Vec<SocketAddr>>,
	peers_preferred_list: Option<Vec<SocketAddr>>,
) {
	// check if we have some peers in db
	let peers = peers.find_peers(p2p::State::Healthy, p2p::Capabilities::FULL_HIST, 100);

	// if so, get their addresses, otherwise use our seeds
	let mut peer_addrs = if peers.len() > 3 {
		peers.iter().map(|p| p.addr).collect::<Vec<_>>()
	} else {
		seed_list()
	};

	// If we have preferred peers add them to the connection
	match peers_preferred_list {
		Some(mut peers_preferred) => peer_addrs.append(&mut peers_preferred),
		None => debug!(LOGGER, "No preferred peers"),
	};

	if peer_addrs.len() == 0 {
		warn!(LOGGER, "No seeds were retrieved.");
	}

	// connect to this first set of addresses
	for addr in peer_addrs {
		tx.send(addr).unwrap();
	}
}

/// Regularly poll a channel receiver for new addresses and initiate a
/// connection if the max peer count isn't exceeded. A request for more
/// peers is also automatically sent after connection.
fn listen_for_addrs(
	peers: Arc<p2p::Peers>,
	p2p: Arc<p2p::Server>,
	capab: p2p::Capabilities,
	rx: &mpsc::Receiver<SocketAddr>,
) {
	let pc = peers.peer_count();
	for addr in rx.try_iter() {
		if pc < p2p.config.peer_max_count() {
			let peers_c = peers.clone();
			let p2p_c = p2p.clone();
			let _ = thread::Builder::new()
				.name("peer_connect".to_string())
				.spawn(move || {
					let mut connect_retry_count = 0;
					loop {
						let connect_peer = p2p_c.connect(&addr);
						match connect_peer {
							Ok(p) => {
								trace!(
									LOGGER,
									"connect_and_req: on {}:{}. connect to {} ok. attempting send_peer_request",
									p2p_c.config.host,
									p2p_c.config.port,
									addr
								);
								if let Ok(p) = p.try_read() {
									let _ = p.send_peer_request(capab);
								}
								let _ = peers_c.update_state(addr, p2p::State::Healthy);
								break;
							}
							Err(e) => {
								debug!(
									LOGGER,
									"connect_and_req: on {}:{}. connect to {} is Defunct. {:?}",
									p2p_c.config.host,
									p2p_c.config.port,
									addr,
									e,
								);
								let _ = peers_c.update_state(addr, p2p::State::Defunct);

								// don't retry if connection refused or PeerWithSelf
								match e {
									p2p::Error::Connection(io_err) => {
										if io::ErrorKind::ConnectionRefused == io_err.kind() {
											break;
										}
									}
									p2p::Error::PeerWithSelf => break,
									_ => continue,
								}
							}
						}

						// retry for 3 times
						thread::sleep(time::Duration::from_secs(1));
						connect_retry_count += 1;
						if connect_retry_count >= 3 {
							break;
						}
						debug!(
							LOGGER,
							"connect_and_req: on {}:{}. connect to {} retrying {}",
							p2p_c.config.host,
							p2p_c.config.port,
							addr,
							connect_retry_count,
						);
					}
				});
		}
	}
}

pub fn dns_seeds() -> Box<Fn() -> Vec<SocketAddr> + Send> {
	Box::new(|| {
		let mut addresses: Vec<SocketAddr> = vec![];
		for dns_seed in DNS_SEEDS {
			let temp_addresses = addresses.clone();
			debug!(LOGGER, "Retrieving seed nodes from dns {}", dns_seed);
			match (dns_seed.to_owned(), 0).to_socket_addrs() {
				Ok(addrs) => addresses.append(
					&mut (addrs
						.map(|mut addr| {
							addr.set_port(13414);
							addr
						})
						.filter(|addr| !temp_addresses.contains(addr))
						.collect()),
				),
				Err(e) => debug!(
					LOGGER,
					"Failed to resolve seed {:?} got error {:?}", dns_seed, e
				),
			}
		}
		debug!(LOGGER, "Retrieved seed addresses: {:?}", addresses);
		addresses
	})
}

/// Extract the list of seeds from a pre-defined text file available through
/// http. Easy method until we have a set of DNS names we can rely on.
pub fn web_seeds() -> Box<Fn() -> Vec<SocketAddr> + Send> {
	Box::new(|| {
		debug!(LOGGER, "Retrieving seed nodes from {}", SEEDS_URL);
		let text: String = api::client::get(SEEDS_URL).expect("Failed to resolve seeds");
		let addrs = text
			.split_whitespace()
			.map(|s| s.parse().unwrap())
			.collect::<Vec<_>>();
		debug!(LOGGER, "Retrieved seed addresses: {:?}", addrs);
		addrs
	})
}

/// Convenience function when the seed list is immediately known. Mostly used
/// for tests.
pub fn predefined_seeds(addrs_str: Vec<String>) -> Box<Fn() -> Vec<SocketAddr> + Send> {
	Box::new(move || {
		addrs_str
			.iter()
			.map(|s| s.parse().unwrap())
			.collect::<Vec<_>>()
	})
}

/// Convenience function when the seed list is immediately known. Mostly used
/// for tests.
pub fn preferred_peers(addrs_str: Vec<String>) -> Option<Vec<SocketAddr>> {
	if addrs_str.is_empty() {
		None
	} else {
		Some(
			addrs_str
				.iter()
				.map(|s| s.parse().unwrap())
				.collect::<Vec<_>>(),
		)
	}
}
<|endoftext|>


<commit_before>//! This crate provides Serde's two derive macros.
//!
//! ```rust
//! # #[macro_use]
//! # extern crate serde_derive;
//! #
//! #[derive(Serialize, Deserialize)]
//! # struct S;
//! #
//! # fn main() {}
//! ```
//!
//! Please refer to [https://serde.rs/derive.html] for how to set this up.
//!
//! [https://serde.rs/derive.html]: https://serde.rs/derive.html

#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.83")]
#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
// Whitelisted clippy lints
#![cfg_attr(
    feature = "cargo-clippy",
    allow(
        cyclomatic_complexity,
        enum_variant_names,
        needless_pass_by_value,
        redundant_field_names,
        too_many_arguments,
        used_underscore_binding,
    )
)]
// Whitelisted clippy_pedantic lints
#![cfg_attr(
    feature = "cargo-clippy",
    allow(
        cast_possible_truncation,
        doc_markdown,
        enum_glob_use,
        filter_map,
        indexing_slicing,
        items_after_statements,
        match_same_arms,
        similar_names,
        single_match_else,
        stutter,
        unseparated_literal_suffix,
        use_self,
    )
)]
// The `quote!` macro requires deep recursion.
#![recursion_limit = "512"]

#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;

extern crate proc_macro;
extern crate proc_macro2;

mod internals;

use proc_macro::TokenStream;
use syn::DeriveInput;

#[macro_use]
mod bound;
#[macro_use]
mod fragment;

mod de;
mod dummy;
mod pretend;
mod ser;
mod try;

#[proc_macro_derive(Serialize, attributes(serde))]
pub fn derive_serialize(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    ser::expand_derive_serialize(&input)
        .unwrap_or_else(to_compile_errors)
        .into()
}

#[proc_macro_derive(Deserialize, attributes(serde))]
pub fn derive_deserialize(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    de::expand_derive_deserialize(&input)
        .unwrap_or_else(to_compile_errors)
        .into()
}

fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
    let compile_errors = errors.iter().map(syn::Error::to_compile_error);
    quote!(#(#compile_errors)*)
}
<commit_msg>Ignore trivially_copy_pass_by_ref lint in serde_derive<commit_after>//! This crate provides Serde's two derive macros.
//!
//! ```rust
//! # #[macro_use]
//! # extern crate serde_derive;
//! #
//! #[derive(Serialize, Deserialize)]
//! # struct S;
//! #
//! # fn main() {}
//! ```
//!
//! Please refer to [https://serde.rs/derive.html] for how to set this up.
//!
//! [https://serde.rs/derive.html]: https://serde.rs/derive.html

#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.83")]
#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
// Whitelisted clippy lints
#![cfg_attr(
    feature = "cargo-clippy",
    allow(
        cyclomatic_complexity,
        enum_variant_names,
        needless_pass_by_value,
        redundant_field_names,
        too_many_arguments,
        trivially_copy_pass_by_ref,
        used_underscore_binding,
    )
)]
// Whitelisted clippy_pedantic lints
#![cfg_attr(
    feature = "cargo-clippy",
    allow(
        cast_possible_truncation,
        doc_markdown,
        enum_glob_use,
        filter_map,
        indexing_slicing,
        items_after_statements,
        match_same_arms,
        similar_names,
        single_match_else,
        stutter,
        unseparated_literal_suffix,
        use_self,
    )
)]
// The `quote!` macro requires deep recursion.
#![recursion_limit = "512"]

#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;

extern crate proc_macro;
extern crate proc_macro2;

mod internals;

use proc_macro::TokenStream;
use syn::DeriveInput;

#[macro_use]
mod bound;
#[macro_use]
mod fragment;

mod de;
mod dummy;
mod pretend;
mod ser;
mod try;

#[proc_macro_derive(Serialize, attributes(serde))]
pub fn derive_serialize(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    ser::expand_derive_serialize(&input)
        .unwrap_or_else(to_compile_errors)
        .into()
}

#[proc_macro_derive(Deserialize, attributes(serde))]
pub fn derive_deserialize(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    de::expand_derive_deserialize(&input)
        .unwrap_or_else(to_compile_errors)
        .into()
}

fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
    let compile_errors = errors.iter().map(syn::Error::to_compile_error);
    quote!(#(#compile_errors)*)
}
<|endoftext|>


<commit_before>use runtime::{Class, Sel};

pub struct Encoding<T>(&'static str);

pub trait Encode {
	fn code() -> Encoding<Self>;
}

impl Encode for i8 {
	fn code() -> Encoding<i8> { Encoding("c") }
}

impl Encode for i16 {
	fn code() -> Encoding<i16> { Encoding("s") }
}

impl Encode for i32 {
	fn code() -> Encoding<i32> { Encoding("i") }
}

impl Encode for i64 {
	fn code() -> Encoding<i64> { Encoding("q") }
}

impl Encode for u8 {
	fn code() -> Encoding<u8> { Encoding("C") }
}

impl Encode for u16 {
	fn code() -> Encoding<u16> { Encoding("S") }
}

impl Encode for u32 {
	fn code() -> Encoding<u32> { Encoding("I") }
}

impl Encode for u64 {
	fn code() -> Encoding<u64> { Encoding("Q") }
}

impl Encode for f32 {
	fn code() -> Encoding<f32> { Encoding("f") }
}

impl Encode for f64 {
	fn code() -> Encoding<f64> { Encoding("d") }
}

impl Encode for Class {
	fn code() -> Encoding<Class> { Encoding("#") }
}

impl Encode for Sel {
	fn code() -> Encoding<Sel> { Encoding(":") }
}

fn encode<T: Encode>() -> &'static str {
	let Encoding(code): Encoding<T> = Encode::code();
	code
}

#[cfg(test)]
mod tests {
	use runtime::{Class, Sel};
	use super::encode;

	#[test]
	fn test_encode() {
		assert!(encode::<u32>() == "I");
		assert!(encode::<Class>() == "#");
		assert!(encode::<Sel>() == ":");
	}
}
<commit_msg>Implemented more Encodings.<commit_after>use std::mem;

use runtime::{Class, Sel};

pub struct Encoding<T>(&'static str);

pub trait Encode {
	fn code() -> Encoding<Self>;
}

impl Encode for i8 {
	fn code() -> Encoding<i8> { Encoding("c") }
}

impl Encode for i16 {
	fn code() -> Encoding<i16> { Encoding("s") }
}

impl Encode for i32 {
	fn code() -> Encoding<i32> { Encoding("i") }
}

impl Encode for i64 {
	fn code() -> Encoding<i64> { Encoding("q") }
}

impl Encode for int {
	fn code() -> Encoding<int> {
		match mem::size_of::<int>() {
			1 => Encoding("c"),
			2 => Encoding("s"),
			4 => Encoding("i"),
			8 => Encoding("q"),
			_ => Encoding("?"),
		}
	}
}

impl Encode for u8 {
	fn code() -> Encoding<u8> { Encoding("C") }
}

impl Encode for u16 {
	fn code() -> Encoding<u16> { Encoding("S") }
}

impl Encode for u32 {
	fn code() -> Encoding<u32> { Encoding("I") }
}

impl Encode for u64 {
	fn code() -> Encoding<u64> { Encoding("Q") }
}

impl Encode for uint {
	fn code() -> Encoding<uint> {
		match mem::size_of::<uint>() {
			1 => Encoding("C"),
			2 => Encoding("S"),
			4 => Encoding("I"),
			8 => Encoding("Q"),
			_ => Encoding("?"),
		}
	}
}

impl Encode for f32 {
	fn code() -> Encoding<f32> { Encoding("f") }
}

impl Encode for f64 {
	fn code() -> Encoding<f64> { Encoding("d") }
}

impl Encode for bool {
	fn code() -> Encoding<bool> { Encoding("B") }
}

impl Encode for () {
	fn code() -> Encoding<()> { Encoding("v") }
}

impl Encode for Class {
	fn code() -> Encoding<Class> { Encoding("#") }
}

impl Encode for Sel {
	fn code() -> Encoding<Sel> { Encoding(":") }
}

pub fn encode<T: Encode>() -> &'static str {
	let Encoding(code): Encoding<T> = Encode::code();
	code
}

#[cfg(test)]
mod tests {
	use runtime::{Class, Sel};
	use super::encode;

	#[test]
	fn test_encode() {
		assert!(encode::<u32>() == "I");
		assert!(encode::<()>() == "v");
		assert!(encode::<Class>() == "#");
		assert!(encode::<Sel>() == ":");
	}
}
<|endoftext|>


<commit_before>//============================================================================
//
// A simple Mandelbrot image generator in Rust
//
// Generator Engine backend
//
// Copyright (c) 2014 Gavin Baker <gavinb@antonym.org>
// Published under the MIT license
//
//============================================================================

extern crate native;
extern crate num;

use std::comm::{Sender, Receiver};
use std::vec::Vec;

use protocol::{RenderType, PreviewRender, FullRender};
use protocol::{EngineStatus, Startup, Processing, RenderComplete};
use protocol::{EngineCommand, UpdateRegion, ZoomIn, ZoomOut, PanLeft, PanRight, PanUp,PanDown, Render, Shutdown};

mod protocol;

static PREVIEW_WIDTH: i32 = 256;
static PREVIEW_HEIGHT: i32 = 256;

//----------------------------------------------------------------------------

type RGB8 = (u8, u8, u8);

pub struct MandelEngine {
    buffer_width: uint,
    buffer_height: uint,
    palette: Vec<RGB8>,
    re0: f32,
    re1: f32,
    im0: f32,
    im1: f32,
}

impl MandelEngine {

    pub fn new(w: uint, h: uint) -> MandelEngine {

        // Init palette using hue sweep in HSV colour space
        let mut p: Vec<RGB8> = Vec::with_capacity(720);
        let s = 1.0f32; // saturation
        let v = 1.0f32; // value
        let c = v * s; // chroma
        for h in range(0, 720) { // hue
            let hp = h as f32/60.0;
            let x = c * (1.0-(hp % 2.0 - 1.0).abs());
            let (r,g,b) = if 0.0 <= hp && hp < 1.0 {
                (c, x, 0.0)
            } else if 1.0 <= hp && hp < 2.0 {
                (x, c, 0.0)
            } else if 2.0 <= hp && hp < 3.0 {
                (0.0, c, x)
            } else if 3.0 <= hp && hp < 4.0 {
                (0.0, x, c)
            } else if 4.0 <= hp && hp < 5.0 {
                (x, 0.0, c)
            } else if 5.0 <= hp && hp < 6.0 {
                (c, 0.0, x)
            } else {
                (0.0, 0.0, 0.0)
            };
            let m = v-c;
            p.push((((r+m)*255.0) as u8, ((g+m)*255.0) as u8, ((b+m)*255.0) as u8));
        }

        MandelEngine {
            re0: -1.4f32,
            re1:  0.6f32,
            im0: -1.0f32,
            im1:  1.0f32,
            buffer_width: w,
            buffer_height: h,
            palette: p
        }
    }

    // Rescale pixel coord (x,y) into cspace
    fn scale_coords(&self, x: uint, y: uint, w: uint, h: uint) -> (f32, f32) {
        let x0 = self.re0;
        let x1 = self.re1;
        let y0 = self.im0;
        let y1 = self.im1;

        let xx = (x as f32) / (w as f32) * (x1-x0) + x0;
        let yy = (y as f32) / (h as f32) * (y1-y0) + y0;

        (xx, yy)
    }

    pub fn serve(&mut self, cmd_chan: &Receiver<EngineCommand>, progress_chan: &Sender<EngineStatus>) {
        let mut running = true;
        while running {
            // pan/zoom by 10% of width
            let delta_r = ((self.re1 - self.re0)*0.1f32).abs();
            let delta_i = ((self.im1 - self.im0)*0.1f32).abs();
            println!("delta r,i {},{}", delta_r, delta_i);

            let cmd = cmd_chan.recv();
            println!("engine: command {}", cmd);
            match cmd {
                UpdateRegion(re0, re1, im0, im1) => {
                    self.re0 = re0; self.re1 = re1; self.im0 = im0; self.im1 = im1;
                },
                ZoomIn => {
                    self.re0 += delta_r;
                    self.re1 -= delta_r;
                    self.im0 += delta_i;
                    self.im1 -= delta_i;
                },
                ZoomOut => {
                    self.re0 -= delta_r;
                    self.re1 += delta_r;
                    self.im0 -= delta_i;
                    self.im1 += delta_i;
                },
                PanLeft => {
                    self.re0 -= delta_r;
                    self.re1 -= delta_r;
                },
                PanRight => {
                    self.re0 += delta_r;
                    self.re1 += delta_r;
                },
                PanUp => {
                    self.im0 += delta_i;
                    self.im1 += delta_i;
                },
                PanDown => {
                    self.im0 -= delta_i;
                    self.im1 -= delta_i;
                },
                Render(typ) => self.process(typ, progress_chan),
                Shutdown => running = false,
            }
        }

        println!("engine: shutdown");
    }

    // Evalute entire region
    fn process(&mut self, typ: RenderType, progress_chan: &Sender<EngineStatus>) {

        let (width, height) = match typ {
            PreviewRender => (PREVIEW_WIDTH as uint, PREVIEW_HEIGHT as uint),
            FullRender => (self.buffer_height, self.buffer_width),
        };

        let mut img: Vec<u8> = Vec::with_capacity(width*height*3);

        let max_iteration = 500;

        println!("+++ process {}x{} RGB8 in {} bytes", width, height, img.capacity());
        println!("            re: {}..{} im: {}..{}", self.re0, self.re1, self.im0, self.im1);

        progress_chan.send(Startup);

        // Process each pixel
        for py in range(0, height) {
            for px in range(0, width) {

                // Project pixels into Mandelbrot domain
                let (x0, y0) = self.scale_coords(px, py, width, height);

                let mut x = 0.0f32;
                let mut y = 0.0f32;
                let mut iteration = 0;

                // Iterate!
                while (x*x + y*y < 4.0) && (iteration < max_iteration) {
                    let x1 = x*x - y*y + x0;
                    y = 2.0*x*y + y0;
                    x = x1;

                    iteration += 1;
                }

                // Colour and plot
                let color = self.palette.get(iteration % 580);
                let (r,g,b) = *color;

                img.push(r);
                img.push(g);
                img.push(b);
            }
            if py % 100 == 0 {
                progress_chan.send(Processing(py));
            }
        }

        progress_chan.send(RenderComplete(typ, img));
    }
}

//----------------------------------------------------------------------------
<commit_msg>Fix engine swap of width/height<commit_after>//============================================================================
//
// A simple Mandelbrot image generator in Rust
//
// Generator Engine backend
//
// Copyright (c) 2014 Gavin Baker <gavinb@antonym.org>
// Published under the MIT license
//
//============================================================================

extern crate native;
extern crate num;

use std::comm::{Sender, Receiver};
use std::vec::Vec;

use protocol::{RenderType, PreviewRender, FullRender};
use protocol::{EngineStatus, Startup, Processing, RenderComplete};
use protocol::{EngineCommand, UpdateRegion, ZoomIn, ZoomOut, PanLeft, PanRight, PanUp,PanDown, Render, Shutdown};

mod protocol;

static PREVIEW_WIDTH: i32 = 256;
static PREVIEW_HEIGHT: i32 = 256;

//----------------------------------------------------------------------------

type RGB8 = (u8, u8, u8);

pub struct MandelEngine {
    buffer_width: uint,
    buffer_height: uint,
    palette: Vec<RGB8>,
    re0: f32,
    re1: f32,
    im0: f32,
    im1: f32,
}

impl MandelEngine {

    pub fn new(w: uint, h: uint) -> MandelEngine {

        // Init palette using hue sweep in HSV colour space
        let mut p: Vec<RGB8> = Vec::with_capacity(720);
        let s = 1.0f32; // saturation
        let v = 1.0f32; // value
        let c = v * s; // chroma
        for h in range(0, 720) { // hue
            let hp = h as f32/60.0;
            let x = c * (1.0-(hp % 2.0 - 1.0).abs());
            let (r,g,b) = if 0.0 <= hp && hp < 1.0 {
                (c, x, 0.0)
            } else if 1.0 <= hp && hp < 2.0 {
                (x, c, 0.0)
            } else if 2.0 <= hp && hp < 3.0 {
                (0.0, c, x)
            } else if 3.0 <= hp && hp < 4.0 {
                (0.0, x, c)
            } else if 4.0 <= hp && hp < 5.0 {
                (x, 0.0, c)
            } else if 5.0 <= hp && hp < 6.0 {
                (c, 0.0, x)
            } else {
                (0.0, 0.0, 0.0)
            };
            let m = v-c;
            p.push((((r+m)*255.0) as u8, ((g+m)*255.0) as u8, ((b+m)*255.0) as u8));
        }

        MandelEngine {
            re0: -1.4f32,
            re1:  0.6f32,
            im0: -1.0f32,
            im1:  1.0f32,
            buffer_width: w,
            buffer_height: h,
            palette: p
        }
    }

    // Rescale pixel coord (x,y) into cspace
    fn scale_coords(&self, x: uint, y: uint, w: uint, h: uint) -> (f32, f32) {
        let x0 = self.re0;
        let x1 = self.re1;
        let y0 = self.im0;
        let y1 = self.im1;

        let xx = (x as f32) / (w as f32) * (x1-x0) + x0;
        let yy = (y as f32) / (h as f32) * (y1-y0) + y0;

        (xx, yy)
    }

    pub fn serve(&mut self, cmd_chan: &Receiver<EngineCommand>, progress_chan: &Sender<EngineStatus>) {
        let mut running = true;
        while running {
            // pan/zoom by 10% of width
            let delta_r = ((self.re1 - self.re0)*0.1f32).abs();
            let delta_i = ((self.im1 - self.im0)*0.1f32).abs();
            println!("delta r,i {},{}", delta_r, delta_i);

            let cmd = cmd_chan.recv();
            println!("engine: command {}", cmd);
            match cmd {
                UpdateRegion(re0, re1, im0, im1) => {
                    self.re0 = re0; self.re1 = re1; self.im0 = im0; self.im1 = im1;
                },
                ZoomIn => {
                    self.re0 += delta_r;
                    self.re1 -= delta_r;
                    self.im0 += delta_i;
                    self.im1 -= delta_i;
                },
                ZoomOut => {
                    self.re0 -= delta_r;
                    self.re1 += delta_r;
                    self.im0 -= delta_i;
                    self.im1 += delta_i;
                },
                PanLeft => {
                    self.re0 -= delta_r;
                    self.re1 -= delta_r;
                },
                PanRight => {
                    self.re0 += delta_r;
                    self.re1 += delta_r;
                },
                PanUp => {
                    self.im0 += delta_i;
                    self.im1 += delta_i;
                },
                PanDown => {
                    self.im0 -= delta_i;
                    self.im1 -= delta_i;
                },
                Render(typ) => self.process(typ, progress_chan),
                Shutdown => running = false,
            }
        }

        println!("engine: shutdown");
    }

    // Evalute entire region
    fn process(&mut self, typ: RenderType, progress_chan: &Sender<EngineStatus>) {

        let (width, height) = match typ {
            PreviewRender => (PREVIEW_WIDTH as uint, PREVIEW_HEIGHT as uint),
            FullRender => (self.buffer_width, self.buffer_height),
        };

        let mut img: Vec<u8> = Vec::with_capacity(width*height*3);

        let max_iteration = 500;

        println!("+++ process {}x{} RGB8 in {} bytes", width, height, img.capacity());
        println!("            re: {}..{} im: {}..{}", self.re0, self.re1, self.im0, self.im1);

        progress_chan.send(Startup);

        // Process each pixel
        for py in range(0, height) {
            for px in range(0, width) {

                // Project pixels into Mandelbrot domain
                let (x0, y0) = self.scale_coords(px, py, width, height);

                let mut x = 0.0f32;
                let mut y = 0.0f32;
                let mut iteration = 0;

                // Iterate!
                while (x*x + y*y < 4.0) && (iteration < max_iteration) {
                    let x1 = x*x - y*y + x0;
                    y = 2.0*x*y + y0;
                    x = x1;

                    iteration += 1;
                }

                // Colour and plot
                let color = self.palette.get(iteration % 580);
                let (r,g,b) = *color;

                img.push(r);
                img.push(g);
                img.push(b);
            }
            if py % 100 == 0 {
                progress_chan.send(Processing(py));
            }
        }

        progress_chan.send(RenderComplete(typ, img));
    }
}

//----------------------------------------------------------------------------
<|endoftext|>


<commit_before>// Copyright (c) 2015, The Radare Project. All rights reserved.
// See the COPYING file at the top-level directory of this distribution.
// Licensed under the BSD 3-Clause License:
// <http://opensource.org/licenses/BSD-3-Clause>
// This file may not be copied, modified, or distributed
// except according to those terms.

//! # The Radare2 Decompilation Library
//! radeco-lib is the crate that powers the
//! [radare2 decompiler](https://github.com/radare/radeco).
//!
//! Apart from decompilation, this library is designed to allow users to
//! perform static analysis on binaries in a easy and intuitive way.
//! __Reusablility__ and __Interactivity__ are the most important
//! design principles of this library.
//!
//! # Design
//! radeco-lib is built on top of r2pipe.rs, a simple library that provides
//! methods
//! to communicate with radare2 (using pipes). To know more about r2pipe or
//! radare2 in general, please head over to the
//! [repo](https://github.com/radare/radare2).
//!
//! radeco-lib works on analyzing ESIL (Evaluable Strings Intermediate
//! Language), an intermediate
//! representation (IR) used by radare2 for emulation. ESIL is converted into
//! an internal SSA IR
//! and used for subsequent analysis and optimizations.
//!
//! __NOTE__: This library is still under heavy developement.
//! Some API's have been stabilized, please check the docs before using
//! radeco-lib
//! in your projects as changes may not be backwards compatible. Contributions,
//! suggestions
//! and bug reports are always welcome at:
//! [tracker](https://github.com/radare/radeco-lib/issues)
//!

#![doc(html_root_url = "https://radare.github.io/radeco-lib/")]
#![doc(html_logo_url = "http://rada.re/r/img/r2logo3.png")]
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(slice_patterns)]
#![feature(try_trait)]
#![feature(custom_attribute)]
//#[cfg(test)] #[macro_use] extern crate quickcheck_macros;

extern crate petgraph;
extern crate regex;
extern crate serde_json;

#[macro_use]
extern crate lazy_static;
extern crate bit_set;
extern crate either;
extern crate fixedbitset;
extern crate linear_map;
extern crate num;
extern crate typed_arena;
extern crate vec_map;

#[cfg(test)]
extern crate quickcheck;

#[cfg(feature = "trace_log")]
#[macro_use]
extern crate log;
#[cfg(feature = "trace_log")]
extern crate env_logger;

extern crate r2api;
extern crate r2pipe;

extern crate esil;
// extern crate capstone_rust;
extern crate rayon;

#[cfg(feature = "profile")]
extern crate cpuprofiler;

extern crate lalrpop_util;

#[macro_use]
pub mod utils;
#[macro_use]
pub mod middle;
#[macro_use]
pub mod analysis;

pub mod backend;
pub mod frontend;
<commit_msg>Remove custom_attribute feature (#332)<commit_after>// Copyright (c) 2015, The Radare Project. All rights reserved.
// See the COPYING file at the top-level directory of this distribution.
// Licensed under the BSD 3-Clause License:
// <http://opensource.org/licenses/BSD-3-Clause>
// This file may not be copied, modified, or distributed
// except according to those terms.

//! # The Radare2 Decompilation Library
//! radeco-lib is the crate that powers the
//! [radare2 decompiler](https://github.com/radare/radeco).
//!
//! Apart from decompilation, this library is designed to allow users to
//! perform static analysis on binaries in a easy and intuitive way.
//! __Reusablility__ and __Interactivity__ are the most important
//! design principles of this library.
//!
//! # Design
//! radeco-lib is built on top of r2pipe.rs, a simple library that provides
//! methods
//! to communicate with radare2 (using pipes). To know more about r2pipe or
//! radare2 in general, please head over to the
//! [repo](https://github.com/radare/radare2).
//!
//! radeco-lib works on analyzing ESIL (Evaluable Strings Intermediate
//! Language), an intermediate
//! representation (IR) used by radare2 for emulation. ESIL is converted into
//! an internal SSA IR
//! and used for subsequent analysis and optimizations.
//!
//! __NOTE__: This library is still under heavy developement.
//! Some API's have been stabilized, please check the docs before using
//! radeco-lib
//! in your projects as changes may not be backwards compatible. Contributions,
//! suggestions
//! and bug reports are always welcome at:
//! [tracker](https://github.com/radare/radeco-lib/issues)
//!

#![doc(html_root_url = "https://radare.github.io/radeco-lib/")]
#![doc(html_logo_url = "http://rada.re/r/img/r2logo3.png")]
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(slice_patterns)]
#![feature(try_trait)]
//#[cfg(test)] #[macro_use] extern crate quickcheck_macros;

extern crate petgraph;
extern crate regex;
extern crate serde_json;

#[macro_use]
extern crate lazy_static;
extern crate bit_set;
extern crate either;
extern crate fixedbitset;
extern crate linear_map;
extern crate num;
extern crate typed_arena;
extern crate vec_map;

#[cfg(test)]
extern crate quickcheck;

#[cfg(feature = "trace_log")]
#[macro_use]
extern crate log;
#[cfg(feature = "trace_log")]
extern crate env_logger;

extern crate r2api;
extern crate r2pipe;

extern crate esil;
// extern crate capstone_rust;
extern crate rayon;

#[cfg(feature = "profile")]
extern crate cpuprofiler;

extern crate lalrpop_util;

#[macro_use]
pub mod utils;
#[macro_use]
pub mod middle;
#[macro_use]
pub mod analysis;

pub mod backend;
pub mod frontend;
<|endoftext|>


<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

use std::thread;
use std::thread::JoinHandle;
use std::net::TcpStream;
use std::ops::DerefMut;
use std::os::unix::io::RawFd;
use std::collections::LinkedList;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{
    channel,
    Sender,
    Receiver,
    TryRecvError
};

use super::types::*;
use super::epoll;
use super::epoll::util::*;
use super::epoll::EpollEvent;
use super::socket::Socket;
use super::simple_stream::nbetstream::NbetStream;

#[allow(dead_code)]
pub struct EventLoop {
    /// Handle to event loop process
    prox: JoinHandle<()>,
    /// Sender<T> to proc
    prox_tx: Sender<TcpStream>
}

impl EventLoop {

    /// Returns a new EventLoop
    pub fn new(server_tx: Sender<EventTuple>) -> EventLoop {
        let (tx, rx): (Sender<TcpStream>, Receiver<TcpStream>) = channel();
        let prox = thread::Builder::new()
            .name("EventLoop".to_string())
            .spawn(move || {
                EventLoop::start(rx, server_tx);
            }).unwrap();

        EventLoop {
            prox: prox,
            prox_tx: tx
        }
    }

    /// Returns a clone of this EventLoop's Sender<TcpStream> channel
    pub fn sender(&self) -> Sender<TcpStream> {
        self.prox_tx.clone()
    }

    /// Main event loop
    fn start(rx: Receiver<TcpStream>, uspace_tx: Sender<EventTuple>) {
        // Master socket list
        let sockets = Arc::new(Mutex::new(LinkedList::<Socket>::new()));

        // Our error handler for this thread and it's children
        let (err_tx, err_rx): (Sender<()>, Receiver<()>) = channel();

        // Create an epoll instance
        let result = epoll::create1(0);
        if result.is_err() {
            let err = result.unwrap_err();
            panic!("Unable to create epoll instance: {}", err)
        }
        let epoll_instance = result.unwrap();

        // Start the epoll thread
        let c_sockets = sockets.clone();
        let c_epoll_instance = epoll_instance.clone();
        let epoll_prox = thread::Builder::new()
            .name("EpollLoop".to_string())
            .spawn(move || {
                EventLoop::epoll_loop(uspace_tx, c_sockets, c_epoll_instance, err_rx);
            }).unwrap();

        for new_stream in rx.iter() {
            match NbetStream::new(new_stream) {
                Ok(s_stream) => {
                    // Add to master list
                    let socket = Socket::new(s_stream);

                    // TODO - Replace with recoverable version once into_inner() is stable
                    // Unfortunately, this is the only stable way to use mutexes at the moment
                    // Hopefully recovering from poisoning will be in 1.2
                    // let mut s_guard = match sockets.lock() {
                    //     Ok(guard) => guard,
                    //     Err(poisoned) => poisoned.into_inner()
                    // };
                    let mut s_guard = sockets.lock().unwrap();
                    let s_list = s_guard.deref_mut();
                    let s_fd = socket.raw_fd();
                    s_list.push_back(socket);

                    // Add to epoll
                    let event = Box::new(EpollEvent {
                        data: s_fd as u64,
                        events: (event_type::EPOLLIN | event_type::EPOLLET)
                        });
                    match epoll::ctl(epoll_instance, ctl_op::ADD, s_fd, event) {
                        Ok(()) => println!("New socket added to epoll list"),
                        Err(e) => println!("Epoll CtrlError: {}", e)
                    };
                }
                Err(e) => {
                    // TODO - write error to log file
                    println!("Error: {}", e);
                }
            };
        }

        // If we get here, shit is real bad. It means we have lost our
        // channel to the outside world.
        // Kill off event loop, so unwinding can begin
        println!("Rust.EventLoop.start - thread finished");
        let _ = err_tx.send(());
    }

    /// Calls epoll_wait forever waiting for events to be fired up from kernel
    fn epoll_loop(uspace_tx: Sender<EventTuple>,
                  sockets: SocketList,
                  epoll_instance: RawFd,
                  err_rx: Receiver<()>) {

        // This is the maximum number of events we want to be notified of at one time
        let mut events = Vec::<EpollEvent>::with_capacity(100);
        unsafe { events.set_len(100); }

        loop {
            // Wait for epoll events
            match epoll::wait(epoll_instance, &mut events[..], -1) {
                Ok(num_events) => {
                    println!("{} epoll event(s) received", num_events);
                    for x in 0..num_events {
                        let tx_clone = uspace_tx.clone();
                        let s_clone = sockets.clone();
                        EventLoop::handle_epoll_event(tx_clone, &events[x as usize], s_clone);
                    }
                }
                Err(e) => {
                    panic!("Error on epoll::wait(): {}", e)
                }
            }

            // If we receive anything on this end or lose communication, we need to halt,
            // because we have no where to send up events, our "user space" channel is
            // gone
            match err_rx.try_recv() {
                Ok(_) => {
                    println!("Error, terminating epoll_loop");
                    break;
                }
                Err(e) => {
                    match e {
                        TryRecvError::Empty => {}
                        _ => {
                            println!("Error, terminating epoll_loo");
                            break;
                        }
                    }
                }
            }
        }
        println!("Rust.EventLoop.epoll_loop - thread finished");
    }

    /// Processes a read on the socket that is ready for reading
    ///
    /// An epoll event is passed to this function, with the fd that is ready to be read
    /// A traversal through the list of sockets is then performed in order to locate the
    /// NbetStream instance that is wrapping that file descriptor.
    /// After a read is performed on that socket, it's data will be passed back to the
    /// "user-space" section of the Server to process the payload however it needs to.
    ///
    /// TODO - Create a faster way to gain a safe reference to the NbetStream wrapping
    /// the fd instead of an O(n) lookup for every event. Currently lookup time is
    /// O(n) * MAX_EPOLL_EVENTS, where MAX_EPOLL_EVENTS is user defined in this function's
    /// caller.
    fn handle_epoll_event(uspace_tx: Sender<EventTuple>,
                          event: &EpollEvent,
                          sockets: SocketList) {

        println!("Rust.EventLoop.handle_epoll_event");

        // The fd we are traversing the list for
        let fd = event.data;

        // We need a handle before the list gets locked and used
        let s_list_clone = sockets.clone();

        // TODO - Replace with recoverable version once into_inner() is stable
        // Unfortunately, this is the only stable way to use mutexes at the moment
        // Hopefully recovering from poisoning will be in 1.2
        // let mut s_guard = match sockets.lock() {
        //     Ok(guard) => guard,
        //     Err(poisoned) => poisoned.into_inner()
        // };
        let mut s_guard = sockets.lock().unwrap();
        let s_list = s_guard.deref_mut();
        for socket in s_list.iter_mut() {
            if socket.raw_fd() as u64 == fd {
                // We've found the socket for the event passed from epoll
                // Lets attempt to read from it
                match socket.read() {
                    Ok(()) => {
                        println!("Rust.EventLoop.handle_epoll_event.socket.read.Ok()");
                        for msg in socket.buffer().iter() {
                            let msg_for_debug = msg.clone();
                            match String::from_utf8(msg_for_debug) {
                                Ok(msg_str) => {
                                    println!("Rust.EventLoop.handle_epoll_event.socket.read.ok.msg_as_string: {}", msg_str);
                                }
                                Err(e) => {
                                    println!("Rust.EventLoop.handle_epoll_event.socket.read.ok.msg_as_string.Err: {}", e);
                                }
                            };

                            let list_handle = s_list_clone.clone();
                            let socket_clone = socket.clone();
                            let msg_clone = msg.clone();
                            let _ = uspace_tx.send((list_handle, socket_clone, msg_clone));
                        }
                    }
                    Err(e) => {
                        println!("Rust.EventLoop.handle_epoll_event.socket.read.Err(e): {}", e);
                    }
                }
            }
        }
    }
}
<commit_msg>Debugging socket removal on disconnect<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

use std::thread;
use std::thread::JoinHandle;
use std::net::TcpStream;
use std::ops::DerefMut;
use std::os::unix::io::RawFd;
use std::collections::LinkedList;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{
    channel,
    Sender,
    Receiver,
    TryRecvError
};

use super::types::*;
use super::epoll;
use super::epoll::util::*;
use super::epoll::EpollEvent;
use super::socket::Socket;
use super::simple_stream::nbetstream::NbetStream;

#[allow(dead_code)]
pub struct EventLoop {
    /// Handle to event loop process
    prox: JoinHandle<()>,
    /// Sender<T> to proc
    prox_tx: Sender<TcpStream>
}

impl EventLoop {

    /// Returns a new EventLoop
    pub fn new(server_tx: Sender<EventTuple>) -> EventLoop {
        let (tx, rx): (Sender<TcpStream>, Receiver<TcpStream>) = channel();
        let prox = thread::Builder::new()
            .name("EventLoop".to_string())
            .spawn(move || {
                EventLoop::start(rx, server_tx);
            }).unwrap();

        EventLoop {
            prox: prox,
            prox_tx: tx
        }
    }

    /// Returns a clone of this EventLoop's Sender<TcpStream> channel
    pub fn sender(&self) -> Sender<TcpStream> {
        self.prox_tx.clone()
    }

    /// Main event loop
    fn start(rx: Receiver<TcpStream>, uspace_tx: Sender<EventTuple>) {
        // Master socket list
        let sockets = Arc::new(Mutex::new(LinkedList::<Socket>::new()));

        // Our error handler for this thread and it's children
        let (err_tx, err_rx): (Sender<()>, Receiver<()>) = channel();

        // Create an epoll instance
        let result = epoll::create1(0);
        if result.is_err() {
            let err = result.unwrap_err();
            panic!("Unable to create epoll instance: {}", err)
        }
        let epoll_instance = result.unwrap();

        // Start the epoll thread
        let c_sockets = sockets.clone();
        let c_epoll_instance = epoll_instance.clone();
        let epoll_prox = thread::Builder::new()
            .name("EpollLoop".to_string())
            .spawn(move || {
                EventLoop::epoll_loop(uspace_tx, c_sockets, c_epoll_instance, err_rx);
            }).unwrap();

        for new_stream in rx.iter() {
            match NbetStream::new(new_stream) {
                Ok(s_stream) => {
                    // Add to master list
                    let socket = Socket::new(s_stream);

                    // TODO - Replace with recoverable version once into_inner() is stable
                    // Unfortunately, this is the only stable way to use mutexes at the moment
                    // Hopefully recovering from poisoning will be in 1.2
                    // let mut s_guard = match sockets.lock() {
                    //     Ok(guard) => guard,
                    //     Err(poisoned) => poisoned.into_inner()
                    // };
                    let mut s_guard = sockets.lock().unwrap();
                    let s_list = s_guard.deref_mut();
                    let s_fd = socket.raw_fd();
                    s_list.push_back(socket);

                    // Add to epoll
                    let event = Box::new(EpollEvent {
                        data: s_fd as u64,
                        events: (event_type::EPOLLIN | event_type::EPOLLET)
                    });
                    match epoll::ctl(epoll_instance, ctl_op::ADD, s_fd, event) {
                        Ok(()) => println!("New socket added to epoll list"),
                        Err(e) => println!("Epoll CtrlError during add: {}", e)
                    };
                }
                Err(e) => {
                    // TODO - write error to log file
                    println!("Error: {}", e);
                }
            };
        }

        // If we get here, shit is real bad. It means we have lost our
        // channel to the outside world.
        // Kill off event loop, so unwinding can begin
        println!("Rust.EventLoop.start - thread finished");
        let _ = err_tx.send(());
    }

    /// Calls epoll_wait forever waiting for events to be fired up from kernel
    fn epoll_loop(uspace_tx: Sender<EventTuple>,
                  sockets: SocketList,
                  epoll_instance: RawFd,
                  err_rx: Receiver<()>) {

        // This is the maximum number of events we want to be notified of at one time
        let mut events = Vec::<EpollEvent>::with_capacity(100);
        unsafe { events.set_len(100); }

        loop {
            // Wait for epoll events
            match epoll::wait(epoll_instance, &mut events[..], -1) {
                Ok(num_events) => {
                    println!("{} epoll event(s) received", num_events);
                    for x in 0..num_events {
                        let tx_clone = uspace_tx.clone();
                        let s_clone = sockets.clone();
                        let epfd_clone = epoll_instance.clone();

                        EventLoop::handle_epoll_event(epfd_clone,
                            &events[x as usize], s_clone, tx_clone);
                    }
                }
                Err(e) => {
                    panic!("Error on epoll::wait(): {}", e)
                }
            }

            // If we receive anything on this end or lose communication, we need to halt,
            // because we have no where to send up events, our "user space" channel is
            // gone
            match err_rx.try_recv() {
                Ok(_) => {
                    println!("Error, terminating epoll_loop");
                    break;
                }
                Err(e) => {
                    match e {
                        TryRecvError::Empty => {}
                        _ => {
                            println!("Error, terminating epoll_loo");
                            break;
                        }
                    }
                }
            }
        }
        println!("Rust.EventLoop.epoll_loop - thread finished");
    }

    /// Processes a read on the socket that is ready for reading
    ///
    /// An epoll event is passed to this function, with the fd that is ready to be read
    /// A traversal through the list of sockets is then performed in order to locate the
    /// NbetStream instance that is wrapping that file descriptor.
    /// After a read is performed on that socket, it's data will be passed back to the
    /// "user-space" section of the Server to process the payload however it needs to.
    ///
    /// TODO - Create a faster way to gain a safe reference to the NbetStream wrapping
    /// the fd instead of an O(n) lookup for every event. Currently lookup time is
    /// O(n) * MAX_EPOLL_EVENTS, where MAX_EPOLL_EVENTS is user defined in this function's
    /// caller.
    fn handle_epoll_event(epoll_instance: RawFd,
                          event: &EpollEvent,
                          sockets: SocketList,
                          uspace_tx: Sender<EventTuple>) {

        println!("Rust.EventLoop.handle_epoll_event");

        // The fd we are traversing the list for
        let fd = event.data;

        // We need a handle before the list gets locked and used
        let s_list_clone = sockets.clone();

        // TODO - Replace with recoverable version once into_inner() is stable
        // Unfortunately, this is the only stable way to use mutexes at the moment
        // Hopefully recovering from poisoning will be in 1.2
        // let mut s_guard = match sockets.lock() {
        //     Ok(guard) => guard,
        //     Err(poisoned) => poisoned.into_inner()
        // };
        let mut s_guard = sockets.lock().unwrap();
        let s_list = s_guard.deref_mut();
        for socket in s_list.iter_mut() {
            if socket.raw_fd() as u64 == fd {
                // We've found the socket for the event passed from epoll
                // Lets attempt to read from it
                match socket.read() {
                    Ok(()) => {
                        println!("Rust.EventLoop.handle_epoll_event.socket.read.Ok()");
                        for msg in socket.buffer().iter() {
                            let msg_for_debug = msg.clone();
                            match String::from_utf8(msg_for_debug) {
                                Ok(msg_str) => {
                                    println!("Rust.EventLoop.handle_epoll_event.socket.read.ok.msg_as_string: {}", msg_str);
                                }
                                Err(e) => {
                                    println!("Rust.EventLoop.handle_epoll_event.socket.read.ok.msg_as_string.Err: {}", e);
                                }
                            };

                            let list_handle = s_list_clone.clone();
                            let socket_clone = socket.clone();
                            let msg_clone = msg.clone();
                            let _ = uspace_tx.send((list_handle, socket_clone, msg_clone));
                        }
                    }
                    Err(e) => {
                        println!("Rust.EventLoop.handle_epoll_event.socket.read.Err(e): {}", e);
                        // We really don't care what happened, it all results in the same
                        // outcome - remove the socket from system...
                        let s_clone = socket.clone();
                        let epfd_clone = epoll_instance.clone();
                        EventLoop::remove_socket_from_epoll(s_clone, epfd_clone);

                        let s_id = socket.id();
                        let list_clone = s_list_clone.clone();
                        EventLoop::remove_socket_from_list(s_id, list_clone);
                    }
                }
            }
        }
    }

    /// Removes socket from the epoll watch list
    fn remove_socket_from_epoll(socket: Socket, epoll_instance: RawFd) {
        // In kernel versions before 2.6.9, the EPOLL_CTL_DEL operation required
        // a non-null pointer in event, even though this argument is ignored.
        // Since Linux 2.6.9, event can be specified as NULL when using
        // EPOLL_CTL_DEL.  Applications that need to be portable to kernels
        // before 2.6.9 should specify a non-null pointer in event.
        let event = Box::new(EpollEvent {
            data: 0 as u32,
            events: 0 as u32
        });
        let s_fd = socket.raw_fd();
        match epoll::ctl(epoll_instance, ctl_op::DEL, s_fd, event) {
            Ok(()) => println!("Socket removed from epoll watch list"),
            Err(e) => println!("Epoll CtrlError during del: {}", e)
        };
    }

    /// Removes socket from master list
    fn remove_socket_from_list(socket_id: u32, sockets: SocketList) {
        // TODO - Replace with recoverable version once into_inner() is stable
        // Unfortunately, this is the only stable way to use mutexes at the moment
        // Hopefully recovering from poisoning will be in 1.2
        // let mut s_guard = match sockets.lock() {
        //     Ok(guard) => guard,
        //     Err(poisoned) => poisoned.into_inner()
        // };
        let mut s_guard = sockets.lock().unwrap();
        let s_list = s_guard.deref_mut();

        let mut socket_found = false;
        let mut index: usize = 1;
        for socket in s_list.iter() {
            if socket.id() == socket_id {
                socket_found = true;
                break;
            }
            index += 1;
        }

        if !socket_found {
            println!("Socket not found in list...?");
            return;
        }

        if index == 1 {
            s_list.pop_front();
        } else {
            let mut split = s_list.split_off(index - 1);
            split.pop_front();
            s_list.append(&mut split);
        }

        println!("Socket removed sucessfully");
    }
}
<|endoftext|>


<commit_before>use regex::Regex;
use std::path::PathBuf;
use std::result::Result;
use subprocess::{Exec, ExitStatus, 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 = "de2237.rsync.net:./";

const RUN_MEDIA_KALVIN: &'static str = "/run/media/kalvin";

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

enum AuricOperationMode {
    Mount,
    Unmount,
    Rsync,
    RunAll,
}

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,
}

fn exec_with_stdout(command: &str, args: &[&str], error_hint: &str) -> Result<String, AuricError> {
    let result = Exec::cmd(command)
        .args(args)
        .stdout(Redirection::Pipe)
        .stderr(Redirection::Pipe)
        .capture()?;
    if !result.success() {
        return Err(AuricError::Subprocess(format!(
            "{} failed: ``{}''",
            error_hint,
            result.stderr_str(),
        )));
    }
    Ok(result.stdout_str())
}

fn exec(command: &str, args: &[&str], error_hint: &str) -> Result<(), AuricError> {
    exec_with_stdout(command, args, error_hint).map(|_stdout_str| ())
}

fn exec_rsync(args: &[&str]) -> Result<(), AuricError> {
    let result = Exec::cmd("rsync").args(args).join()?;
    if !result.success() {
        match result {
            ExitStatus::Exited(code) => {
                return Err(AuricError::Subprocess(format!(
                    "rsync exited with code {}",
                    code
                )))
            }
            ExitStatus::Signaled(signum) => {
                return Err(AuricError::Subprocess(format!(
                    "rsync exited with signal {}",
                    signum
                )))
            }
            ExitStatus::Other(code) => {
                return Err(AuricError::Subprocess(format!(
                    "unknown rsync error: {}",
                    code
                )))
            }
            ExitStatus::Undetermined => {
                return Err(AuricError::Subprocess("unknown rsync error!".to_string()))
            }
        }
    }
    Ok(())
}

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(());
        }

        // Invoke `sshfs`
        // *    with compression,
        // *    allowing other users to access the mounted filesystem
        //      (necessary to allow loop-mounts), and
        // *    attempting to reconnect if we lose the connection.
        exec(
            "sshfs",
            &[
                "-C",
                "-o",
                "allow_other,reconnect,ServerAliveInterval=26",
                RSYNC_DOT_NET_REMOTE_DIR,
                self.mountpoint.to_str().unwrap(),
            ],
            "sshfs mount",
        )
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        if !self.loop_backing_device_present() {
            return Ok(());
        }
        exec(
            "fusermount",
            &["-u", self.mountpoint.to_str().unwrap()],
            "fusermount -u",
        )
    }

    pub fn count_ssh_processes(&self) -> Result<usize, AuricError> {
        let pgrep_stdout = exec_with_stdout("pgrep", &["-af", "ssh"], "pgrep ssh")?;
        Ok(pgrep_stdout.lines().count())
    }

    pub fn wait_until_just_one_ssh_process(&self) -> Result<(), AuricError> {
        let mut tries: i32 = 5;
        let mut sleep_seconds: u64 = 1;
        let mut ssh_processes: usize = 0;
        while tries > 0 {
            ssh_processes = self.count_ssh_processes()?;
            if ssh_processes == 1 {
                return Ok(());
            }
            std::thread::sleep(std::time::Duration::from_secs(sleep_seconds));
            tries -= 1;
            sleep_seconds *= 2;
        }
        Err(AuricError::Subprocess(format!(
            "Timed out with {} ssh processes still present",
            ssh_processes
        )))
    }
}

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 losetup_stdout = exec_with_stdout("losetup", &["-l"], "loop device enumeration")?;
        for line in losetup_stdout.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(());
        }

        exec(
            "udisksctl",
            &["loop-setup", "-f", self.backing.to_str().unwrap()],
            "loop device attach",
        )
    }

    // 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);
            }
        };
        exec(
            "udisksctl",
            &["loop-delete", "-b", attached_loop.to_str().unwrap()],
            "loop device detach",
        )
    }
}

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

    pub fn unlock(&self, locked_device: &PathBuf) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["unlock", "-b", locked_device.to_str().unwrap()],
            format!("unlocking {}", locked_device.to_str().unwrap()).as_str(),
        )
    }

    pub fn lock(&self, underlying_device: &PathBuf) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["lock", "-b", underlying_device.to_str().unwrap()],
            format!("locking {}", underlying_device.to_str().unwrap()).as_str(),
        )
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["mount", "-b", self.device.to_str().unwrap()],
            format!("mounting {}", self.device.to_str().unwrap()).as_str(),
        )
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["unmount", "-b", self.device.to_str().unwrap()],
            format!("unmounting {}", self.device.to_str().unwrap()).as_str(),
        )
    }
}

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),
            "rsync" => return Ok(AuricOperationMode::Rsync),
            "push" => return Ok(AuricOperationMode::RunAll),
            _ => {
                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> {
        println!("{}", "Mounting sshfs...");
        self.sshfs_manager.mount()?;
        println!("{}", "Attaching loop device...");
        self.loop_manager.attach()?;
        println!("{}", "Finding newly attached loop device...");
        let loop_device = self.loop_manager.find()?;
        println!("{}", "Unlocking volume...");
        self.luks_manager.unlock(&loop_device)?;
        println!("{}", "Mounting volume...");
        self.luks_manager.mount()
    }

    fn unmount(&self) -> Result<(), AuricError> {
        println!("{}", "Unmounting volume...");
        self.luks_manager.unmount()?;
        println!("{}", "Finding backing loop device...");
        let loop_device = self.loop_manager.find()?;
        println!("{}", "Locking volume...");
        self.luks_manager.lock(&loop_device)?;
        println!("{}", "Detaching loop device...");
        self.loop_manager.detach()?;
        println!("{}", "Unmounting sshfs...");
        self.sshfs_manager.unmount()?;
        println!("{}", "Waiting for ssh processes to disappear...");
        self.sshfs_manager.wait_until_just_one_ssh_process()
    }

    fn rsync(&self) -> Result<(), AuricError> {
        // I'm not pulling in the `home_dir` crate just to grab a value
        // that I suspect will be immutable for many years to come.
        // Likewise, the user mount directory is lazily hardcoded.
        let mut remote_directory = PathBuf::from(RUN_MEDIA_KALVIN);
        remote_directory.push(LOOP_DEVICE_PARTITION_LABEL);
        if !remote_directory.is_dir() {
            return Err(AuricError::Invocation(format!(
                "remote directory ``{}'' not mounted",
                remote_directory.display()
            )));
        }

        exec_rsync(&[
            "--delete",
            "-avPS",
            "--exclude=/.cache/",
            "--exclude=/.cargo/",
            "--exclude=/.local/share/",
            "--exclude=/.mozilla/",
            "--exclude=/.thunderbird/",
            "--exclude=/Downloads/.firefox-nightly/",
            "--exclude=/Downloads/.thunderbird-beta/",
            "--delete-excluded",
            "/home/kalvin/",
            remote_directory.to_str().unwrap(),
        ])
    }

    fn run_all(&self) -> Result<(), AuricError> {
        self.mount()?;
        self.rsync()?;
        self.unmount()
    }

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

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>aa: Play a stupid word game<commit_after>use regex::Regex;
use std::path::PathBuf;
use std::result::Result;
use subprocess::{Exec, ExitStatus, 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 = "d\
                                                e\
                                                2\
                                                2\
                                                3\
                                                7\
                                                .rsync.net:./";

const RUN_MEDIA_KALVIN: &'static str = "/run/media/kalvin";

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

enum AuricOperationMode {
    Mount,
    Unmount,
    Rsync,
    RunAll,
}

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,
}

fn exec_with_stdout(command: &str, args: &[&str], error_hint: &str) -> Result<String, AuricError> {
    let result = Exec::cmd(command)
        .args(args)
        .stdout(Redirection::Pipe)
        .stderr(Redirection::Pipe)
        .capture()?;
    if !result.success() {
        return Err(AuricError::Subprocess(format!(
            "{} failed: ``{}''",
            error_hint,
            result.stderr_str(),
        )));
    }
    Ok(result.stdout_str())
}

fn exec(command: &str, args: &[&str], error_hint: &str) -> Result<(), AuricError> {
    exec_with_stdout(command, args, error_hint).map(|_stdout_str| ())
}

fn exec_rsync(args: &[&str]) -> Result<(), AuricError> {
    let result = Exec::cmd("rsync").args(args).join()?;
    if !result.success() {
        match result {
            ExitStatus::Exited(code) => {
                return Err(AuricError::Subprocess(format!(
                    "rsync exited with code {}",
                    code
                )))
            }
            ExitStatus::Signaled(signum) => {
                return Err(AuricError::Subprocess(format!(
                    "rsync exited with signal {}",
                    signum
                )))
            }
            ExitStatus::Other(code) => {
                return Err(AuricError::Subprocess(format!(
                    "unknown rsync error: {}",
                    code
                )))
            }
            ExitStatus::Undetermined => {
                return Err(AuricError::Subprocess("unknown rsync error!".to_string()))
            }
        }
    }
    Ok(())
}

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(());
        }

        // Invoke `sshfs`
        // *    with compression,
        // *    allowing other users to access the mounted filesystem
        //      (necessary to allow loop-mounts), and
        // *    attempting to reconnect if we lose the connection.
        exec(
            "sshfs",
            &[
                "-C",
                "-o",
                "allow_other,reconnect,ServerAliveInterval=26",
                RSYNC_DOT_NET_REMOTE_DIR,
                self.mountpoint.to_str().unwrap(),
            ],
            "sshfs mount",
        )
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        if !self.loop_backing_device_present() {
            return Ok(());
        }
        exec(
            "fusermount",
            &["-u", self.mountpoint.to_str().unwrap()],
            "fusermount -u",
        )
    }

    pub fn count_ssh_processes(&self) -> Result<usize, AuricError> {
        let pgrep_stdout = exec_with_stdout("pgrep", &["-af", "ssh"], "pgrep ssh")?;
        Ok(pgrep_stdout.lines().count())
    }

    pub fn wait_until_just_one_ssh_process(&self) -> Result<(), AuricError> {
        let mut tries: i32 = 5;
        let mut sleep_seconds: u64 = 1;
        let mut ssh_processes: usize = 0;
        while tries > 0 {
            ssh_processes = self.count_ssh_processes()?;
            if ssh_processes == 1 {
                return Ok(());
            }
            std::thread::sleep(std::time::Duration::from_secs(sleep_seconds));
            tries -= 1;
            sleep_seconds *= 2;
        }
        Err(AuricError::Subprocess(format!(
            "Timed out with {} ssh processes still present",
            ssh_processes
        )))
    }
}

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 losetup_stdout = exec_with_stdout("losetup", &["-l"], "loop device enumeration")?;
        for line in losetup_stdout.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(());
        }

        exec(
            "udisksctl",
            &["loop-setup", "-f", self.backing.to_str().unwrap()],
            "loop device attach",
        )
    }

    // 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);
            }
        };
        exec(
            "udisksctl",
            &["loop-delete", "-b", attached_loop.to_str().unwrap()],
            "loop device detach",
        )
    }
}

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

    pub fn unlock(&self, locked_device: &PathBuf) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["unlock", "-b", locked_device.to_str().unwrap()],
            format!("unlocking {}", locked_device.to_str().unwrap()).as_str(),
        )
    }

    pub fn lock(&self, underlying_device: &PathBuf) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["lock", "-b", underlying_device.to_str().unwrap()],
            format!("locking {}", underlying_device.to_str().unwrap()).as_str(),
        )
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["mount", "-b", self.device.to_str().unwrap()],
            format!("mounting {}", self.device.to_str().unwrap()).as_str(),
        )
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["unmount", "-b", self.device.to_str().unwrap()],
            format!("unmounting {}", self.device.to_str().unwrap()).as_str(),
        )
    }
}

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),
            "rsync" => return Ok(AuricOperationMode::Rsync),
            "push" => return Ok(AuricOperationMode::RunAll),
            _ => {
                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> {
        println!("{}", "Mounting sshfs...");
        self.sshfs_manager.mount()?;
        println!("{}", "Attaching loop device...");
        self.loop_manager.attach()?;
        println!("{}", "Finding newly attached loop device...");
        let loop_device = self.loop_manager.find()?;
        println!("{}", "Unlocking volume...");
        self.luks_manager.unlock(&loop_device)?;
        println!("{}", "Mounting volume...");
        self.luks_manager.mount()
    }

    fn unmount(&self) -> Result<(), AuricError> {
        println!("{}", "Unmounting volume...");
        self.luks_manager.unmount()?;
        println!("{}", "Finding backing loop device...");
        let loop_device = self.loop_manager.find()?;
        println!("{}", "Locking volume...");
        self.luks_manager.lock(&loop_device)?;
        println!("{}", "Detaching loop device...");
        self.loop_manager.detach()?;
        println!("{}", "Unmounting sshfs...");
        self.sshfs_manager.unmount()?;
        println!("{}", "Waiting for ssh processes to disappear...");
        self.sshfs_manager.wait_until_just_one_ssh_process()
    }

    fn rsync(&self) -> Result<(), AuricError> {
        // I'm not pulling in the `home_dir` crate just to grab a value
        // that I suspect will be immutable for many years to come.
        // Likewise, the user mount directory is lazily hardcoded.
        let mut remote_directory = PathBuf::from(RUN_MEDIA_KALVIN);
        remote_directory.push(LOOP_DEVICE_PARTITION_LABEL);
        if !remote_directory.is_dir() {
            return Err(AuricError::Invocation(format!(
                "remote directory ``{}'' not mounted",
                remote_directory.display()
            )));
        }

        exec_rsync(&[
            "--delete",
            "-avPS",
            "--exclude=/.cache/",
            "--exclude=/.cargo/",
            "--exclude=/.local/share/",
            "--exclude=/.mozilla/",
            "--exclude=/.thunderbird/",
            "--exclude=/Downloads/.firefox-nightly/",
            "--exclude=/Downloads/.thunderbird-beta/",
            "--delete-excluded",
            "/home/kalvin/",
            remote_directory.to_str().unwrap(),
        ])
    }

    fn run_all(&self) -> Result<(), AuricError> {
        self.mount()?;
        self.rsync()?;
        self.unmount()
    }

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

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|>