<commit_before>/* 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/. */

// Needed to derive `Serialize` on ServiceProperties
#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]
// For Docopt macro
#![plugin(docopt_macros)]

// Make linter fail for every warning
#![plugin(clippy)]
#![deny(clippy)]

#![cfg_attr(test, feature(const_fn))] // Dependency of stainless
#![cfg_attr(test, plugin(stainless))] // Test runner

#![feature(reflect_marker)]

#![feature(associated_consts)]

extern crate chrono;
extern crate core;
extern crate docopt;
extern crate env_logger;
extern crate foxbox_adapters;
extern crate foxbox_taxonomy;
extern crate foxbox_users;
#[macro_use]
extern crate hyper;
#[macro_use]
extern crate iron;
extern crate iron_cors;
#[cfg(test)]
extern crate iron_test;
extern crate libc;
#[macro_use]
extern crate log;
extern crate mio;
extern crate mount;
extern crate nix;
extern crate openssl;
extern crate openssl_sys;
extern crate rand;
extern crate router;
extern crate rustc_serialize;
extern crate rusqlite;
extern crate serde;
extern crate serde_json;
extern crate staticfile;
extern crate time;
extern crate timer;
extern crate transformable_channels;
extern crate unicase;
extern crate uuid;
extern crate ws;
extern crate multicast_dns;
extern crate xml;

#[cfg(test)]
extern crate regex;
#[cfg(test)]
extern crate tempdir;

// Need to be declared first so to let the macros be visible from other modules.
#[macro_use]
mod utils;
mod adapters;
mod config_store;
mod controller;
mod http_server;
mod managed_process;
mod profile_service;
mod registration;
mod service;
mod upnp;
mod service_router;
mod stable_uuid;
mod static_router;
mod taxonomy_router;
mod tls;
mod traits;
mod tunnel_controller;
mod ws_server;

#[cfg(test)]
mod stubs {
    #![allow(dead_code)]
    #![allow(unused_variables)]
    #![allow(boxed_local)]
    pub mod service;
    pub mod controller;
}

use controller::FoxBox;
use env_logger::LogBuilder;
use tunnel_controller:: { TunnelConfig, Tunnel };
use libc::SIGINT;
use log::{ LogRecord, LogLevelFilter };

use multicast_dns::host::HostManager;
use profile_service::ProfilePath;
use std::env;
use std::mem;
use std::sync::atomic::{ AtomicBool, Ordering, ATOMIC_BOOL_INIT };
use tls::TlsOption;
use traits::Controller;

docopt!(Args derive Debug, "
Usage: foxbox [-v] [-h] [-l <hostname>] [-p <port>] [-w <wsport>] [-d <profile_path>] [-r <url>] [-i <iface>] [-t <tunnel>] [-s <secret>] [--disable-tls] [--remote-name <hostname>] [-c <namespace;key;value>]...

Options:
    -v, --verbose            Toggle verbose output.
    -l, --local-name <hostname>    Set local hostname. Linux only. Requires to be a member of the netdev group.
    -p, --port <port>        Set port to listen on for http connections. [default: 3000]
    -w, --wsport <wsport>    Set port to listen on for websocket. [default: 4000]
    -d, --profile <path>     Set profile path to store user data.
    -r, --register <url>     Change the url of the registration endpoint. [default: http://localhost:4242/register]
    -i, --iface <iface>      Specify the local IP interface.
    -t, --tunnel <tunnel>    Set the tunnel endpoint's hostname. If omitted, the tunnel is disabled.
    -s, --tunnel-secret <secret>    Set the tunnel shared secret. [default: secret]
        --disable-tls               Run as a plain HTTP server, disabling encryption.
        --remote-name <hostname>    Set remote hostname. This the URL to access the box through the bridge. If omitted, the tunnel is disabled
    -c, --config <namespace;key;value>  Set configuration override
    -h, --help               Print this help menu.
",
        flag_local_name: Option<String>,
        flag_port: u16,
        flag_wsport: u16,
        flag_profile: Option<String>,
        flag_register: String,
        flag_iface: Option<String>,
        flag_tunnel: Option<String>,
        flag_tunnel_secret: String,
        flag_disable_tls: bool,
        flag_remote_name: Option<String>,
        flag_config: Option<Vec<String>>);

/// Updates local host name with the provided host name string. If requested host name
/// is not available (used by anyone else on the same network) then collision
/// resolution logic is triggered and alternative name is chosen automatically
/// (host name plus "-2", "-3" and etc. postfix). This function blocks until host name
/// is updated and returns actual host name.
///
/// # Panics
///
/// Panics if provided host name is not valid non-FQDN host name.
///
/// # Arguments
///
/// * `hostname` - host name name we'd like to set (should be a valid non-FQDN host name).
fn update_hostname(hostname: String) -> Option<String> {
    let host_manager = HostManager::new();

    if !host_manager.is_valid_name(&hostname) {
        panic!("Host name `{}` is not a valid host name!", &hostname);
    }

    Some(host_manager.set_name(&hostname))
}

// Handle SIGINT (Ctrl-C) for manual shutdown.
// Signal handlers must not do anything substantial. To trigger shutdown, we atomically
// flip this flag; the event loop checks the flag and exits accordingly.
static SHUTDOWN_FLAG: AtomicBool = ATOMIC_BOOL_INIT;
unsafe fn handle_sigint(_:i32) {
    SHUTDOWN_FLAG.store(true, Ordering::Release);
}

#[cfg(any(target_os = "linux", target_os = "android"))]
#[inline]
fn tid_str() -> String {
    // gettid only exists for the linux and android variants of nix
    format!("({}) ", nix::unistd::gettid())
}

#[cfg(not(any(target_os = "linux", target_os = "android")))]
#[inline]
fn tid_str() -> &'static str {
    ""
}

fn main() {
    unsafe {
        libc::signal(SIGINT, mem::transmute(handle_sigint));
    }

    let format = |record: &LogRecord| {
        let t = time::now();
        format!("{}.{:03}: {}{:5} {}",
            time::strftime("%Y-%m-%d %H:%M:%S", &t).unwrap(),
            t.tm_nsec / 1000_000,
            tid_str(),
            record.level(),
            record.args()
        )
    };
    let mut builder = LogBuilder::new();
    builder.format(format).filter(None, LogLevelFilter::Info);

    if env::var("RUST_LOG").is_ok() {
       builder.parse(&env::var("RUST_LOG").unwrap());
    }
    builder.init().unwrap();

    let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());<commit_msg>Colorizing logger output on terminals<commit_after>/* 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/. */

// Needed to derive `Serialize` on ServiceProperties
#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]
// For Docopt macro
#![plugin(docopt_macros)]

// Make linter fail for every warning
#![plugin(clippy)]
#![deny(clippy)]

#![cfg_attr(test, feature(const_fn))] // Dependency of stainless
#![cfg_attr(test, plugin(stainless))] // Test runner

#![feature(reflect_marker)]

#![feature(associated_consts)]

extern crate chrono;
extern crate core;
extern crate docopt;
extern crate env_logger;
extern crate foxbox_adapters;
extern crate foxbox_taxonomy;
extern crate foxbox_users;
#[macro_use]
extern crate hyper;
#[macro_use]
extern crate iron;
extern crate iron_cors;
#[cfg(test)]
extern crate iron_test;
extern crate libc;
#[macro_use]
extern crate log;
extern crate mio;
extern crate mount;
extern crate nix;
extern crate openssl;
extern crate openssl_sys;
extern crate rand;
extern crate router;
extern crate rustc_serialize;
extern crate rusqlite;
extern crate serde;
extern crate serde_json;
extern crate staticfile;
extern crate time;
extern crate timer;
extern crate transformable_channels;
extern crate unicase;
extern crate uuid;
extern crate ws;
extern crate multicast_dns;
extern crate xml;

#[cfg(test)]
extern crate regex;
#[cfg(test)]
extern crate tempdir;

// Need to be declared first so to let the macros be visible from other modules.
#[macro_use]
mod utils;
mod adapters;
mod config_store;
mod controller;
mod http_server;
mod managed_process;
mod profile_service;
mod registration;
mod service;
mod upnp;
mod service_router;
mod stable_uuid;
mod static_router;
mod taxonomy_router;
mod tls;
mod traits;
mod tunnel_controller;
mod ws_server;

#[cfg(test)]
mod stubs {
    #![allow(dead_code)]
    #![allow(unused_variables)]
    #![allow(boxed_local)]
    pub mod service;
    pub mod controller;
}

use controller::FoxBox;
use env_logger::LogBuilder;
use tunnel_controller:: { TunnelConfig, Tunnel };
use libc::SIGINT;
use log::{ LogRecord, LogLevelFilter };

use multicast_dns::host::HostManager;
use profile_service::ProfilePath;
use std::env;
use std::mem;
use std::sync::atomic::{ AtomicBool, Ordering, ATOMIC_BOOL_INIT };
use tls::TlsOption;
use traits::Controller;

docopt!(Args derive Debug, "
Usage: foxbox [-v] [-h] [-l <hostname>] [-p <port>] [-w <wsport>] [-d <profile_path>] [-r <url>] [-i <iface>] [-t <tunnel>] [-s <secret>] [--disable-tls] [--remote-name <hostname>] [-c <namespace;key;value>]...

Options:
    -v, --verbose            Toggle verbose output.
    -l, --local-name <hostname>    Set local hostname. Linux only. Requires to be a member of the netdev group.
    -p, --port <port>        Set port to listen on for http connections. [default: 3000]
    -w, --wsport <wsport>    Set port to listen on for websocket. [default: 4000]
    -d, --profile <path>     Set profile path to store user data.
    -r, --register <url>     Change the url of the registration endpoint. [default: http://localhost:4242/register]
    -i, --iface <iface>      Specify the local IP interface.
    -t, --tunnel <tunnel>    Set the tunnel endpoint's hostname. If omitted, the tunnel is disabled.
    -s, --tunnel-secret <secret>    Set the tunnel shared secret. [default: secret]
        --disable-tls               Run as a plain HTTP server, disabling encryption.
        --remote-name <hostname>    Set remote hostname. This the URL to access the box through the bridge. If omitted, the tunnel is disabled
    -c, --config <namespace;key;value>  Set configuration override
    -h, --help               Print this help menu.
",
        flag_local_name: Option<String>,
        flag_port: u16,
        flag_wsport: u16,
        flag_profile: Option<String>,
        flag_register: String,
        flag_iface: Option<String>,
        flag_tunnel: Option<String>,
        flag_tunnel_secret: String,
        flag_disable_tls: bool,
        flag_remote_name: Option<String>,
        flag_config: Option<Vec<String>>);

/// Updates local host name with the provided host name string. If requested host name
/// is not available (used by anyone else on the same network) then collision
/// resolution logic is triggered and alternative name is chosen automatically
/// (host name plus "-2", "-3" and etc. postfix). This function blocks until host name
/// is updated and returns actual host name.
///
/// # Panics
///
/// Panics if provided host name is not valid non-FQDN host name.
///
/// # Arguments
///
/// * `hostname` - host name name we'd like to set (should be a valid non-FQDN host name).
fn update_hostname(hostname: String) -> Option<String> {
    let host_manager = HostManager::new();

    if !host_manager.is_valid_name(&hostname) {
        panic!("Host name `{}` is not a valid host name!", &hostname);
    }

    Some(host_manager.set_name(&hostname))
}

// Handle SIGINT (Ctrl-C) for manual shutdown.
// Signal handlers must not do anything substantial. To trigger shutdown, we atomically
// flip this flag; the event loop checks the flag and exits accordingly.
static SHUTDOWN_FLAG: AtomicBool = ATOMIC_BOOL_INIT;
unsafe fn handle_sigint(_:i32) {
    SHUTDOWN_FLAG.store(true, Ordering::Release);
}

#[cfg(any(target_os = "linux", target_os = "android"))]
#[inline]
fn tid_str() -> String {
    // gettid only exists for the linux and android variants of nix
    format!("({}) ", nix::unistd::gettid())
}

#[cfg(not(any(target_os = "linux", target_os = "android")))]
#[inline]
fn tid_str() -> &'static str {
    ""
}

fn main() {
    unsafe {
        libc::signal(SIGINT, mem::transmute(handle_sigint));
    }

    let mut builder = LogBuilder::new();
    let istty = unsafe { libc::isatty(libc::STDERR_FILENO as i32) } != 0;
    if istty {
        // Colorized output formatter
        let format = |record: &LogRecord| {
            let t = time::now();
            let level_color = match record.level() {
                log::LogLevel::Error => "\x1b[1;31m",  // bold red
                log::LogLevel::Warn  => "\x1b[1;33m",  // bold yellow
                log::LogLevel::Info  => "\x1b[1;32m",  // bold green
                log::LogLevel::Debug => "\x1b[1;34m",  // bold blue
                log::LogLevel::Trace => "\x1b[1;35m"   // bold magenta
            };
            format!("[\x1b[90m{}.{:03}\x1b[0m] {}{}{:5}\x1b[0m {}",
                time::strftime("%Y-%m-%d %H:%M:%S", &t).unwrap(),
                t.tm_nsec / 1_000_000,
                tid_str(),
                level_color,
                record.level(),
                record.args()
            )
        };
        builder.format(format).filter(None, LogLevelFilter::Info);
    } else {
        // Plain output formatter
        let format = |record: &LogRecord| {
            let t = time::now();
            format!("{}.{:03} {}{:5} {}",
                time::strftime("%Y-%m-%d %H:%M:%S", &t).unwrap(),
                t.tm_nsec / 1_000_000,
                tid_str(),
                record.level(),
                record.args()
            )
        };
        builder.format(format).filter(None, LogLevelFilter::Info);
    }

    if env::var("RUST_LOG").is_ok() {
       builder.parse(&env::var("RUST_LOG").unwrap());
    }
    builder.init().unwrap();

    let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());
{'fraction_non_alphanumeric': 0.13379320255802257, 'fraction_numerical': 0.007760293166630739, 'mean_word_length': 3.279827798277983, 'pattern_counts': {'":': 0, '<': 59, '<?xml version=': 0, '>': 70, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5162114', 'n_tokens_mistral': 4513, 'n_tokens_neox': 4343, 'n_words': 1419}