...

Source file src/codeberg.org/tslocum/sshtargate/main.go

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

View as plain text