2018-09-18 15:36:38 +00:00
//
// R o u t e r R u n n e r . s w i f t
// I 2 P L a u n c h e r
//
// C r e a t e d b y M i k a l V i l l a o n 1 8 / 0 9 / 2 0 1 8 .
// C o p y r i g h t © 2 0 1 8 T h e I 2 P P r o j e c t . A l l r i g h t s r e s e r v e d .
//
import Foundation
2018-10-11 16:59:59 +00:00
class RouterRunner : NSObject {
2018-09-18 15:36:38 +00:00
2018-10-11 16:59:59 +00:00
var daemonPath : String ?
2018-09-18 15:36:38 +00:00
var arguments : String ?
2018-10-11 16:59:59 +00:00
static var launchAgent : LaunchAgent ?
let routerStatus : RouterProcessStatus = RouterProcessStatus ( )
2018-09-18 15:36:38 +00:00
var currentRunningProcess : Subprocess ?
var currentProcessResults : ExecutionResult ?
2018-12-08 09:12:24 +00:00
let domainLabel = String ( NSString ( format : " %@.I2PRouter " , APPDOMAIN ) )
2018-10-11 16:59:59 +00:00
2018-12-08 09:12:24 +00:00
let plistName = String ( NSString ( format : " %@.I2PRouter.plist " , APPDOMAIN ) )
2018-09-18 15:36:38 +00:00
2018-12-08 09:12:24 +00:00
// l e t a p p S u p p o r t P a t h = F i l e M a n a g e r . d e f a u l t . u r l s ( f o r : F i l e M a n a g e r . S e a r c h P a t h D i r e c t o r y . a p p l i c a t i o n S u p p o r t D i r e c t o r y , i n : F i l e M a n a g e r . S e a r c h P a t h D o m a i n M a s k . u s e r D o m a i n M a s k )
2018-10-11 16:59:59 +00:00
2018-10-13 03:54:01 +00:00
override init ( ) {
super . init ( )
}
2018-10-11 16:59:59 +00:00
func SetupAgent ( ) {
let agent = SetupAndReturnAgent ( )
RouterRunner . launchAgent = agent
}
typealias Async = ( _ success : ( ) -> Void , _ failure : ( NSError ) -> Void ) -> Void
func retry ( numberOfTimes : Int , _ sleepForS : UInt32 , task : ( ) -> Async , success : ( ) -> Void , failure : ( NSError ) -> Void ) {
task ( ) ( success , { error in
if numberOfTimes > 1 {
sleep ( sleepForS )
retry ( numberOfTimes : numberOfTimes - 1 , sleepForS , task : task , success : success , failure : failure )
} else {
failure ( error )
}
} )
}
func SetupAndReturnAgent ( ) -> LaunchAgent {
2018-12-08 09:12:24 +00:00
let applicationsSupportPath : URL = FileManager . default . urls ( for : . applicationSupportDirectory , in : . userDomainMask ) . first !
2018-10-11 16:59:59 +00:00
let defaultStartupFlags : [ String ] = [
" -Djava.awt.headless=true " ,
2018-12-08 09:12:24 +00:00
" " . appendingFormat ( " -Di2p.base.dir=%@ " , Preferences . shared ( ) . i2pBaseDirectory ) ,
" " . appendingFormat ( " -Dwrapper.logfile=%@/i2p/router.log " , applicationsSupportPath . absoluteString ) ,
" " . appendingFormat ( " -Dwrapper.java.pidfile=%@/i2p/router.pid " , applicationsSupportPath . absoluteString ) ,
" -Dwrapper.logfile.loglevel=DEBUG " , // TODO: A l l o w l o g l e v e l t o b e s e t f r o m P r e f e r e n c e s ?
2018-10-11 16:59:59 +00:00
" -Dwrapper.console.loglevel=DEBUG " ,
" net.i2p.router.Router "
]
2018-12-08 09:12:24 +00:00
let javaCliArgs = Preferences . shared ( ) . javaCommandPath . splitByWhitespace ( )
self . daemonPath = javaCliArgs [ 0 ]
2018-10-11 16:59:59 +00:00
self . arguments = defaultStartupFlags . joined ( separator : " " )
2018-12-08 09:12:24 +00:00
let basePath = Preferences . shared ( ) . i2pBaseDirectory
2018-10-11 16:59:59 +00:00
let jars = try ! FileManager . default . contentsOfDirectory ( atPath : basePath + " /lib " )
var classpath : String = " . "
for jar in jars {
classpath += " : " + basePath + " /lib/ " + jar
}
var cliArgs : [ String ] = [
self . daemonPath ! ,
]
2018-12-08 09:12:24 +00:00
cliArgs . append ( contentsOf : javaCliArgs . dropFirst ( ) )
2018-10-11 16:59:59 +00:00
cliArgs . append ( contentsOf : [
" -cp " ,
classpath ,
] )
2018-12-08 09:12:24 +00:00
// T h i s a l l o w j a v a a r g u m e n t s t o b e p a s s e d f r o m t h e s e t t i n g s
cliArgs . append ( contentsOf : Preferences . shared ( ) . javaCommandOptions . splitByWhitespace ( ) )
2018-10-11 16:59:59 +00:00
cliArgs . append ( contentsOf : defaultStartupFlags )
let agent = LaunchAgent ( label : self . domainLabel , program : cliArgs )
agent . launchOnlyOnce = false
agent . keepAlive = false
agent . workingDirectory = basePath
agent . userName = NSUserName ( )
2018-12-08 09:12:24 +00:00
agent . standardErrorPath = NSString ( format : " %@/router.stderr.log " , Preferences . shared ( ) . i2pLogDirectory ) as String
agent . standardOutPath = NSString ( format : " %@/router.stdout.log " , Preferences . shared ( ) . i2pLogDirectory ) as String
2018-10-11 16:59:59 +00:00
agent . environmentVariables = [
" PATH " : " /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin " ,
" I2PBASE " : basePath ,
2018-09-18 15:36:38 +00:00
]
2018-10-11 16:59:59 +00:00
agent . disabled = false
agent . processType = ProcessType . adaptive
RouterRunner . launchAgent = agent
2018-12-08 09:12:24 +00:00
// N O T E : I s u s p e c t t h i s i s b e t t e r t o s o l v e i n t h e a p p l i c a t i o n
agent . runAtLoad = false // P r e f e r e n c e s . s h a r e d ( ) . s t a r t R o u t e r O n L a u n c h e r S t a r t
2018-10-11 16:59:59 +00:00
agent . keepAlive = true
2018-10-13 03:54:01 +00:00
DispatchQueue ( label : " background_starter " ) . async {
do {
2018-12-08 09:12:24 +00:00
// TODO: F i n d a b e t t e r w a y t h a n s l e e p
2018-10-13 03:54:01 +00:00
try LaunchAgentManager . shared . write ( agent , called : self . plistName )
sleep ( 1 )
try LaunchAgentManager . shared . load ( agent )
sleep ( 1 )
let agentStatus = LaunchAgentManager . shared . status ( agent )
switch agentStatus {
case . running :
break
case . loaded :
DispatchQueue . main . async {
RouterManager . shared ( ) . eventManager . trigger ( eventName : " router_can_start " , information : agent )
}
break
case . unloaded :
break
}
} catch {
DispatchQueue . main . async {
RouterManager . shared ( ) . eventManager . trigger ( eventName : " router_setup_error " , information : " \( error ) " )
}
2018-10-11 16:59:59 +00:00
}
}
return agent
2018-09-18 15:36:38 +00:00
}
2018-10-13 03:54:01 +00:00
func StartAgent ( _ information : Any ? = nil ) {
2018-12-08 09:12:24 +00:00
if ( RouterManager . shared ( ) . checkIfRouterCanStart ( ) ) {
let agent = RouterRunner . launchAgent ? ? information as ! LaunchAgent
DispatchQueue ( label : " background_block " ) . async {
LaunchAgentManager . shared . start ( agent , { ( proc ) in
NSLog ( " Will call onLaunchdStarted " )
} )
}
} else {
SBridge . sendUserNotification ( " Whops! Please wait " , formattedMsg : " I'm sorry but it's still something unresolved before we can start the I2P router. Please wait. " )
2018-10-11 16:59:59 +00:00
}
2018-09-18 15:36:38 +00:00
}
2018-10-13 03:54:01 +00:00
func StopAgent ( _ callback : @ escaping ( ) -> ( ) = { } ) {
let agentStatus = LaunchAgentManager . shared . status ( RouterRunner . launchAgent ! )
DispatchQueue ( label : " background_block " ) . async {
do {
switch agentStatus {
case . running :
// F o r n o w w e n e e d t o u s e u n l o a d t o s t o p i t .
try LaunchAgentManager . shared . unload ( RouterRunner . launchAgent ! , { ( proc ) in
// C a l l e d w h e n s t o p i s a c t u a l l y e x e c u t e d
proc . waitUntilExit ( )
DispatchQueue . main . async {
RouterManager . shared ( ) . eventManager . trigger ( eventName : " router_stop " , information : " ok " )
callback ( )
}
} )
try LaunchAgentManager . shared . load ( RouterRunner . launchAgent ! )
break
case . unloaded :
// S e e m s i t s o m e t i m e s g e t u n l o a d e d o n s t o p , w e l o a d i t a g a i n .
try ! LaunchAgentManager . shared . load ( RouterRunner . launchAgent ! )
return
default : break
}
} catch {
NSLog ( " Error \( error ) " )
}
2018-10-11 16:59:59 +00:00
}
2018-09-18 15:36:38 +00:00
}
2018-10-11 16:59:59 +00:00
func SetupLaunchd ( ) {
do {
try LaunchAgentManager . shared . write ( RouterRunner . launchAgent ! , called : self . plistName )
try LaunchAgentManager . shared . load ( RouterRunner . launchAgent ! )
} catch {
RouterManager . shared ( ) . eventManager . trigger ( eventName : " router_exception " , information : error )
2018-09-18 15:36:38 +00:00
}
2018-10-11 16:59:59 +00:00
}
func TeardownLaunchd ( ) {
/* l e t s t a t u s = L a u n c h A g e n t M a n a g e r . s h a r e d . s t a t u s ( R o u t e r R u n n e r . l a u n c h A g e n t ! )
switch status {
case . running : */
do {
// U n l o a d n o m a t t e r p r e v i o u s s t a t e !
try LaunchAgentManager . shared . unload ( RouterRunner . launchAgent ! )
let plistPath = NSHomeDirectory ( ) + " /Library/LaunchAgents/ " + self . plistName
sleep ( 1 )
if FileManager . default . fileExists ( atPath : plistPath ) {
try FileManager . default . removeItem ( atPath : plistPath )
}
} catch LaunchAgentManagerError . urlNotSet ( label : self . domainLabel ) {
Logger . MLog ( level : 3 , " URL not set in launch agent " )
} catch {
Logger . MLog ( level : 3 , " " . appendingFormat ( " Error in launch agent: %s " , error as CVarArg ) )
RouterManager . shared ( ) . eventManager . trigger ( eventName : " router_exception " , information : error )
}
/* b r e a k
default : break
}
*/
2018-09-18 15:36:38 +00:00
}
}