package main import ( "flag" "fmt" "log" "os" "os/signal" "path" "strings" "sync" "syscall" "github.com/anmitsu/go-shlex" "gitlab.com/tslocum/sshtargate/pkg/gate" ) const ( version = "0.0.0" versionInfo = `sshtargate - Host SSH portals to applications - v` + version + ` https://gitlab.com/tslocum/sshtargate The MIT License (MIT) Copyright (c) 2020 Trevor Slocum ` ) var ( printVersionInfo bool configPath string portals []*gate.Portal portalsLock = new(sync.Mutex) done = make(chan bool) ) func main() { flag.BoolVar(&printVersionInfo, "version", false, "print version information and exit") flag.StringVar(&configPath, "config", "", "path to configuration file") flag.Parse() if printVersionInfo { fmt.Print(versionInfo) return } // TODO: Allow portals to be specified via arguments // TODO: Catch SIGHUP sigc := make(chan os.Signal, 1) signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM) go func() { <-sigc done <- true }() log.Println("Initializing sshtargate...") if configPath == "" { homedir, err := os.UserHomeDir() if err == nil && homedir != "" { configPath = path.Join(homedir, ".config", "sshtargate", "config.yaml") } } err := readConfig(configPath) if err != nil { log.Fatalf("failed to read configuration file %s: %s", configPath, err) } for pname, pcfg := range config.Portals { cs, err := shlex.Split(pcfg.Command, true) if err != nil { log.Fatalf("failed to split command %s", pcfg.Command) } pname, pcfg := pname, pcfg // Capture go func() { wg := new(sync.WaitGroup) for _, address := range pcfg.Host { wg.Add(1) address := address // Capture go func() { p, err := gate.NewPortal(pname, address, cs) if err != nil { log.Fatalf("failed to start portal %s on %s: %s", pname, address, err) } portalsLock.Lock() portals = append(portals, p) portalsLock.Unlock() wg.Done() }() } wg.Wait() log.Printf("Opened portal %s on %s to %s", pname, strings.Join(pcfg.Host, ","), pcfg.Command) }() } <-done portalsLock.Lock() for _, p := range portals { p.Shutdown() } portalsLock.Unlock() }