summaryrefslogtreecommitdiff
path: root/cmd/dserver/main.go
blob: 936edceb41c9ede0de8194df094ea52c7eaa1943 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package main

import (
	"context"
	"flag"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"github.com/mimecast/dtail/internal/cli"
	"github.com/mimecast/dtail/internal/config"
	"github.com/mimecast/dtail/internal/io/dlog"
	"github.com/mimecast/dtail/internal/server"
	"github.com/mimecast/dtail/internal/source"
	"github.com/mimecast/dtail/internal/user"
	"github.com/mimecast/dtail/internal/version"
)

// The evil begins here.
func main() {
	var args config.Args
	var color bool
	var displayVersion bool
	var pprof string
	var shutdownAfter int

	user.NoRootCheck()

	flag.BoolVar(&color, "color", false, "Enable ANSII terminal colors")
	flag.BoolVar(&displayVersion, "version", false, "Display version")
	flag.IntVar(&args.SSHPort, "port", config.DefaultSSHPort, "SSH server port")
	flag.IntVar(&shutdownAfter, "shutdownAfter", 0, "Shutdown after so many seconds")
	flag.StringVar(&args.ConfigFile, "cfg", "", "Config file path")
	flag.StringVar(&args.LogDir, "logDir", "", "Log dir")
	flag.StringVar(&args.LogLevel, "logLevel", config.DefaultLogLevel, "Log level")
	flag.StringVar(&args.Logger, "logger", config.DefaultServerLogger, "Logger name")
	flag.StringVar(&args.SSHBindAddress, "bindAddress", "", "The SSH bind address")
	flag.StringVar(&pprof, "pprof", "", "Start PProf server this address")

	flag.Parse()
	args.NoColor = !color
	config.Setup(source.Server, &args, flag.Args())

	if displayVersion {
		runtimeCfg := config.CurrentRuntime()
		version.PrintAndExit(runtimeCfg.Client != nil && runtimeCfg.Client.TermColorsEnable)
	}
	version.Print(false)

	// rootCtx is always cancelled on exit to ensure the internal goroutine
	// spawned by context.WithCancel is released. When -shutdownAfter is set,
	// ctx is replaced by a child WithTimeout context whose own cancel is also
	// deferred, preventing the lostcancel leak flagged by go vet.
	rootCtx, rootCancel := context.WithCancel(context.Background())
	defer rootCancel()

	ctx := rootCtx
	cancel := context.CancelFunc(rootCancel)

	if shutdownAfter > 0 {
		// Override ctx with a timeout-bounded child; defer its cancel so the
		// timeout goroutine is always cleaned up regardless of code path.
		var timeoutCancel context.CancelFunc
		ctx, timeoutCancel = context.WithTimeout(rootCtx, time.Duration(shutdownAfter)*time.Second)
		defer timeoutCancel()
		// Callers that invoke cancel() (e.g. the signal handler and post-serve
		// cleanup) should trigger the timeout cancel so the server shuts down
		// promptly even before the deadline fires.
		cancel = timeoutCancel
	}

	sigCh := make(chan os.Signal, 10)
	signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)

	go func() {
		select {
		case <-sigCh:
			cancel()
		case <-ctx.Done():
		}
	}()

	var wg sync.WaitGroup
	wg.Add(1)
	dlog.Start(ctx, &wg, source.Server)

	var pprofServer *cli.PProfServer
	if pprof != "" {
		// Enable mutex and block profiling so the /debug/pprof/mutex and
		// /debug/pprof/block endpoints actually contain samples. These rates
		// are gated on --pprof so they cost nothing when profiling is off.
		cli.EnableProfilingRates()

		// Assign to the outer pprofServer with '=' (declaring pprofErr
		// separately) so it is NOT shadowed: the graceful Shutdown below relies
		// on the outer var being non-nil to actually stop the pprof server.
		var pprofErr error
		pprofServer, pprofErr = cli.NewPProfServer(pprof)
		if pprofErr != nil {
			dlog.Client.Error("Unable to start PProf", pprofErr)
		} else {
			dlog.Client.Info("Starting PProf", pprofServer.Address())
			pprofServer.Start(nil)
		}
	}

	serv := server.New(config.CurrentRuntime())
	status := serv.Start(ctx)
	cancel()
	if pprofServer != nil {
		shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
		if err := pprofServer.Shutdown(shutdownCtx); err != nil {
			dlog.Client.Error("Unable to stop PProf", err)
		}
		shutdownCancel()
	}

	wg.Wait()
	os.Exit(status)
}