Host SSH portals to applications
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

120 lines
2.2 KiB

package main
import (
"flag"
"fmt"
"log"
"os"
"os/signal"
"strings"
"sync"
"syscall"
"code.rocketnine.space/tslocum/ez"
"code.rocketnine.space/tslocum/sshtargate/portal"
"github.com/anmitsu/go-shlex"
)
const (
version = "0.0.0"
versionInfo = `sshtargate - Host SSH portals to applications - v` + version + `
https://code.rocketnine.space/tslocum/sshtargate
The MIT License (MIT)
Copyright (c) 2020 Trevor Slocum <trevor@rocketnine.space>
`
)
var (
printVersionInfo bool
configPath string
portals []*portal.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
}
if configPath == "" {
var err error
configPath, err = ez.DefaultConfigPath("sshtargate")
if err != nil {
log.Fatal(err)
}
}
// 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...")
err := ez.Deserialize(config, configPath)
if err != nil {
log.Fatalf("failed to read configuration file: %s", err)
}
if len(config.Portals) == 0 {
log.Println("Warning: No portals are defined")
}
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 := portal.New(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()
}