/**
 * Copyright (c) 2020 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */ import * as net from "net" ; import { Socket } from "net" ; import { platform } from "os" ; import semver from "semver" ; import { ipcMain } from "electron" ; import * as packageJson from "../../../package.json" ; import { staticAssetPath } from "../../utils/paths" ; import { log } from "../../shared/log/log" ; import { sudoExec } from "../../utils/sudo" ; import { uid } from "../../utils/user" ; import { MainIpcListenChannels } from "../../shared/ipc" ; const isWin = platform ( ) === "win32" ; function mystSockPath ( ) { if ( isWin ) { return "\\\\.\\pipe\\mystpipe" ; } return "/var/run/myst.sock" ; } const supervisorBin = ( ) : string => { let supervisorBinaryName = "bin/myst_supervisor" ; if ( isWin ) { supervisorBinaryName += ".exe" ; } return staticAssetPath ( supervisorBinaryName ) ; } ; export class Supervisor { conn ? : Socket ; registerIPC ( ) { ipcMain . handle ( MainIpcListenChannels . SupervisorConnect , ( ) => this . connect ( ) ) ; ipcMain . handle ( MainIpcListenChannels . SupervisorInstall , ( ) => this . install ( ) ) ; ipcMain . handle ( MainIpcListenChannels . SupervisorUpgrade , ( ) => this . upgrade ( ) ) ; ipcMain . handle ( MainIpcListenChannels . SupervisorDisconnect , ( ) => this . disconnect ( ) ) ; } async connect ( ) { log . info ( "Connecting to the supervisor..." ) ; const mystSock = mystSockPath ( ) ; return await new Promise ( ( resolve , reject ) => { this . conn = net . createConnection ( mystSock ) . on ( "connect" , ( ) => { log . info ( "Connected to: " , mystSock ) ; return resolve ( ) ; } ) . on ( "data" , ( data ) => { log . info ( "Server:" , data . toString ( ) ) ; } ) . on ( "error" , function ( data ) { return reject ( data ) ; } ) ; } ) ; } /**
     * Sends command to the supervisor and returns the response.
     */ request ( command , timeout = 2000 ) { return new Promise ( ( resolve , reject ) => { let timer ; this . conn ?. write ( command + "\n" ) ; const responseHandler = ( data ) => { clearTimeout ( timer ) ; const message = data . toString ( ) . trim ( ) ; if ( ! message . startsWith ( "ok" ) ) { reject ( new Error ( message . replace ( "error: " , "" ) ) ) ; } if ( message . startsWith ( "ok: " ) ) { resolve ( message . replace ( "ok: " , "" ) ) ; } else { resolve ( ) ; } } ; this . conn ?. once ( "data" , responseHandler ) ; timer = setTimeout ( ( ) => { reject ( new Error ( "timed out waiting for response" ) ) ; this . conn ?. removeListener ( "data" , responseHandler ) ; } , timeout ) ; } ) ; } runningVersion ( ) { return this . request ( "version" ) as Promise < string > ; } async upgrade ( ) { const bundledVersion = packageJson . dependencies [ "@mysteriumnetwork/node" ] ; let runningVersion = "" ; try { runningVersion = await this . runningVersion ( ) ; } catch ( err ) { log . error ( "Error checking running version" , err ) ; } log . info ( "Supervisor version bundled:" , bundledVersion , "running:" , runningVersion ) ; if ( runningVersion == bundledVersion ) { log . info ( "Running supervisor version matches, skipping the upgrade" ) ; return ; } if ( ! semver . valid ( runningVersion ) || ! semver . valid ( bundledVersion ) ) { log . info ( "Exotic versions of supervisor found, proceeding to upgrade" ) ; } else if ( semver . gte ( runningVersion , bundledVersion ) ) { log . info ( "Running supervisor version is compatible, skipping the upgrade" ) ; return ; } log . info ( `Upgrading supervisor ${ runningVersion } → ${ bundledVersion }` ) ; await supervisor . install ( ) ; } async install ( ) { return await new Promise ( ( resolve ) => { sudoExec ( `"${ supervisorBin ( ) }" -install -uid ${ uid ( ) }` ) ; const waitUntilConnected = ( ) : void => { this . connect ( ) . then ( ( ) => resolve ( ) ) . catch ( ( ) => setTimeout ( waitUntilConnected , 500 ) ) ; } ; setTimeout ( waitUntilConnected , 500 ) ; } ) ; } disconnect ( ) { if ( this . conn ) { this . conn . destroy ( ) ; } } } export const supervisor = new Supervisor ( ) ;