github.com/aitjcize/Overlord@v0.0.0-20240314041920-104a804cf5e8/overlord/ghost.go (about)

     1  // Copyright 2015 The Chromium OS Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style license that can be
     3  // found in the LICENSE file.
     4  
     5  package overlord
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"crypto/sha1"
    11  	"crypto/tls"
    12  	"crypto/x509"
    13  	"encoding/json"
    14  	"errors"
    15  	"fmt"
    16  	"io"
    17  	"io/ioutil"
    18  	"log"
    19  	"net"
    20  	"net/http"
    21  	"net/rpc"
    22  	"net/rpc/jsonrpc"
    23  	"os"
    24  	"os/exec"
    25  	"path/filepath"
    26  	"runtime"
    27  	"strconv"
    28  	"strings"
    29  	"sync"
    30  	"syscall"
    31  	"time"
    32  	"unsafe"
    33  
    34  	"github.com/creack/pty"
    35  	"github.com/gorilla/websocket"
    36  	"github.com/pkg/term/termios"
    37  	uuid "github.com/satori/go.uuid"
    38  )
    39  
    40  var ghostRPCStubPort = GetenvInt("GHOST_RPC_PORT", 4499)
    41  
    42  const (
    43  	defaultShell         = "/bin/sh"
    44  	pingInterval         = 10 * time.Second
    45  	readTimeout          = 3 * time.Second
    46  	connectTimeout       = 10 * time.Second
    47  	httpRequestTimeout   = 30 * time.Second
    48  	retryIntervalSeconds = 2
    49  	blockSize            = 4096
    50  )
    51  
    52  // Exported
    53  const (
    54  	RandomMID = "##random_mid##" // Random Machine ID identifier
    55  )
    56  
    57  // TLS modes
    58  const (
    59  	TLSDetect = iota
    60  	TLSForceDisable
    61  	TLSForceEnable
    62  )
    63  
    64  // Terminal resize control
    65  const (
    66  	controlNone  = 255 // Control State None
    67  	controlStart = 128 // Control Start Code
    68  	controlEnd   = 129 // Control End Code
    69  )
    70  
    71  // Stream control
    72  const (
    73  	stdinClosed = "##STDIN_CLOSED##"
    74  )
    75  
    76  // Registration status
    77  const (
    78  	statusDisconnected = "disconnected"
    79  )
    80  
    81  type ghostRPCStub struct {
    82  	ghost *Ghost
    83  }
    84  
    85  // EmptyArgs for RPC.
    86  type EmptyArgs struct {
    87  }
    88  
    89  // EmptyReply for RPC.
    90  type EmptyReply struct {
    91  }
    92  
    93  func (rpcStub *ghostRPCStub) Reconnect(args *EmptyArgs, reply *EmptyReply) error {
    94  	rpcStub.ghost.reset = true
    95  	return nil
    96  }
    97  
    98  func (rpcStub *ghostRPCStub) GetStatus(args *EmptyArgs, reply *string) error {
    99  	*reply = rpcStub.ghost.RegisterStatus
   100  	if rpcStub.ghost.RegisterStatus == Success {
   101  		*reply = fmt.Sprintf("%s %s", *reply, rpcStub.ghost.connectedAddr)
   102  	}
   103  	return nil
   104  }
   105  
   106  func (rpcStub *ghostRPCStub) RegisterTTY(args []string, reply *EmptyReply) error {
   107  	rpcStub.ghost.RegisterTTY(args[0], args[1])
   108  	return nil
   109  }
   110  
   111  func (rpcStub *ghostRPCStub) RegisterSession(args []string, reply *EmptyReply) error {
   112  	rpcStub.ghost.RegisterSession(args[0], args[1])
   113  	return nil
   114  }
   115  
   116  func (rpcStub *ghostRPCStub) AddToDownloadQueue(args []string, reply *EmptyReply) error {
   117  	rpcStub.ghost.AddToDownloadQueue(args[0], args[1])
   118  	return nil
   119  }
   120  
   121  // downloadInfo is a structure that we be place into download queue.
   122  // In our case since we always execute 'ghost --download' in our pseudo
   123  // terminal so ttyName will always have the form /dev/pts/X
   124  type downloadInfo struct {
   125  	Ttyname  string
   126  	Filename string
   127  }
   128  
   129  // fileOperation is a structure for storing file upload/download intent.
   130  type fileOperation struct {
   131  	Action   string
   132  	Filename string
   133  	Perm     int
   134  }
   135  
   136  type fileUploadContext struct {
   137  	Ready bool
   138  	Data  chan []byte
   139  }
   140  
   141  type tlsSettings struct {
   142  	Enabled     bool        // TLS enabled or not
   143  	tlsCertFile string      // TLS certificate in PEM format
   144  	verify      bool        // Wether or not to verify the certificate
   145  	Config      *tls.Config // TLS configuration
   146  }
   147  
   148  func newTLSSettings(tlsCertFile string, verify bool) *tlsSettings {
   149  	return &tlsSettings{false, tlsCertFile, verify, nil}
   150  }
   151  
   152  func (t *tlsSettings) updateContext() {
   153  	if !t.Enabled {
   154  		t.Config = nil
   155  		return
   156  	}
   157  
   158  	if t.verify {
   159  		config := &tls.Config{
   160  			InsecureSkipVerify: false,
   161  			MinVersion:         tls.VersionTLS12,
   162  			RootCAs:            nil,
   163  		}
   164  		if t.tlsCertFile != "" {
   165  			log.Println("TLSSettings: using user-supplied ca-certificate")
   166  			cert, err := ioutil.ReadFile(t.tlsCertFile)
   167  			if err != nil {
   168  				log.Fatalln(err)
   169  			}
   170  			caCertPool := x509.NewCertPool()
   171  			caCertPool.AppendCertsFromPEM(cert)
   172  			config.RootCAs = caCertPool
   173  		} else {
   174  			log.Println("TLSSettings: using built-in ca-certificates")
   175  		}
   176  		t.Config = config
   177  	} else {
   178  		log.Println("TLSSettings: skipping TLS verification!!!")
   179  		t.Config = &tls.Config{InsecureSkipVerify: true}
   180  	}
   181  }
   182  
   183  func (t *tlsSettings) SetEnabled(enabled bool) {
   184  	status := "True"
   185  	if !enabled {
   186  		status = "False"
   187  	}
   188  
   189  	log.Println("TLSSettings: enabled:", status)
   190  	if enabled != t.Enabled {
   191  		t.Enabled = enabled
   192  		t.updateContext()
   193  	}
   194  }
   195  
   196  // Ghost type is the main context for storing the ghost state.
   197  type Ghost struct {
   198  	*RPCCore
   199  	addrs           []string               // List of possible Overlord addresses
   200  	server          *rpc.Server            // RPC server handle
   201  	connectedAddr   string                 // Current connected Overlord address
   202  	tls             *tlsSettings           // TLS settings
   203  	mode            int                    // mode, see constants.go
   204  	mid             string                 // Machine ID
   205  	sid             string                 // Session ID
   206  	terminalSid     string                 // Associated terminal session ID
   207  	ttyName2Sid     sync.Map               // Mapping between ttyName and Sid
   208  	terminalSid2Pid sync.Map               // Mapping between terminalSid and pid
   209  	propFile        string                 // Properties file filename
   210  	properties      map[string]interface{} // Client properties
   211  	RegisterStatus  string                 // Register status from server response
   212  	reset           bool                   // Whether to reset the connection
   213  	quit            bool                   // Whether to quit the connection
   214  	readChan        chan []byte            // The incoming data channel
   215  	readErrChan     chan error             // The incoming data error channel
   216  	pauseLanDisc    bool                   // Stop LAN discovery
   217  	ttyDevice       string                 // Terminal device to open
   218  	shellCommand    string                 // Shell command to execute
   219  	fileOp          fileOperation          // File operation name
   220  	downloadQueue   chan downloadInfo      // Download queue
   221  	uploadContext   fileUploadContext      // File upload context
   222  	port            int                    // Port number to forward
   223  	tlsMode         int                    // TLS mode
   224  }
   225  
   226  // NewGhost creates a Ghost object.
   227  func NewGhost(addrs []string, tls *tlsSettings, mode int, mid string) *Ghost {
   228  	var (
   229  		finalMid string
   230  		err      error
   231  	)
   232  
   233  	if mid == RandomMID {
   234  		finalMid = uuid.NewV4().String()
   235  	} else if mid != "" {
   236  		finalMid = mid
   237  	} else {
   238  		finalMid, err = GetMachineID()
   239  		if err != nil {
   240  			log.Fatalln("Unable to get machine ID:", err)
   241  		}
   242  	}
   243  	return &Ghost{
   244  		RPCCore:        NewRPCCore(nil),
   245  		addrs:          addrs,
   246  		tls:            tls,
   247  		mode:           mode,
   248  		mid:            finalMid,
   249  		sid:            uuid.NewV4().String(),
   250  		properties:     make(map[string]interface{}),
   251  		RegisterStatus: statusDisconnected,
   252  		reset:          false,
   253  		quit:           false,
   254  		pauseLanDisc:   false,
   255  		downloadQueue:  make(chan downloadInfo),
   256  		uploadContext:  fileUploadContext{Data: make(chan []byte)},
   257  	}
   258  }
   259  
   260  // SetSid sets the Session ID for the Ghost instance.
   261  func (ghost *Ghost) SetSid(sid string) *Ghost {
   262  	ghost.sid = sid
   263  	return ghost
   264  }
   265  
   266  // SetTerminalSid sets the terminal session ID for the Ghost instance.
   267  func (ghost *Ghost) SetTerminalSid(sid string) *Ghost {
   268  	ghost.terminalSid = sid
   269  	return ghost
   270  }
   271  
   272  // SetPropFile sets the property file filename.
   273  func (ghost *Ghost) SetPropFile(propFile string) *Ghost {
   274  	ghost.propFile = propFile
   275  	return ghost
   276  }
   277  
   278  // SetTtyDevice sets the TTY device name to open.
   279  func (ghost *Ghost) SetTtyDevice(ttyDevice string) *Ghost {
   280  	ghost.ttyDevice = ttyDevice
   281  	return ghost
   282  }
   283  
   284  // SetShellCommand sets the shell comamnd to execute.
   285  func (ghost *Ghost) SetShellCommand(command string) *Ghost {
   286  	ghost.shellCommand = command
   287  	return ghost
   288  }
   289  
   290  // SetFileOp sets the file operation to perform.
   291  func (ghost *Ghost) SetFileOp(operation, filename string, perm int) *Ghost {
   292  	ghost.fileOp.Action = operation
   293  	ghost.fileOp.Filename = filename
   294  	ghost.fileOp.Perm = perm
   295  	return ghost
   296  }
   297  
   298  // SetModeForwardPort sets the port to forward.
   299  func (ghost *Ghost) SetModeForwardPort(port int) *Ghost {
   300  	ghost.port = port
   301  	return ghost
   302  }
   303  
   304  // SetTLSMode sets the mode of tls detection.
   305  func (ghost *Ghost) SetTLSMode(mode int) *Ghost {
   306  	ghost.tlsMode = mode
   307  	return ghost
   308  }
   309  
   310  func (ghost *Ghost) existsInAddr(target string) bool {
   311  	for _, x := range ghost.addrs {
   312  		if target == x {
   313  			return true
   314  		}
   315  	}
   316  	return false
   317  }
   318  
   319  func (ghost *Ghost) loadProperties() {
   320  	if ghost.propFile == "" {
   321  		return
   322  	}
   323  
   324  	bytes, err := ioutil.ReadFile(ghost.propFile)
   325  	if err != nil {
   326  		log.Printf("loadProperties: %s\n", err)
   327  		return
   328  	}
   329  
   330  	if err := json.Unmarshal(bytes, &ghost.properties); err != nil {
   331  		log.Printf("loadProperties: %s\n", err)
   332  		return
   333  	}
   334  }
   335  
   336  func (ghost *Ghost) tlsEnabled(addr string) (bool, error) {
   337  	conn, err := net.DialTimeout("tcp", addr, connectTimeout)
   338  	if err != nil {
   339  		return false, err
   340  	}
   341  	defer conn.Close()
   342  
   343  	colonPos := strings.LastIndex(addr, ":")
   344  	tlsConn := tls.Client(conn, &tls.Config{
   345  		// Allow any certificate since we only want to check if server talks TLS.
   346  		InsecureSkipVerify: true,
   347  		MinVersion:         tls.VersionTLS12,
   348  		RootCAs:            nil,
   349  		ServerName:         addr[:colonPos],
   350  	})
   351  	defer tlsConn.Close()
   352  
   353  	handshakeTimeout := false
   354  
   355  	// Close the connection to stop handshake if it's taking too long.
   356  	go func() {
   357  		time.Sleep(connectTimeout)
   358  		conn.Close()
   359  		handshakeTimeout = true
   360  	}()
   361  
   362  	err = tlsConn.Handshake()
   363  	if err != nil || handshakeTimeout {
   364  		return false, nil
   365  	}
   366  	return true, nil
   367  }
   368  
   369  // Upgrade starts the upgrade sequence of the ghost instance.
   370  func (ghost *Ghost) Upgrade() error {
   371  	log.Println("Upgrade: initiating upgrade sequence...")
   372  
   373  	exePath, err := os.Executable()
   374  	if err != nil {
   375  		return errors.New("Upgrade: can not find executable path")
   376  	}
   377  
   378  	var buffer bytes.Buffer
   379  	var client http.Client
   380  
   381  	httpsEnabled, err := ghost.tlsEnabled(ghost.connectedAddr)
   382  	if err != nil {
   383  		return errors.New("Upgrade: failed to connect to Overlord HTTP server, " +
   384  			"abort")
   385  	}
   386  
   387  	if ghost.tls.Enabled && !httpsEnabled {
   388  		return errors.New("Upgrade: TLS enforced but found Overlord HTTP server " +
   389  			"without TLS enabled! Possible mis-configuration or DNS/IP spoofing " +
   390  			"detected, abort")
   391  	}
   392  
   393  	proto := "http"
   394  	if httpsEnabled {
   395  		proto = "https"
   396  	}
   397  	url := fmt.Sprintf("%s://%s/upgrade/ghost.%s", proto, ghost.connectedAddr,
   398  		GetPlatformString())
   399  
   400  	if httpsEnabled {
   401  		tr := &http.Transport{TLSClientConfig: ghost.tls.Config}
   402  		client = http.Client{Transport: tr, Timeout: httpRequestTimeout}
   403  	} else {
   404  		client = http.Client{Timeout: httpRequestTimeout}
   405  	}
   406  
   407  	// Download the sha1sum for ghost for verification
   408  	resp, err := client.Get(url + ".sha1")
   409  	if err != nil || resp.StatusCode != 200 {
   410  		return errors.New("Upgrade: failed to download sha1sum file, abort")
   411  	}
   412  	sha1sumBytes := make([]byte, 40)
   413  	resp.Body.Read(sha1sumBytes)
   414  	sha1sum := strings.Trim(string(sha1sumBytes), "\n ")
   415  	defer resp.Body.Close()
   416  
   417  	// Compare the current version of ghost, if sha1 is the same, skip upgrading
   418  	currentSha1sum, _ := GetFileSha1(exePath)
   419  
   420  	if currentSha1sum == sha1sum {
   421  		log.Println("Upgrade: ghost is already up-to-date, skipping upgrade")
   422  		return nil
   423  	}
   424  
   425  	// Download upgrade version of ghost
   426  	resp2, err := client.Get(url)
   427  	if err != nil || resp2.StatusCode != 200 {
   428  		return errors.New("Upgrade: failed to download upgrade, abort")
   429  	}
   430  	defer resp2.Body.Close()
   431  
   432  	_, err = buffer.ReadFrom(resp2.Body)
   433  	if err != nil {
   434  		return errors.New("Upgrade: failed to download upgrade, abort")
   435  	}
   436  
   437  	// Compare SHA1 sum
   438  	if sha1sum != fmt.Sprintf("%x", sha1.Sum(buffer.Bytes())) {
   439  		return errors.New("Upgrade: sha1sum mismatch, abort")
   440  	}
   441  
   442  	os.Remove(exePath)
   443  	exeFile, err := os.Create(exePath)
   444  	if err != nil {
   445  		return errors.New("Upgrade: can not open ghost executable for writing")
   446  	}
   447  	_, err = buffer.WriteTo(exeFile)
   448  	if err != nil {
   449  		return fmt.Errorf("Upgrade: %s", err)
   450  	}
   451  	exeFile.Close()
   452  
   453  	err = os.Chmod(exePath, 0755)
   454  	if err != nil {
   455  		return fmt.Errorf("Upgrade: %s", err)
   456  	}
   457  
   458  	log.Println("Upgrade: restarting ghost...")
   459  	os.Args[0] = exePath
   460  	err = syscall.Exec(exePath, os.Args, os.Environ())
   461  	if err != nil {
   462  		return fmt.Errorf("Upgrade: exec: %s", err)
   463  	}
   464  	return nil
   465  }
   466  
   467  func (ghost *Ghost) handleTerminalRequest(req *Request) error {
   468  	type RequestParams struct {
   469  		Sid       string `json:"sid"`
   470  		TtyDevice string `json:"tty_device"`
   471  	}
   472  
   473  	var params RequestParams
   474  	if err := json.Unmarshal(req.Params, &params); err != nil {
   475  		return err
   476  	}
   477  
   478  	go func() {
   479  		log.Printf("Received terminal command, Terminal agent %s spawned\n", params.Sid)
   480  		addrs := []string{ghost.connectedAddr}
   481  		// Terminal sessions are identified with session ID, thus we don't care
   482  		// machine ID and can make them random.
   483  		g := NewGhost(addrs, ghost.tls, ModeTerminal, RandomMID).SetSid(
   484  			params.Sid).SetTtyDevice(params.TtyDevice)
   485  		g.Start(false, false)
   486  	}()
   487  
   488  	res := NewResponse(req.Rid, Success, nil)
   489  	return ghost.SendResponse(res)
   490  }
   491  
   492  func (ghost *Ghost) handleShellRequest(req *Request) error {
   493  	type RequestParams struct {
   494  		Sid string `json:"sid"`
   495  		Cmd string `json:"command"`
   496  	}
   497  
   498  	var params RequestParams
   499  	if err := json.Unmarshal(req.Params, &params); err != nil {
   500  		return err
   501  	}
   502  
   503  	go func() {
   504  		log.Printf("Received shell command: %s, Shell agent %s spawned\n", params.Cmd, params.Sid)
   505  		addrs := []string{ghost.connectedAddr}
   506  		// Shell sessions are identified with session ID, thus we don't care
   507  		// machine ID and can make them random.
   508  		g := NewGhost(addrs, ghost.tls, ModeShell, RandomMID).SetSid(
   509  			params.Sid).SetShellCommand(params.Cmd)
   510  		g.Start(false, false)
   511  	}()
   512  
   513  	res := NewResponse(req.Rid, Success, nil)
   514  	return ghost.SendResponse(res)
   515  }
   516  
   517  func (ghost *Ghost) handleFileDownloadRequest(req *Request) error {
   518  	type RequestParams struct {
   519  		Sid      string `json:"sid"`
   520  		Filename string `json:"filename"`
   521  	}
   522  
   523  	var params RequestParams
   524  	if err := json.Unmarshal(req.Params, &params); err != nil {
   525  		return err
   526  	}
   527  
   528  	filename := params.Filename
   529  	if !strings.HasPrefix(filename, "/") {
   530  		home := os.Getenv("HOME")
   531  		if home == "" {
   532  			home = "/tmp"
   533  		}
   534  		filename = filepath.Join(home, filename)
   535  	}
   536  
   537  	f, err := os.Open(filename)
   538  	if err != nil {
   539  		res := NewResponse(req.Rid, err.Error(), nil)
   540  		return ghost.SendResponse(res)
   541  	}
   542  	f.Close()
   543  
   544  	go func() {
   545  		log.Printf("Received file_download command, File agent %s spawned\n", params.Sid)
   546  		addrs := []string{ghost.connectedAddr}
   547  		g := NewGhost(addrs, ghost.tls, ModeFile, RandomMID).SetSid(
   548  			params.Sid).SetFileOp("download", filename, 0)
   549  		g.Start(false, false)
   550  	}()
   551  
   552  	res := NewResponse(req.Rid, Success, nil)
   553  	return ghost.SendResponse(res)
   554  }
   555  
   556  func (ghost *Ghost) handleFileUploadRequest(req *Request) error {
   557  	type RequestParams struct {
   558  		Sid         string `json:"sid"`
   559  		TerminalSid string `json:"terminal_sid"`
   560  		Filename    string `json:"filename"`
   561  		Dest        string `json:"dest"`
   562  		Perm        int    `json:"perm"`
   563  		CheckOnly   bool   `json:"check_only"`
   564  	}
   565  
   566  	var params RequestParams
   567  	if err := json.Unmarshal(req.Params, &params); err != nil {
   568  		return err
   569  	}
   570  
   571  	targetDir := os.Getenv("HOME")
   572  	if targetDir == "" {
   573  		targetDir = "/tmp"
   574  	}
   575  
   576  	destPath := params.Dest
   577  	if destPath != "" {
   578  		if !filepath.IsAbs(destPath) {
   579  			destPath = filepath.Join(targetDir, destPath)
   580  		}
   581  
   582  		st, err := os.Stat(destPath)
   583  		if err == nil && st.Mode().IsDir() {
   584  			destPath = filepath.Join(destPath, params.Filename)
   585  		}
   586  	} else {
   587  		if params.TerminalSid != "" {
   588  			if pid, ok := ghost.terminalSid2Pid.Load(params.TerminalSid); ok {
   589  				cwd, err := GetProcessWorkingDirectory(pid.(int))
   590  				if err == nil {
   591  					targetDir = cwd
   592  				}
   593  			}
   594  		}
   595  		destPath = filepath.Join(targetDir, params.Filename)
   596  	}
   597  
   598  	os.MkdirAll(filepath.Dir(destPath), 0755)
   599  
   600  	f, err := os.Create(destPath)
   601  	if err != nil {
   602  		res := NewResponse(req.Rid, err.Error(), nil)
   603  		return ghost.SendResponse(res)
   604  	}
   605  	f.Close()
   606  
   607  	// If not check_only, spawn ModeFile mode ghost agent to handle upload
   608  	if !params.CheckOnly {
   609  		go func() {
   610  			log.Printf("Received file_upload command, File agent %s spawned\n",
   611  				params.Sid)
   612  			addrs := []string{ghost.connectedAddr}
   613  			g := NewGhost(addrs, ghost.tls, ModeFile, RandomMID).SetSid(
   614  				params.Sid).SetFileOp("upload", destPath, params.Perm)
   615  			g.Start(false, false)
   616  		}()
   617  	}
   618  
   619  	res := NewResponse(req.Rid, Success, nil)
   620  	return ghost.SendResponse(res)
   621  }
   622  
   623  func (ghost *Ghost) handleModeForwardRequest(req *Request) error {
   624  	type RequestParams struct {
   625  		Sid  string `json:"sid"`
   626  		Port int    `json:"port"`
   627  	}
   628  
   629  	var params RequestParams
   630  	if err := json.Unmarshal(req.Params, &params); err != nil {
   631  		return err
   632  	}
   633  
   634  	go func() {
   635  		log.Printf("Received forward command, ModeForward agent %s spawned\n", params.Sid)
   636  		addrs := []string{ghost.connectedAddr}
   637  		g := NewGhost(addrs, ghost.tls, ModeForward, RandomMID).SetSid(
   638  			params.Sid).SetModeForwardPort(params.Port)
   639  		g.Start(false, false)
   640  	}()
   641  
   642  	res := NewResponse(req.Rid, Success, nil)
   643  	return ghost.SendResponse(res)
   644  }
   645  
   646  // StartDownloadServer starts the download server.
   647  func (ghost *Ghost) StartDownloadServer() error {
   648  	log.Println("StartDownloadServer: started")
   649  
   650  	defer func() {
   651  		ghost.quit = true
   652  		ghost.Conn.Close()
   653  		log.Println("StartDownloadServer: terminated")
   654  	}()
   655  
   656  	file, err := os.Open(ghost.fileOp.Filename)
   657  	if err != nil {
   658  		return err
   659  	}
   660  	defer file.Close()
   661  
   662  	io.Copy(ghost.Conn, file)
   663  	return nil
   664  }
   665  
   666  // StartUploadServer starts the upload server.
   667  func (ghost *Ghost) StartUploadServer() error {
   668  	log.Println("StartUploadServer: started")
   669  
   670  	defer func() {
   671  		log.Println("StartUploadServer: terminated")
   672  	}()
   673  
   674  	filePath := ghost.fileOp.Filename
   675  	dirPath := filepath.Dir(filePath)
   676  	if _, err := os.Stat(dirPath); os.IsNotExist(err) {
   677  		os.MkdirAll(dirPath, 0755)
   678  	}
   679  
   680  	file, err := os.Create(filePath)
   681  	if err != nil {
   682  		return err
   683  	}
   684  	defer file.Close()
   685  
   686  	for {
   687  		buffer := <-ghost.uploadContext.Data
   688  		if buffer == nil {
   689  			break
   690  		}
   691  		file.Write(buffer)
   692  	}
   693  
   694  	if ghost.fileOp.Perm > 0 {
   695  		file.Chmod(os.FileMode(ghost.fileOp.Perm))
   696  	}
   697  
   698  	return nil
   699  }
   700  
   701  func (ghost *Ghost) handleRequest(req *Request) error {
   702  	var err error
   703  	switch req.Name {
   704  	case "upgrade":
   705  		err = ghost.Upgrade()
   706  	case "terminal":
   707  		err = ghost.handleTerminalRequest(req)
   708  	case "shell":
   709  		err = ghost.handleShellRequest(req)
   710  	case "file_download":
   711  		err = ghost.handleFileDownloadRequest(req)
   712  	case "clear_to_download":
   713  		err = ghost.StartDownloadServer()
   714  	case "file_upload":
   715  		err = ghost.handleFileUploadRequest(req)
   716  	case "forward":
   717  		err = ghost.handleModeForwardRequest(req)
   718  	default:
   719  		err = errors.New(`Received unregistered command "` + req.Name + `", ignoring`)
   720  	}
   721  	return err
   722  }
   723  
   724  func (ghost *Ghost) processRequests(reqs []*Request) error {
   725  	for _, req := range reqs {
   726  		if err := ghost.handleRequest(req); err != nil {
   727  			return err
   728  		}
   729  	}
   730  	return nil
   731  }
   732  
   733  // Ping sends a ping message to the overlord server.
   734  func (ghost *Ghost) Ping() error {
   735  	pingHandler := func(res *Response) error {
   736  		if res == nil {
   737  			ghost.reset = true
   738  			return errors.New("Ping timeout")
   739  		}
   740  		return nil
   741  	}
   742  	req := NewRequest("ping", nil)
   743  	req.SetTimeout(pingTimeout)
   744  	return ghost.SendRequest(req, pingHandler)
   745  }
   746  
   747  func (ghost *Ghost) handleTTYControl(tty *os.File, controlString string) error {
   748  	// Terminal Command for ghost
   749  	// Implements the Message interface.
   750  	type TerminalCommand struct {
   751  		Command string          `json:"command"`
   752  		Params  json.RawMessage `json:"params"`
   753  	}
   754  
   755  	// winsize stores the Height and Width of a terminal.
   756  	type winsize struct {
   757  		height uint16
   758  		width  uint16
   759  	}
   760  
   761  	var control TerminalCommand
   762  	err := json.Unmarshal([]byte(controlString), &control)
   763  	if err != nil {
   764  		log.Println("mal-formed JSON request, ignored")
   765  		return nil
   766  	}
   767  
   768  	command := control.Command
   769  	if command == "resize" {
   770  		var params []int
   771  		err := json.Unmarshal([]byte(control.Params), &params)
   772  		if err != nil || len(params) != 2 {
   773  			log.Println("mal-formed JSON request, ignored")
   774  			return nil
   775  		}
   776  		ws := &winsize{width: uint16(params[1]), height: uint16(params[0])}
   777  		syscall.Syscall(syscall.SYS_IOCTL, tty.Fd(),
   778  			uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(ws)))
   779  	} else {
   780  		return errors.New("Invalid request command " + command)
   781  	}
   782  	return nil
   783  }
   784  
   785  func (ghost *Ghost) getTTYName() (string, error) {
   786  	ttyName, err := os.Readlink(fmt.Sprintf("/proc/%d/fd/0", os.Getpid()))
   787  	if err != nil {
   788  		return "", err
   789  	}
   790  	return ttyName, nil
   791  }
   792  
   793  // SpawnTTYServer Spawns a TTY server and forward I/O to the TCP socket.
   794  func (ghost *Ghost) SpawnTTYServer(res *Response) error {
   795  	log.Println("SpawnTTYServer: started")
   796  
   797  	var tty *os.File
   798  	var err error
   799  	stopConn := make(chan struct{})
   800  
   801  	defer func() {
   802  		ghost.quit = true
   803  		if tty != nil {
   804  			tty.Close()
   805  		}
   806  		ghost.Conn.Close()
   807  		log.Println("SpawnTTYServer: terminated")
   808  	}()
   809  
   810  	if ghost.ttyDevice == "" {
   811  		// No TTY device specified, open a PTY (pseudo terminal) instead.
   812  		shell := os.Getenv("SHELL")
   813  		if shell == "" {
   814  			shell = defaultShell
   815  		}
   816  
   817  		home := os.Getenv("HOME")
   818  		if home == "" {
   819  			home = "/root"
   820  		}
   821  
   822  		// Add ghost executable to PATH
   823  		exePath, err := os.Executable()
   824  		if err == nil {
   825  			os.Setenv("PATH", fmt.Sprintf("%s:%s", filepath.Dir(exePath),
   826  				os.Getenv("PATH")))
   827  		}
   828  
   829  		os.Chdir(home)
   830  		cmd := exec.Command(shell)
   831  		tty, err = pty.Start(cmd)
   832  		if err != nil {
   833  			return errors.New(`SpawnTTYServer: Cannot start "` + shell + `", abort`)
   834  		}
   835  
   836  		defer func() {
   837  			cmd.Process.Kill()
   838  		}()
   839  
   840  		// Register the mapping of sid and ttyName
   841  		ttyName, err := termios.Ptsname(tty.Fd())
   842  		if err != nil {
   843  			return err
   844  		}
   845  
   846  		client, err := ghostRPCStubServer()
   847  
   848  		// Ghost could be launched without RPC server, ignore registration
   849  		if err == nil {
   850  			err = client.Call("rpc.RegisterTTY", []string{ghost.sid, ttyName},
   851  				&EmptyReply{})
   852  			if err != nil {
   853  				return err
   854  			}
   855  
   856  			err = client.Call("rpc.RegisterSession", []string{
   857  				ghost.sid, strconv.Itoa(cmd.Process.Pid)}, &EmptyReply{})
   858  			if err != nil {
   859  				return err
   860  			}
   861  		}
   862  
   863  		go func() {
   864  			io.Copy(ghost.Conn, tty)
   865  			cmd.Wait()
   866  			close(stopConn)
   867  		}()
   868  	} else {
   869  		// Open a TTY device
   870  		tty, err = os.OpenFile(ghost.ttyDevice, os.O_RDWR, 0)
   871  		if err != nil {
   872  			return err
   873  		}
   874  
   875  		var term syscall.Termios
   876  		err := termios.Tcgetattr(tty.Fd(), &term)
   877  		if err != nil {
   878  			return nil
   879  		}
   880  
   881  		termios.Cfmakeraw(&term)
   882  		term.Iflag &= (syscall.IXON | syscall.IXOFF)
   883  		term.Cflag |= syscall.CLOCAL
   884  		term.Ispeed = syscall.B115200
   885  		term.Ospeed = syscall.B115200
   886  
   887  		if err = termios.Tcsetattr(tty.Fd(), termios.TCSANOW, &term); err != nil {
   888  			return err
   889  		}
   890  
   891  		go func() {
   892  			io.Copy(ghost.Conn, tty)
   893  			close(stopConn)
   894  		}()
   895  	}
   896  
   897  	var controlBuffer bytes.Buffer
   898  	var writeBuffer bytes.Buffer
   899  	controlState := controlNone
   900  
   901  	processBuffer := func(buffer []byte) error {
   902  		writeBuffer.Reset()
   903  		for len(buffer) > 0 {
   904  			if controlState != controlNone {
   905  				index := bytes.IndexByte(buffer, controlEnd)
   906  				if index != -1 {
   907  					controlBuffer.Write(buffer[:index])
   908  					err := ghost.handleTTYControl(tty, controlBuffer.String())
   909  					controlState = controlNone
   910  					controlBuffer.Reset()
   911  					if err != nil {
   912  						return err
   913  					}
   914  					buffer = buffer[index+1:]
   915  				} else {
   916  					controlBuffer.Write(buffer)
   917  					buffer = buffer[0:0]
   918  				}
   919  			} else {
   920  				index := bytes.IndexByte(buffer, controlStart)
   921  				if index != -1 {
   922  					controlState = controlStart
   923  					writeBuffer.Write(buffer[:index])
   924  					buffer = buffer[index+1:]
   925  				} else {
   926  					writeBuffer.Write(buffer)
   927  					buffer = buffer[0:0]
   928  				}
   929  			}
   930  		}
   931  		if writeBuffer.Len() != 0 {
   932  			tty.Write(writeBuffer.Bytes())
   933  		}
   934  		return nil
   935  	}
   936  
   937  	if ghost.ReadBuffer != "" {
   938  		processBuffer([]byte(ghost.ReadBuffer))
   939  		ghost.ReadBuffer = ""
   940  	}
   941  
   942  	for {
   943  		select {
   944  		case buffer := <-ghost.readChan:
   945  			err := processBuffer(buffer)
   946  			if err != nil {
   947  				log.Println("SpawnTTYServer:", err)
   948  			}
   949  		case err := <-ghost.readErrChan:
   950  			if err == io.EOF {
   951  				log.Println("SpawnTTYServer: connection terminated")
   952  				return nil
   953  			}
   954  			return err
   955  		case <-stopConn:
   956  			return nil
   957  		}
   958  	}
   959  }
   960  
   961  // SpawnShellServer spawns a Shell server and forward input/output from/to the TCP socket.
   962  func (ghost *Ghost) SpawnShellServer(res *Response) error {
   963  	log.Println("SpawnShellServer: started")
   964  
   965  	var err error
   966  
   967  	defer func() {
   968  		ghost.quit = true
   969  		if err != nil {
   970  			ghost.Conn.Write([]byte(err.Error() + "\n"))
   971  		}
   972  		ghost.Conn.Close()
   973  		log.Println("SpawnShellServer: terminated")
   974  	}()
   975  
   976  	// Execute shell command from HOME directory
   977  	home := os.Getenv("HOME")
   978  	if home == "" {
   979  		home = "/tmp"
   980  	}
   981  	os.Chdir(home)
   982  
   983  	// Add ghost executable to PATH
   984  	exePath, err := os.Executable()
   985  	if err == nil {
   986  		os.Setenv("PATH", fmt.Sprintf("%s:%s", os.Getenv("PATH"),
   987  			filepath.Dir(exePath)))
   988  	}
   989  
   990  	cmd := exec.Command(defaultShell, "-c", ghost.shellCommand)
   991  	stdout, err := cmd.StdoutPipe()
   992  	if err != nil {
   993  		return err
   994  	}
   995  	stderr, err := cmd.StderrPipe()
   996  	if err != nil {
   997  		return err
   998  	}
   999  	stdin, err := cmd.StdinPipe()
  1000  	if err != nil {
  1001  		return err
  1002  	}
  1003  
  1004  	stopConn := make(chan struct{})
  1005  
  1006  	if ghost.ReadBuffer != "" {
  1007  		stdin.Write([]byte(ghost.ReadBuffer))
  1008  		ghost.ReadBuffer = ""
  1009  	}
  1010  
  1011  	go io.Copy(ghost.Conn, stdout)
  1012  	go func() {
  1013  		io.Copy(ghost.Conn, stderr)
  1014  		close(stopConn)
  1015  	}()
  1016  
  1017  	if err = cmd.Start(); err != nil {
  1018  		return err
  1019  	}
  1020  
  1021  	defer func() {
  1022  		time.Sleep(100 * time.Millisecond) // Wait for process to terminate
  1023  
  1024  		process := (*PollableProcess)(cmd.Process)
  1025  		_, err = process.Poll()
  1026  		// Check if the process is terminated. If not, send SIGlogcatTypeVT100 to the process,
  1027  		// then wait for 1 second.  Send another SIGKILL to make sure the process is
  1028  		// terminated.
  1029  		if err != nil {
  1030  			cmd.Process.Signal(syscall.SIGTERM)
  1031  			time.Sleep(time.Second)
  1032  			cmd.Process.Kill()
  1033  			cmd.Wait()
  1034  		}
  1035  	}()
  1036  
  1037  	for {
  1038  		select {
  1039  		case buf := <-ghost.readChan:
  1040  			if len(buf) >= len(stdinClosed)*2 {
  1041  				idx := bytes.Index(buf, []byte(stdinClosed+stdinClosed))
  1042  				if idx != -1 {
  1043  					stdin.Write(buf[:idx])
  1044  					stdin.Close()
  1045  					continue
  1046  				}
  1047  			}
  1048  			stdin.Write(buf)
  1049  		case err := <-ghost.readErrChan:
  1050  			if err == io.EOF {
  1051  				log.Println("SpawnShellServer: connection terminated")
  1052  				return nil
  1053  			}
  1054  			log.Printf("SpawnShellServer: %s\n", err)
  1055  			return err
  1056  		case <-stopConn:
  1057  			return nil
  1058  		}
  1059  	}
  1060  }
  1061  
  1062  // InitiatefileOperation initiates a file operation.
  1063  // The operation could either be 'download' or 'upload'
  1064  // This function starts handshake with overlord then execute download sequence.
  1065  func (ghost *Ghost) InitiatefileOperation(res *Response) error {
  1066  	if ghost.fileOp.Action == "download" {
  1067  		fi, err := os.Stat(ghost.fileOp.Filename)
  1068  		if err != nil {
  1069  			return err
  1070  		}
  1071  
  1072  		req := NewRequest("request_to_download", map[string]interface{}{
  1073  			"terminal_sid": ghost.terminalSid,
  1074  			"filename":     filepath.Base(ghost.fileOp.Filename),
  1075  			"size":         fi.Size(),
  1076  		})
  1077  
  1078  		return ghost.SendRequest(req, nil)
  1079  	} else if ghost.fileOp.Action == "upload" {
  1080  		ghost.uploadContext.Ready = true
  1081  		req := NewRequest("clear_to_upload", nil)
  1082  		req.SetTimeout(-1)
  1083  		err := ghost.SendRequest(req, nil)
  1084  		if err != nil {
  1085  			return err
  1086  		}
  1087  		go ghost.StartUploadServer()
  1088  		return nil
  1089  	}
  1090  	return errors.New("InitiatefileOperation: unknown file operation, ignored")
  1091  }
  1092  
  1093  // SpawnPortModeForwardServer spawns a port forwarding server and forward I/O to
  1094  // the TCP socket.
  1095  func (ghost *Ghost) SpawnPortModeForwardServer(res *Response) error {
  1096  	log.Println("SpawnPortModeForwardServer: started")
  1097  
  1098  	var err error
  1099  
  1100  	defer func() {
  1101  		ghost.quit = true
  1102  		if err != nil {
  1103  			ghost.Conn.Write([]byte(err.Error() + "\n"))
  1104  		}
  1105  		ghost.Conn.Close()
  1106  		log.Println("SpawnPortModeForwardServer: terminated")
  1107  	}()
  1108  
  1109  	conn, err := net.DialTimeout("tcp", fmt.Sprintf("127.0.0.1:%d", ghost.port),
  1110  		connectTimeout)
  1111  	if err != nil {
  1112  		return err
  1113  	}
  1114  	defer conn.Close()
  1115  
  1116  	stopConn := make(chan struct{})
  1117  
  1118  	if ghost.ReadBuffer != "" {
  1119  		conn.Write([]byte(ghost.ReadBuffer))
  1120  		ghost.ReadBuffer = ""
  1121  	}
  1122  
  1123  	go func() {
  1124  		io.Copy(ghost.Conn, conn)
  1125  		close(stopConn)
  1126  	}()
  1127  
  1128  	for {
  1129  		select {
  1130  		case buf := <-ghost.readChan:
  1131  			conn.Write(buf)
  1132  		case err := <-ghost.readErrChan:
  1133  			if err == io.EOF {
  1134  				log.Println("SpawnPortModeForwardServer: connection terminated")
  1135  				return nil
  1136  			}
  1137  			return err
  1138  		case <-stopConn:
  1139  			return nil
  1140  		}
  1141  	}
  1142  }
  1143  
  1144  // Register existent to Overlord.
  1145  func (ghost *Ghost) Register() error {
  1146  	for _, addr := range ghost.addrs {
  1147  		var (
  1148  			conn net.Conn
  1149  			err  error
  1150  		)
  1151  
  1152  		log.Printf("Trying %s ...\n", addr)
  1153  		ghost.Reset()
  1154  
  1155  		// Check if server has TLS enabled.
  1156  		// Only control channel needs to determine if TLS is enabled. Other mode
  1157  		// should use the tlsSettings passed in when it was spawned.
  1158  		if ghost.mode == ModeControl {
  1159  			var enabled bool
  1160  
  1161  			switch ghost.tlsMode {
  1162  			case TLSDetect:
  1163  				enabled, err = ghost.tlsEnabled(addr)
  1164  				if err != nil {
  1165  					continue
  1166  				}
  1167  			case TLSForceEnable:
  1168  				enabled = true
  1169  			case TLSForceDisable:
  1170  				enabled = false
  1171  			}
  1172  
  1173  			ghost.tls.SetEnabled(enabled)
  1174  		}
  1175  
  1176  		proto := "ws"
  1177  		if ghost.tls.Enabled {
  1178  			proto = "wss"
  1179  		}
  1180  		uri := fmt.Sprintf("%s://%s/connect", proto, addr)
  1181  
  1182  		dialer := websocket.DefaultDialer
  1183  
  1184  		if ghost.tls.Config != nil {
  1185  			dialer = &websocket.Dialer{
  1186  				Proxy:            http.ProxyFromEnvironment,
  1187  				HandshakeTimeout: 45 * time.Second,
  1188  				TLSClientConfig:  ghost.tls.Config,
  1189  			}
  1190  		}
  1191  
  1192  		wsConn, _, err := dialer.Dial(uri, http.Header{})
  1193  		if err != nil {
  1194  			log.Printf("error: %s\n", err)
  1195  			continue
  1196  		}
  1197  
  1198  		conn = wsConn.UnderlyingConn()
  1199  
  1200  		log.Println("Connection established, registering...")
  1201  
  1202  		ghost.Conn = conn
  1203  		req := NewRequest("register", map[string]interface{}{
  1204  			"mid":        ghost.mid,
  1205  			"sid":        ghost.sid,
  1206  			"mode":       ghost.mode,
  1207  			"properties": ghost.properties,
  1208  		})
  1209  
  1210  		registered := func(res *Response) error {
  1211  			if res == nil {
  1212  				ghost.reset = true
  1213  				return errors.New("Register request timeout")
  1214  			} else if res.Response != Success {
  1215  				log.Println("Register:", res.Response)
  1216  			} else {
  1217  				log.Printf("Registered with Overlord at %s", addr)
  1218  				ghost.connectedAddr = addr
  1219  				if err := ghost.Upgrade(); err != nil {
  1220  					log.Println(err)
  1221  				}
  1222  				ghost.pauseLanDisc = true
  1223  			}
  1224  			ghost.RegisterStatus = res.Response
  1225  			return nil
  1226  		}
  1227  
  1228  		var handler ResponseHandler
  1229  		switch ghost.mode {
  1230  		case ModeControl:
  1231  			handler = registered
  1232  		case ModeTerminal:
  1233  			handler = ghost.SpawnTTYServer
  1234  		case ModeShell:
  1235  			handler = ghost.SpawnShellServer
  1236  		case ModeFile:
  1237  			handler = ghost.InitiatefileOperation
  1238  		case ModeForward:
  1239  			handler = ghost.SpawnPortModeForwardServer
  1240  		}
  1241  		err = ghost.SendRequest(req, handler)
  1242  		return nil
  1243  	}
  1244  
  1245  	return errors.New("Cannot connect to any server")
  1246  }
  1247  
  1248  // InitiateDownload initiates a client-initiated download request.
  1249  func (ghost *Ghost) InitiateDownload(info downloadInfo) {
  1250  	go func() {
  1251  		addrs := []string{ghost.connectedAddr}
  1252  
  1253  		val, ok := ghost.ttyName2Sid.Load(info.Ttyname)
  1254  		if !ok {
  1255  			log.Printf("Failed to get SID")
  1256  			return
  1257  		}
  1258  
  1259  		g := NewGhost(addrs, ghost.tls, ModeFile, RandomMID).SetTerminalSid(
  1260  			val.(string)).SetFileOp("download", info.Filename, 0)
  1261  		g.Start(false, false)
  1262  	}()
  1263  }
  1264  
  1265  // Reset all states for a new connection.
  1266  func (ghost *Ghost) Reset() {
  1267  	ghost.ClearRequests()
  1268  	ghost.reset = false
  1269  	ghost.loadProperties()
  1270  	ghost.RegisterStatus = statusDisconnected
  1271  }
  1272  
  1273  // Listen is the main routine for listen to socket messages.
  1274  func (ghost *Ghost) Listen() error {
  1275  	readChan, readErrChan := ghost.SpawnReaderRoutine()
  1276  	pingTicker := time.NewTicker(time.Duration(pingInterval))
  1277  	reqTicker := time.NewTicker(time.Duration(timeoutCheckInterval))
  1278  
  1279  	ghost.readChan = readChan
  1280  	ghost.readErrChan = readErrChan
  1281  
  1282  	defer func() {
  1283  		ghost.StopConn()
  1284  		ghost.pauseLanDisc = false
  1285  	}()
  1286  
  1287  	for {
  1288  		select {
  1289  		case buffer := <-readChan:
  1290  			if ghost.uploadContext.Ready {
  1291  				if ghost.ReadBuffer != "" {
  1292  					// Write the leftover from previous ReadBuffer
  1293  					ghost.uploadContext.Data <- []byte(ghost.ReadBuffer)
  1294  					ghost.ReadBuffer = ""
  1295  				}
  1296  				ghost.uploadContext.Data <- buffer
  1297  				continue
  1298  			}
  1299  			reqs := ghost.ParseRequests(string(buffer), ghost.RegisterStatus != Success)
  1300  			if ghost.quit {
  1301  				return nil
  1302  			}
  1303  			if err := ghost.processRequests(reqs); err != nil {
  1304  				log.Println(err)
  1305  			}
  1306  		case err := <-readErrChan:
  1307  			if err == io.EOF {
  1308  				if ghost.uploadContext.Ready {
  1309  					ghost.uploadContext.Data <- nil
  1310  					ghost.quit = true
  1311  					return nil
  1312  				}
  1313  				return errors.New("Connection dropped")
  1314  			}
  1315  			return err
  1316  		case info := <-ghost.downloadQueue:
  1317  			ghost.InitiateDownload(info)
  1318  		case <-pingTicker.C:
  1319  			if ghost.mode == ModeControl {
  1320  				ghost.Ping()
  1321  			}
  1322  		case <-reqTicker.C:
  1323  			err := ghost.ScanForTimeoutRequests()
  1324  			if ghost.reset {
  1325  				if err == nil {
  1326  					err = errors.New("reset request")
  1327  				}
  1328  				return err
  1329  			}
  1330  		}
  1331  	}
  1332  }
  1333  
  1334  // RegisterTTY register the TTY to a session.
  1335  func (ghost *Ghost) RegisterTTY(sesssionID, ttyName string) {
  1336  	ghost.ttyName2Sid.Store(ttyName, sesssionID)
  1337  }
  1338  
  1339  // RegisterSession register the PID to a session.
  1340  func (ghost *Ghost) RegisterSession(sesssionID, pidStr string) {
  1341  	pid, err := strconv.Atoi(pidStr)
  1342  	if err != nil {
  1343  		panic(err)
  1344  	}
  1345  	ghost.terminalSid2Pid.Store(sesssionID, pid)
  1346  }
  1347  
  1348  // AddToDownloadQueue adds a downloadInfo to the download queue
  1349  func (ghost *Ghost) AddToDownloadQueue(ttyName, filename string) {
  1350  	ghost.downloadQueue <- downloadInfo{ttyName, filename}
  1351  }
  1352  
  1353  // StartLanDiscovery starts listening to LAN discovery message.
  1354  func (ghost *Ghost) StartLanDiscovery() {
  1355  	log.Println("LAN discovery: started")
  1356  	buf := make([]byte, bufferSize)
  1357  	conn, err := net.ListenPacket("udp", fmt.Sprintf(":%d", OverlordLDPort))
  1358  	if err != nil {
  1359  		log.Printf("LAN discovery: %s, abort\n", err)
  1360  		return
  1361  	}
  1362  
  1363  	defer func() {
  1364  		conn.Close()
  1365  		log.Println("LAN discovery: stopped")
  1366  	}()
  1367  
  1368  	for {
  1369  		conn.SetReadDeadline(time.Now().Add(readTimeout))
  1370  		n, remote, err := conn.ReadFrom(buf)
  1371  
  1372  		if ghost.pauseLanDisc {
  1373  			log.Println("LAN discovery: paused")
  1374  			ticker := time.NewTicker(readTimeout)
  1375  		waitLoop:
  1376  			for {
  1377  				select {
  1378  				case <-ticker.C:
  1379  					if !ghost.pauseLanDisc {
  1380  						break waitLoop
  1381  					}
  1382  				}
  1383  			}
  1384  			log.Println("LAN discovery: resumed")
  1385  			continue
  1386  		}
  1387  
  1388  		if err != nil {
  1389  			continue
  1390  		}
  1391  
  1392  		// LAN discovery packet format: "OVERLORD [host]:port"
  1393  		data := strings.Split(string(buf[:n]), " ")
  1394  		if data[0] != "OVERLORD" {
  1395  			continue
  1396  		}
  1397  
  1398  		overlordAddrParts := strings.Split(data[1], ":")
  1399  		remoteAddrParts := strings.Split(remote.String(), ":")
  1400  
  1401  		var remoteAddr string
  1402  		if strings.Trim(overlordAddrParts[0], " ") == "" {
  1403  			remoteAddr = remoteAddrParts[0] + ":" + overlordAddrParts[1]
  1404  		} else {
  1405  			remoteAddr = data[1]
  1406  		}
  1407  
  1408  		if !ghost.existsInAddr(remoteAddr) {
  1409  			log.Printf("LAN discovery: got overlord address %s", remoteAddr)
  1410  			ghost.addrs = append(ghost.addrs, remoteAddr)
  1411  		}
  1412  	}
  1413  }
  1414  
  1415  // ServeHTTP method for serving JSON-RPC over HTTP.
  1416  func (ghost *Ghost) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  1417  	var conn, _, err = w.(http.Hijacker).Hijack()
  1418  	if err != nil {
  1419  		log.Print("rpc hijacking ", req.RemoteAddr, ": ", err.Error())
  1420  		return
  1421  	}
  1422  	io.WriteString(conn, "HTTP/1.1 200\n")
  1423  	io.WriteString(conn, "Content-Type: application/json-rpc\n\n")
  1424  	ghost.server.ServeCodec(jsonrpc.NewServerCodec(conn))
  1425  }
  1426  
  1427  // StartRPCServer starts a local RPC server used for communication between
  1428  // ghost instances.
  1429  func (ghost *Ghost) StartRPCServer() {
  1430  	log.Println("RPC Server: started")
  1431  
  1432  	ghost.server = rpc.NewServer()
  1433  	ghost.server.RegisterName("rpc", &ghostRPCStub{ghost})
  1434  
  1435  	http.Handle("/", ghost)
  1436  	err := http.ListenAndServe(fmt.Sprintf("127.0.0.1:%d", ghostRPCStubPort), nil)
  1437  	if err != nil {
  1438  		log.Fatalf("Unable to listen at port %d: %s\n", ghostRPCStubPort, err)
  1439  	}
  1440  }
  1441  
  1442  // ScanGateway scans current network gateway and add it into addrs if not
  1443  // already exist.
  1444  func (ghost *Ghost) ScanGateway() {
  1445  	if gateways, err := GetGateWayIP(); err == nil {
  1446  		for _, gw := range gateways {
  1447  			addr := fmt.Sprintf("%s:%d", gw, DefaultHTTPSPort)
  1448  			if !ghost.existsInAddr(addr) {
  1449  				ghost.addrs = append(ghost.addrs, addr)
  1450  			}
  1451  			addr = fmt.Sprintf("%s:%d", gw, DefaultHTTPPort)
  1452  			if !ghost.existsInAddr(addr) {
  1453  				ghost.addrs = append(ghost.addrs, addr)
  1454  			}
  1455  		}
  1456  	}
  1457  }
  1458  
  1459  // Start bootstraps and start the client.
  1460  func (ghost *Ghost) Start(lanDisc bool, RPCServer bool) {
  1461  	log.Printf("%s started\n", ModeStr(ghost.mode))
  1462  	log.Printf("MID: %s\n", ghost.mid)
  1463  	log.Printf("SID: %s\n", ghost.sid)
  1464  
  1465  	if lanDisc {
  1466  		go ghost.StartLanDiscovery()
  1467  	}
  1468  
  1469  	if RPCServer {
  1470  		go ghost.StartRPCServer()
  1471  	}
  1472  
  1473  	for {
  1474  		ghost.ScanGateway()
  1475  		err := ghost.Register()
  1476  		if err == nil {
  1477  			err = ghost.Listen()
  1478  		}
  1479  		if ghost.quit {
  1480  			break
  1481  		}
  1482  		log.Printf("%s, retrying in %ds\n", err, retryIntervalSeconds)
  1483  		time.Sleep(retryIntervalSeconds * time.Second)
  1484  		ghost.Reset()
  1485  	}
  1486  }
  1487  
  1488  // Returns a ghostRPCStub client object which can be used to call ghostRPCStub methods.
  1489  func ghostRPCStubServer() (*rpc.Client, error) {
  1490  	conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", ghostRPCStubPort))
  1491  	if err != nil {
  1492  		return nil, err
  1493  	}
  1494  
  1495  	io.WriteString(conn, "GET / HTTP/1.1\nHost: 127.0.0.1\n\n")
  1496  	_, err = http.ReadResponse(bufio.NewReader(conn), nil)
  1497  	if err == nil {
  1498  		return jsonrpc.NewClient(conn), nil
  1499  	}
  1500  	return nil, err
  1501  }
  1502  
  1503  // DownloadFile adds a file to the download queue, which would be pickup by the
  1504  // ghost control channel instance and perform download.
  1505  func DownloadFile(filename string) {
  1506  	client, err := ghostRPCStubServer()
  1507  	if err != nil {
  1508  		log.Printf("error: %s\n", err)
  1509  		os.Exit(1)
  1510  	}
  1511  
  1512  	var ttyName string
  1513  	var f *os.File
  1514  
  1515  	absPath, err := filepath.Abs(filename)
  1516  	if err != nil {
  1517  		goto fail
  1518  	}
  1519  
  1520  	_, err = os.Stat(absPath)
  1521  	if err != nil {
  1522  		goto fail
  1523  	}
  1524  
  1525  	f, err = os.Open(absPath)
  1526  	if err != nil {
  1527  		goto fail
  1528  	}
  1529  	f.Close()
  1530  
  1531  	ttyName, err = Ttyname(os.Stdout.Fd())
  1532  	if err != nil {
  1533  		goto fail
  1534  	}
  1535  
  1536  	err = client.Call("rpc.AddToDownloadQueue", []string{ttyName, absPath},
  1537  		&EmptyReply{})
  1538  	if err != nil {
  1539  		goto fail
  1540  	}
  1541  	os.Exit(0)
  1542  
  1543  fail:
  1544  	log.Println(err)
  1545  	os.Exit(1)
  1546  }
  1547  
  1548  // StartGhost starts the Ghost client.
  1549  func StartGhost(args []string, mid string, noLanDisc bool, noRPCServer bool,
  1550  	tlsCertFile string, verify bool, propFile string, download string,
  1551  	reset bool, status bool, tlsMode int) {
  1552  	var addrs []string
  1553  
  1554  	if status {
  1555  		client, err := ghostRPCStubServer()
  1556  		if err != nil {
  1557  			log.Printf("error: %s\n", err)
  1558  			os.Exit(1)
  1559  		}
  1560  
  1561  		var reply string
  1562  		err = client.Call("rpc.GetStatus", &EmptyArgs{}, &reply)
  1563  		if err != nil {
  1564  			log.Printf("GetStatus: %s\n", err)
  1565  			os.Exit(1)
  1566  		}
  1567  		fmt.Println(reply)
  1568  		os.Exit(0)
  1569  	}
  1570  
  1571  	if reset {
  1572  		client, err := ghostRPCStubServer()
  1573  		if err != nil {
  1574  			log.Printf("error: %s\n", err)
  1575  			os.Exit(1)
  1576  		}
  1577  
  1578  		err = client.Call("rpc.Reconnect", &EmptyArgs{}, &EmptyReply{})
  1579  		if err != nil {
  1580  			log.Printf("Reset: %s\n", err)
  1581  			os.Exit(1)
  1582  		}
  1583  		os.Exit(0)
  1584  	}
  1585  
  1586  	if download != "" {
  1587  		DownloadFile(download)
  1588  	}
  1589  
  1590  	if len(args) >= 1 {
  1591  		if strings.Index(args[0], ":") == -1 {
  1592  			addrs = append(addrs,
  1593  				fmt.Sprintf("%s:%d", args[0], DefaultHTTPSPort),
  1594  				fmt.Sprintf("%s:%d", args[0], DefaultHTTPPort))
  1595  		} else {
  1596  			addrs = append(addrs, args[0])
  1597  		}
  1598  	}
  1599  	addrs = append(addrs,
  1600  		fmt.Sprintf("127.0.0.1:%d", DefaultHTTPSPort),
  1601  		fmt.Sprintf("127.0.0.1:%d", DefaultHTTPPort))
  1602  
  1603  	tlsSettings := newTLSSettings(tlsCertFile, verify)
  1604  
  1605  	if propFile != "" {
  1606  		var err error
  1607  		propFile, err = filepath.Abs(propFile)
  1608  		if err != nil {
  1609  			log.Println("propFile:", err)
  1610  			os.Exit(1)
  1611  		}
  1612  	}
  1613  
  1614  	g := NewGhost(addrs, tlsSettings, ModeControl, mid)
  1615  	g.SetPropFile(propFile).SetTLSMode(tlsMode)
  1616  	go g.Start(!noLanDisc, !noRPCServer)
  1617  
  1618  	ticker := time.NewTicker(time.Duration(60 * time.Second))
  1619  
  1620  	for {
  1621  		select {
  1622  		case <-ticker.C:
  1623  			log.Printf("Num of Goroutines: %d\n", runtime.NumGoroutine())
  1624  		}
  1625  	}
  1626  }