...

Source file src/code.rocketnine.space/tslocum/sshtargate/main.go

Documentation: code.rocketnine.space/tslocum/sshtargate

     1  package main
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"log"
     7  	"os"
     8  	"os/signal"
     9  	"strings"
    10  	"sync"
    11  	"syscall"
    12  
    13  	"code.rocketnine.space/tslocum/ez"
    14  	"code.rocketnine.space/tslocum/sshtargate/portal"
    15  	"github.com/anmitsu/go-shlex"
    16  )
    17  
    18  const (
    19  	version = "0.0.0"
    20  
    21  	versionInfo = `sshtargate - Host SSH portals to applications - v` + version + `
    22  https://code.rocketnine.space/tslocum/sshtargate
    23  The MIT License (MIT)
    24  Copyright (c) 2020 Trevor Slocum <trevor@rocketnine.space>
    25  `
    26  )
    27  
    28  var (
    29  	printVersionInfo bool
    30  	configPath       string
    31  
    32  	portals     []*portal.Portal
    33  	portalsLock = new(sync.Mutex)
    34  
    35  	done = make(chan bool)
    36  )
    37  
    38  func main() {
    39  	flag.BoolVar(&printVersionInfo, "version", false, "print version information and exit")
    40  	flag.StringVar(&configPath, "config", "", "path to configuration file")
    41  	flag.Parse()
    42  
    43  	if printVersionInfo {
    44  		fmt.Print(versionInfo)
    45  		return
    46  	}
    47  
    48  	if configPath == "" {
    49  		var err error
    50  		configPath, err = ez.DefaultConfigPath("sshtargate")
    51  		if err != nil {
    52  			log.Fatal(err)
    53  		}
    54  	}
    55  
    56  	// TODO: Allow portals to be specified via arguments
    57  
    58  	// TODO: Catch SIGHUP
    59  	sigc := make(chan os.Signal, 1)
    60  	signal.Notify(sigc,
    61  		syscall.SIGINT,
    62  		syscall.SIGTERM)
    63  	go func() {
    64  		<-sigc
    65  
    66  		done <- true
    67  	}()
    68  
    69  	log.Println("Initializing sshtargate...")
    70  
    71  	err := ez.Deserialize(config, configPath)
    72  	if err != nil {
    73  		log.Fatalf("failed to read configuration file: %s", err)
    74  	}
    75  
    76  	if len(config.Portals) == 0 {
    77  		log.Println("Warning: No portals are defined")
    78  	}
    79  
    80  	for pname, pcfg := range config.Portals {
    81  		cs, err := shlex.Split(pcfg.Command, true)
    82  		if err != nil {
    83  			log.Fatalf("failed to split command %s", pcfg.Command)
    84  		}
    85  
    86  		pname, pcfg := pname, pcfg // Capture
    87  		go func() {
    88  			wg := new(sync.WaitGroup)
    89  
    90  			for _, address := range pcfg.Host {
    91  				wg.Add(1)
    92  				address := address // Capture
    93  
    94  				go func() {
    95  					p, err := portal.New(pname, address, cs)
    96  					if err != nil {
    97  						log.Fatalf("failed to start portal %s on %s: %s", pname, address, err)
    98  					}
    99  
   100  					portalsLock.Lock()
   101  					portals = append(portals, p)
   102  					portalsLock.Unlock()
   103  
   104  					wg.Done()
   105  				}()
   106  			}
   107  
   108  			wg.Wait()
   109  			log.Printf("Opened portal %s on %s to %s", pname, strings.Join(pcfg.Host, ","), pcfg.Command)
   110  		}()
   111  	}
   112  
   113  	<-done
   114  
   115  	portalsLock.Lock()
   116  	for _, p := range portals {
   117  		p.Shutdown()
   118  	}
   119  	portalsLock.Unlock()
   120  }
   121  

View as plain text