2017-01-25 18:51:41 +01:00
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
2016-02-05 13:40:41 +01:00
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
2016-01-27 17:18:38 +01:00
//! Ethcore client application.
#![ warn(missing_docs) ]
2016-03-11 11:16:49 +01:00
#![ cfg_attr(feature= " dev " , feature(plugin)) ]
#![ cfg_attr(feature= " dev " , plugin(clippy)) ]
2016-04-06 10:07:24 +02:00
#![ cfg_attr(feature= " dev " , allow(useless_format)) ]
2016-07-10 13:18:33 +02:00
#![ cfg_attr(feature= " dev " , allow(match_bool)) ]
2016-04-21 13:12:43 +02:00
2017-02-13 16:38:47 +01:00
extern crate ansi_term ;
extern crate app_dirs ;
extern crate ctrlc ;
2016-01-23 23:53:20 +01:00
extern crate docopt ;
2016-01-09 23:21:57 +01:00
extern crate env_logger ;
2016-02-05 13:49:36 +01:00
extern crate fdlimit ;
2017-03-23 22:20:00 +01:00
extern crate futures ;
2017-07-11 12:23:46 +02:00
extern crate futures_cpupool ;
2017-02-13 16:38:47 +01:00
extern crate isatty ;
extern crate jsonrpc_core ;
extern crate num_cpus ;
2016-02-25 14:09:39 +01:00
extern crate number_prefix ;
2017-09-02 20:09:13 +02:00
extern crate parking_lot ;
2017-02-13 16:38:47 +01:00
extern crate regex ;
extern crate rlp ;
2016-03-09 14:11:15 +01:00
extern crate rpassword ;
2017-07-06 11:36:15 +02:00
extern crate rustc_hex ;
2016-04-10 15:12:20 +02:00
extern crate semver ;
2016-09-26 19:21:25 +02:00
extern crate serde ;
extern crate serde_json ;
2017-07-06 11:36:15 +02:00
#[ macro_use ]
extern crate serde_derive ;
2017-02-13 16:38:47 +01:00
extern crate time ;
extern crate toml ;
2016-09-01 14:55:07 +02:00
2017-02-13 16:38:47 +01:00
extern crate ethcore ;
extern crate ethcore_devtools as devtools ;
extern crate ethcore_io as io ;
extern crate ethcore_ipc as ipc ;
2016-07-09 17:18:34 +02:00
extern crate ethcore_ipc_hypervisor as hypervisor ;
2017-02-13 16:38:47 +01:00
extern crate ethcore_ipc_nano as nanoipc ;
extern crate ethcore_light as light ;
extern crate ethcore_logger ;
2016-09-01 14:55:07 +02:00
extern crate ethcore_util as util ;
2017-09-04 16:36:49 +02:00
extern crate ethcore_bigint as bigint ;
2017-07-28 19:06:39 +02:00
extern crate ethcore_network as network ;
2017-04-03 11:13:51 +02:00
extern crate ethkey ;
2017-02-13 16:38:47 +01:00
extern crate ethsync ;
2017-08-28 14:11:55 +02:00
extern crate node_health ;
2017-07-03 07:31:29 +02:00
extern crate panic_hook ;
2017-02-13 16:38:47 +01:00
extern crate parity_hash_fetch as hash_fetch ;
2017-02-16 14:51:33 +01:00
extern crate parity_ipfs_api ;
2017-04-13 16:32:07 +02:00
extern crate parity_local_store as local_store ;
2017-02-13 16:38:47 +01:00
extern crate parity_reactor ;
2017-04-13 16:32:07 +02:00
extern crate parity_rpc ;
2017-02-13 16:38:47 +01:00
extern crate parity_updater as updater ;
2017-07-14 20:40:28 +02:00
extern crate parity_whisper ;
2017-03-22 06:23:40 +01:00
extern crate path ;
2017-04-13 16:32:07 +02:00
extern crate rpc_cli ;
2017-08-29 14:38:01 +02:00
extern crate node_filter ;
2017-08-31 11:35:41 +02:00
extern crate hash ;
2016-09-01 14:55:07 +02:00
2017-02-14 22:45:43 +01:00
#[ macro_use ]
extern crate log as rlog ;
2016-08-24 18:35:38 +02:00
#[ cfg(feature= " stratum " ) ]
extern crate ethcore_stratum ;
2017-02-20 16:13:21 +01:00
#[ cfg(feature= " secretstore " ) ]
extern crate ethcore_secretstore ;
2016-05-26 18:21:15 +02:00
#[ cfg(feature = " dapps " ) ]
2017-04-03 10:27:37 +02:00
extern crate parity_dapps ;
#[ cfg(test) ]
#[ macro_use ]
extern crate pretty_assertions ;
2016-01-20 04:19:38 +01:00
2017-03-23 13:25:31 +01:00
#[ cfg(windows) ] extern crate ws2_32 ;
#[ cfg(windows) ] extern crate winapi ;
2017-02-13 16:38:47 +01:00
mod account ;
mod blockchain ;
2016-07-25 16:09:47 +02:00
mod cache ;
2016-04-21 15:41:25 +02:00
mod cli ;
2016-04-21 16:45:04 +02:00
mod configuration ;
2017-02-13 16:38:47 +01:00
mod dapps ;
2017-02-16 14:41:33 +01:00
mod ipfs ;
2016-07-25 16:09:47 +02:00
mod deprecated ;
mod dir ;
2017-02-13 16:38:47 +01:00
mod helpers ;
mod informant ;
2017-03-23 22:20:00 +01:00
mod light_helpers ;
2017-02-13 16:38:47 +01:00
mod migration ;
2016-07-16 14:24:57 +02:00
mod modules ;
2017-02-13 16:38:47 +01:00
mod params ;
2016-07-25 16:09:47 +02:00
mod presale ;
2017-02-13 16:38:47 +01:00
mod rpc ;
mod rpc_apis ;
2016-07-25 16:09:47 +02:00
mod run ;
2017-05-24 12:24:07 +02:00
mod secretstore ;
2017-02-13 16:38:47 +01:00
mod signer ;
mod snapshot ;
mod upgrade ;
mod url ;
mod user_defaults ;
2017-07-14 20:40:28 +02:00
mod whisper ;
2017-02-13 16:38:47 +01:00
2016-09-06 15:31:13 +02:00
#[ cfg(feature= " ipc " ) ]
2016-08-24 18:35:38 +02:00
mod boot ;
2017-02-13 16:38:47 +01:00
#[ cfg(feature= " ipc " ) ]
mod sync ;
2016-08-24 18:35:38 +02:00
#[ cfg(feature= " stratum " ) ]
mod stratum ;
2016-04-21 16:45:04 +02:00
2016-07-25 16:09:47 +02:00
use std ::{ process , env } ;
2016-12-10 23:58:39 +01:00
use std ::collections ::HashMap ;
2016-12-11 03:33:10 +01:00
use std ::io ::{ self as stdio , BufReader , Read , Write } ;
2017-03-23 13:25:31 +01:00
use std ::fs ::{ remove_file , metadata , File , create_dir_all } ;
2016-12-10 23:58:39 +01:00
use std ::path ::PathBuf ;
2017-08-31 11:35:41 +02:00
use hash ::keccak_buffer ;
2016-09-10 11:37:14 +02:00
use cli ::Args ;
2016-11-02 19:42:21 +01:00
use configuration ::{ Cmd , Execute , Configuration } ;
2016-07-25 16:09:47 +02:00
use deprecated ::find_deprecated ;
2016-11-02 19:42:21 +01:00
use ethcore_logger ::setup_log ;
2016-12-15 19:53:13 +01:00
use dir ::default_hypervisor_path ;
2016-07-25 16:09:47 +02:00
2016-09-23 15:28:09 +02:00
fn print_hash_of ( maybe_file : Option < String > ) -> Result < String , String > {
if let Some ( file ) = maybe_file {
2016-12-27 12:53:56 +01:00
let mut f = BufReader ::new ( File ::open ( & file ) . map_err ( | _ | " Unable to open file " . to_owned ( ) ) ? ) ;
2017-08-31 11:35:41 +02:00
let hash = keccak_buffer ( & mut f ) . map_err ( | _ | " Unable to read from file " . to_owned ( ) ) ? ;
2016-09-23 15:28:09 +02:00
Ok ( hash . hex ( ) )
} else {
Err ( " Streaming from standard input not yet supported. Specify a file. " . to_owned ( ) )
}
}
2016-12-10 23:58:39 +01:00
enum PostExecutionAction {
Print ( String ) ,
2017-03-13 12:10:53 +01:00
Restart ( Option < String > ) ,
2016-12-10 23:58:39 +01:00
Quit ,
}
2016-12-11 04:05:02 +01:00
fn execute ( command : Execute , can_restart : bool ) -> Result < PostExecutionAction , String > {
2016-11-02 19:42:21 +01:00
let logger = setup_log ( & command . logger ) . expect ( " Logger is initialized only once; qed " ) ;
match command . cmd {
2016-07-25 16:09:47 +02:00
Cmd ::Run ( run_cmd ) = > {
2017-03-13 12:10:53 +01:00
let ( restart , spec_name ) = run ::execute ( run_cmd , can_restart , logger ) ? ;
Ok ( if restart { PostExecutionAction ::Restart ( spec_name ) } else { PostExecutionAction ::Quit } )
2016-04-21 15:41:25 +02:00
} ,
2016-12-10 23:58:39 +01:00
Cmd ::Version = > Ok ( PostExecutionAction ::Print ( Args ::print_version ( ) ) ) ,
Cmd ::Hash ( maybe_file ) = > print_hash_of ( maybe_file ) . map ( | s | PostExecutionAction ::Print ( s ) ) ,
Cmd ::Account ( account_cmd ) = > account ::execute ( account_cmd ) . map ( | s | PostExecutionAction ::Print ( s ) ) ,
Cmd ::ImportPresaleWallet ( presale_cmd ) = > presale ::execute ( presale_cmd ) . map ( | s | PostExecutionAction ::Print ( s ) ) ,
2016-12-16 11:00:17 +01:00
Cmd ::Blockchain ( blockchain_cmd ) = > blockchain ::execute ( blockchain_cmd ) . map ( | _ | PostExecutionAction ::Quit ) ,
2017-07-28 19:07:38 +02:00
Cmd ::SignerToken ( ws_conf , ui_conf , logger_config ) = > signer ::execute ( ws_conf , ui_conf , logger_config ) . map ( | s | PostExecutionAction ::Print ( s ) ) ,
2016-12-15 18:23:02 +01:00
Cmd ::SignerSign { id , pwfile , port , authfile } = > rpc_cli ::signer_sign ( id , pwfile , port , authfile ) . map ( | s | PostExecutionAction ::Print ( s ) ) ,
Cmd ::SignerList { port , authfile } = > rpc_cli ::signer_list ( port , authfile ) . map ( | s | PostExecutionAction ::Print ( s ) ) ,
Cmd ::SignerReject { id , port , authfile } = > rpc_cli ::signer_reject ( id , port , authfile ) . map ( | s | PostExecutionAction ::Print ( s ) ) ,
2016-12-10 23:58:39 +01:00
Cmd ::Snapshot ( snapshot_cmd ) = > snapshot ::execute ( snapshot_cmd ) . map ( | s | PostExecutionAction ::Print ( s ) ) ,
2016-05-26 18:24:51 +02:00
}
2016-04-21 15:41:25 +02:00
}
2016-12-11 04:05:02 +01:00
fn start ( can_restart : bool ) -> Result < PostExecutionAction , String > {
2016-09-10 11:37:14 +02:00
let args : Vec < String > = env ::args ( ) . collect ( ) ;
2017-03-13 12:10:53 +01:00
let conf = Configuration ::parse ( & args , take_spec_name_override ( ) ) . unwrap_or_else ( | e | e . exit ( ) ) ;
2016-04-21 15:41:25 +02:00
2016-07-25 16:09:47 +02:00
let deprecated = find_deprecated ( & conf . args ) ;
for d in deprecated {
println! ( " {} " , d ) ;
2016-06-14 16:12:46 +02:00
}
2016-12-27 12:53:56 +01:00
let cmd = conf . into_command ( ) ? ;
2016-12-11 04:05:02 +01:00
execute ( cmd , can_restart )
2016-05-23 18:42:59 +02:00
}
2016-12-10 23:58:39 +01:00
#[ cfg(not(feature= " stratum " )) ]
fn stratum_main ( _ : & mut HashMap < String , fn ( ) > ) { }
2016-08-24 18:35:38 +02:00
#[ cfg(feature= " stratum " ) ]
2016-12-10 23:58:39 +01:00
fn stratum_main ( alt_mains : & mut HashMap < String , fn ( ) > ) {
alt_mains . insert ( " stratum " . to_owned ( ) , stratum ::main ) ;
2016-08-24 18:35:38 +02:00
}
2016-12-10 23:58:39 +01:00
#[ cfg(not(feature= " ipc " )) ]
fn sync_main ( _ : & mut HashMap < String , fn ( ) > ) { }
#[ cfg(feature= " ipc " ) ]
fn sync_main ( alt_mains : & mut HashMap < String , fn ( ) > ) {
alt_mains . insert ( " sync " . to_owned ( ) , sync ::main ) ;
2016-08-24 18:35:38 +02:00
}
2016-12-11 03:33:10 +01:00
fn updates_path ( name : & str ) -> PathBuf {
2016-12-15 19:53:13 +01:00
let mut dest = PathBuf ::from ( default_hypervisor_path ( ) ) ;
2016-12-11 03:33:10 +01:00
dest . push ( name ) ;
2016-12-10 23:58:39 +01:00
dest
2016-09-06 15:31:13 +02:00
}
2016-12-11 03:33:10 +01:00
fn latest_exe_path ( ) -> Option < PathBuf > {
File ::open ( updates_path ( " latest " ) ) . ok ( )
. and_then ( | mut f | { let mut exe = String ::new ( ) ; f . read_to_string ( & mut exe ) . ok ( ) . map ( | _ | updates_path ( & exe ) ) } )
2016-12-22 18:26:39 +01:00
}
2016-12-11 03:33:10 +01:00
2017-03-13 12:10:53 +01:00
fn set_spec_name_override ( spec_name : String ) {
2017-03-23 13:25:31 +01:00
if let Err ( e ) = create_dir_all ( default_hypervisor_path ( ) )
. and_then ( | _ | File ::create ( updates_path ( " spec_name_overide " ) )
. and_then ( | mut f | f . write_all ( spec_name . as_bytes ( ) ) ) )
2017-03-13 12:10:53 +01:00
{
2017-03-23 13:25:31 +01:00
warn! ( " Couldn't override chain spec: {} at {:?} " , e , updates_path ( " spec_name_overide " ) ) ;
2017-03-13 12:10:53 +01:00
}
}
fn take_spec_name_override ( ) -> Option < String > {
let p = updates_path ( " spec_name_overide " ) ;
let r = File ::open ( p . clone ( ) ) . ok ( )
. and_then ( | mut f | { let mut spec_name = String ::new ( ) ; f . read_to_string ( & mut spec_name ) . ok ( ) . map ( | _ | spec_name ) } ) ;
let _ = remove_file ( p ) ;
r
}
2017-01-17 23:34:46 +01:00
#[ cfg(windows) ]
fn global_cleanup ( ) {
// We need to cleanup all sockets before spawning another Parity process. This makes shure everything is cleaned up.
2017-02-13 16:38:47 +01:00
// The loop is required because of internal refernce counter for winsock dll. We don't know how many crates we use do
2017-01-17 23:34:46 +01:00
// initialize it. There's at least 2 now.
for _ in 0 .. 10 {
2017-03-23 13:25:31 +01:00
unsafe { ::ws2_32 ::WSACleanup ( ) ; }
}
}
#[ cfg(not(windows)) ]
fn global_init ( ) { }
#[ cfg(windows) ]
fn global_init ( ) {
// When restarting in the same process this reinits windows sockets.
unsafe {
const WS_VERSION : u16 = 0x202 ;
let mut wsdata : ::winapi ::winsock2 ::WSADATA = ::std ::mem ::zeroed ( ) ;
::ws2_32 ::WSAStartup ( WS_VERSION , & mut wsdata ) ;
2017-01-17 23:34:46 +01:00
}
}
#[ cfg(not(windows)) ]
fn global_cleanup ( ) { }
2016-12-10 23:58:39 +01:00
// Starts ~/.parity-updates/parity and returns the code it exits with.
fn run_parity ( ) -> Option < i32 > {
2017-03-23 13:25:31 +01:00
global_init ( ) ;
2016-12-11 04:05:02 +01:00
use ::std ::ffi ::OsString ;
let prefix = vec! [ OsString ::from ( " --can-restart " ) , OsString ::from ( " --force-direct " ) ] ;
2017-03-23 13:25:31 +01:00
let res = latest_exe_path ( ) . and_then ( | exe | process ::Command ::new ( exe )
2017-01-17 23:34:46 +01:00
. args ( & ( env ::args_os ( ) . skip ( 1 ) . chain ( prefix . into_iter ( ) ) . collect ::< Vec < _ > > ( ) ) )
2016-12-11 04:05:02 +01:00
. status ( )
. map ( | es | es . code ( ) . unwrap_or ( 128 ) )
. ok ( )
2017-03-23 13:25:31 +01:00
) ;
global_cleanup ( ) ;
res
2016-12-10 23:58:39 +01:00
}
const PLEASE_RESTART_EXIT_CODE : i32 = 69 ;
// Run our version of parity.
2016-12-22 18:26:39 +01:00
// Returns the exit error code.
2016-12-11 04:05:02 +01:00
fn main_direct ( can_restart : bool ) -> i32 {
2017-03-23 13:25:31 +01:00
global_init ( ) ;
2016-12-10 23:58:39 +01:00
let mut alt_mains = HashMap ::new ( ) ;
sync_main ( & mut alt_mains ) ;
stratum_main ( & mut alt_mains ) ;
2017-03-23 13:25:31 +01:00
let res = if let Some ( f ) = std ::env ::args ( ) . nth ( 1 ) . and_then ( | arg | alt_mains . get ( & arg . to_string ( ) ) ) {
2016-12-10 23:58:39 +01:00
f ( ) ;
0
2016-09-06 15:31:13 +02:00
} else {
2016-12-11 04:05:02 +01:00
match start ( can_restart ) {
2016-12-10 23:58:39 +01:00
Ok ( result ) = > match result {
2016-12-13 20:20:10 +01:00
PostExecutionAction ::Print ( s ) = > { println! ( " {} " , s ) ; 0 } ,
2017-03-13 12:10:53 +01:00
PostExecutionAction ::Restart ( spec_name_override ) = > {
if let Some ( spec_name ) = spec_name_override {
set_spec_name_override ( spec_name ) ;
}
PLEASE_RESTART_EXIT_CODE
} ,
2016-12-10 23:58:39 +01:00
PostExecutionAction ::Quit = > 0 ,
} ,
Err ( err ) = > {
writeln! ( & mut stdio ::stderr ( ) , " {} " , err ) . expect ( " StdErr available; qed " ) ;
1
} ,
}
2017-03-23 13:25:31 +01:00
} ;
global_cleanup ( ) ;
res
2016-09-06 15:31:13 +02:00
}
2016-12-10 23:58:39 +01:00
fn println_trace_main ( s : String ) {
if env ::var ( " RUST_LOG " ) . ok ( ) . and_then ( | s | s . find ( " main=trace " ) ) . is_some ( ) {
println! ( " {} " , s ) ;
2016-07-26 00:21:08 +02:00
}
2016-12-10 23:58:39 +01:00
}
2016-07-26 00:21:08 +02:00
2016-12-10 23:58:39 +01:00
#[ macro_export ]
macro_rules ! trace_main {
( $arg :expr ) = > ( println_trace_main ( $arg . into ( ) ) ) ;
( $( $arg :tt ) * ) = > ( println_trace_main ( format! ( " {} " , format_args! ( $( $arg ) * ) ) ) ) ;
}
2016-08-24 18:35:38 +02:00
2016-12-10 23:58:39 +01:00
fn main ( ) {
2017-07-03 07:31:29 +02:00
panic_hook ::set ( ) ;
2016-12-10 23:58:39 +01:00
// assuming the user is not running with `--force-direct`, then:
// if argv[0] == "parity" and this executable != ~/.parity-updates/parity, run that instead.
let force_direct = std ::env ::args ( ) . any ( | arg | arg = = " --force-direct " ) ;
let exe = std ::env ::current_exe ( ) . ok ( ) ;
2016-12-11 02:02:40 +01:00
let development = exe . as_ref ( ) . and_then ( | p | p . parent ( ) . and_then ( | p | p . parent ( ) ) . and_then ( | p | p . file_name ( ) ) . map ( | n | n = = " target " ) ) . unwrap_or ( false ) ;
2016-12-11 03:33:10 +01:00
let same_name = exe . as_ref ( ) . map ( | p | p . file_stem ( ) . map_or ( false , | s | s = = " parity " ) & & p . extension ( ) . map_or ( true , | x | x = = " exe " ) ) . unwrap_or ( false ) ;
2017-01-17 23:34:46 +01:00
trace_main! ( " Starting up {} (force-direct: {}, development: {}, same-name: {}) " , std ::env ::current_exe ( ) . map ( | x | format! ( " {} " , x . display ( ) ) ) . unwrap_or ( " <unknown> " . to_owned ( ) ) , force_direct , development , same_name ) ;
if ! force_direct & & ! development & & same_name {
2016-12-10 23:58:39 +01:00
// looks like we're not running ~/.parity-updates/parity when the user is expecting otherwise.
2016-12-22 18:26:39 +01:00
// Everything run inside a loop, so we'll be able to restart from the child into a new version seamlessly.
2016-12-10 23:58:39 +01:00
loop {
2016-12-22 18:26:39 +01:00
// If we fail to run the updated parity then fallback to local version.
2017-01-17 23:34:46 +01:00
let latest_exe = latest_exe_path ( ) ;
let have_update = latest_exe . as_ref ( ) . map_or ( false , | p | p . exists ( ) ) ;
let is_non_updated_current = exe . as_ref ( ) . map_or ( false , | exe | latest_exe . as_ref ( ) . map_or ( false , | lexe | exe . canonicalize ( ) . ok ( ) ! = lexe . canonicalize ( ) . ok ( ) ) ) ;
2017-03-11 14:45:50 +01:00
let update_is_newer = match (
latest_exe . as_ref ( )
. and_then ( | p | metadata ( p . as_path ( ) ) . ok ( ) )
. and_then ( | m | m . modified ( ) . ok ( ) ) ,
exe . as_ref ( )
. and_then ( | p | metadata ( p . as_path ( ) ) . ok ( ) )
. and_then ( | m | m . modified ( ) . ok ( ) )
) {
( Some ( latest_exe_time ) , Some ( this_exe_time ) ) if latest_exe_time > this_exe_time = > true ,
_ = > false ,
} ;
2017-03-13 12:10:53 +01:00
trace_main! ( " Starting... (have-update: {}, non-updated-current: {}, update-is-newer: {}) " , have_update , is_non_updated_current , update_is_newer ) ;
2017-03-11 14:45:50 +01:00
let exit_code = if have_update & & is_non_updated_current & & update_is_newer {
2017-01-17 23:34:46 +01:00
trace_main! ( " Attempting to run latest update ({})... " , latest_exe . as_ref ( ) . expect ( " guarded by have_update; latest_exe must exist for have_update; qed " ) . display ( ) ) ;
run_parity ( ) . unwrap_or_else ( | | { trace_main! ( " Falling back to local... " ) ; main_direct ( true ) } )
} else {
trace_main! ( " No latest update. Attempting to direct... " ) ;
main_direct ( true )
} ;
2016-12-10 23:58:39 +01:00
trace_main! ( " Latest exited with {} " , exit_code ) ;
if exit_code ! = PLEASE_RESTART_EXIT_CODE {
trace_main! ( " Quitting... " ) ;
process ::exit ( exit_code ) ;
}
trace_main! ( " Rerunning... " ) ;
2016-05-24 20:29:19 +02:00
}
2016-12-10 23:58:39 +01:00
} else {
trace_main! ( " Running direct " ) ;
// Otherwise, we're presumably running the version we want. Just run and fall-through.
2016-12-11 04:05:02 +01:00
let can_restart = std ::env ::args ( ) . any ( | arg | arg = = " --can-restart " ) ;
process ::exit ( main_direct ( can_restart ) ) ;
2016-05-24 20:29:19 +02:00
}
2016-06-07 17:21:19 +02:00
}