mirror of
https://github.com/bolucat/Archive.git
synced 2025-09-27 04:30:12 +08:00
119 lines
2.4 KiB
Go
119 lines
2.4 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
var pluginCmd *exec.Cmd
|
|
|
|
func startPlugin(plugin, pluginOpts, ssAddr string, isServer bool) (newAddr string, err error) {
|
|
logf("starting plugin (%s) with option (%s)....", plugin, pluginOpts)
|
|
freePort, err := getFreePort()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to fetch an unused port for plugin (%v)", err)
|
|
}
|
|
localHost := "127.0.0.1"
|
|
ssHost, ssPort, err := net.SplitHostPort(ssAddr)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
newAddr = localHost + ":" + freePort
|
|
if isServer {
|
|
if ssHost == "" {
|
|
ssHost = "0.0.0.0"
|
|
}
|
|
logf("plugin (%s) will listen on %s:%s", plugin, ssHost, ssPort)
|
|
} else {
|
|
logf("plugin (%s) will listen on %s:%s", plugin, localHost, freePort)
|
|
}
|
|
err = execPlugin(plugin, pluginOpts, ssHost, ssPort, localHost, freePort)
|
|
return
|
|
}
|
|
|
|
func killPlugin() {
|
|
if pluginCmd != nil {
|
|
pluginCmd.Process.Signal(syscall.SIGTERM)
|
|
waitCh := make(chan struct{})
|
|
go func() {
|
|
pluginCmd.Wait()
|
|
close(waitCh)
|
|
}()
|
|
timeout := time.After(3 * time.Second)
|
|
select {
|
|
case <-waitCh:
|
|
case <-timeout:
|
|
pluginCmd.Process.Kill()
|
|
}
|
|
}
|
|
}
|
|
|
|
func execPlugin(plugin, pluginOpts, remoteHost, remotePort, localHost, localPort string) (err error) {
|
|
pluginFile := plugin
|
|
if fileExists(plugin) {
|
|
if !filepath.IsAbs(plugin) {
|
|
pluginFile = "./" + plugin
|
|
}
|
|
} else {
|
|
pluginFile, err = exec.LookPath(plugin)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
logH := newLogHelper("[" + plugin + "]: ")
|
|
env := append(os.Environ(),
|
|
"SS_REMOTE_HOST="+remoteHost,
|
|
"SS_REMOTE_PORT="+remotePort,
|
|
"SS_LOCAL_HOST="+localHost,
|
|
"SS_LOCAL_PORT="+localPort,
|
|
"SS_PLUGIN_OPTIONS="+pluginOpts,
|
|
)
|
|
cmd := &exec.Cmd{
|
|
Path: pluginFile,
|
|
Env: env,
|
|
Stdout: logH,
|
|
Stderr: logH,
|
|
}
|
|
if err = cmd.Start(); err != nil {
|
|
return err
|
|
}
|
|
pluginCmd = cmd
|
|
go func() {
|
|
if err := cmd.Wait(); err != nil {
|
|
logf("plugin exited (%v)\n", err)
|
|
os.Exit(2)
|
|
}
|
|
logf("plugin exited\n")
|
|
os.Exit(0)
|
|
}()
|
|
return nil
|
|
}
|
|
|
|
func fileExists(filename string) bool {
|
|
info, err := os.Stat(filename)
|
|
if os.IsNotExist(err) {
|
|
return false
|
|
}
|
|
return !info.IsDir()
|
|
}
|
|
|
|
func getFreePort() (string, error) {
|
|
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
l, err := net.ListenTCP("tcp", addr)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
port := fmt.Sprintf("%d", l.Addr().(*net.TCPAddr).Port)
|
|
l.Close()
|
|
return port, nil
|
|
}
|