forked from bazil/bazil
/
cli.go
150 lines (131 loc) · 3.15 KB
/
cli.go
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package cli
import (
"flag"
"fmt"
"log"
"net/http"
"os"
"path/filepath"
"runtime/pprof"
"time"
"bazil.org/bazil/cliutil/flagx"
"bazil.org/bazil/cliutil/subcommands"
"bazil.org/bazil/defaults"
"bazil.org/bazil/util/httpunix"
"bazil.org/fuse"
"github.com/tv42/jog"
)
type bazil struct {
flag.FlagSet
Config struct {
Verbose bool
Debug bool
DataDir flagx.AbsPath
CPUProfile string
}
Control http.Client
}
var _ = Service(&bazil{})
func (b *bazil) Setup() (ok bool) {
if b.Config.Debug {
log := jog.New(nil)
fuse.Debug = log.Event
}
if b.Config.CPUProfile != "" {
f, err := os.Create(b.Config.CPUProfile)
if err != nil {
log.Printf("cpu profiling: %v", err)
return false
}
err = pprof.StartCPUProfile(f)
if err != nil {
log.Printf("cpu profiling: %v", err)
return false
}
}
u := &httpunix.HTTPUnixTransport{
DialTimeout: 100 * time.Millisecond,
RequestTimeout: 1 * time.Second,
ResponseHeaderTimeout: 1 * time.Second,
}
u.RegisterLocation("bazil", filepath.Join(b.Config.DataDir.String(), "control"))
b.Control = http.Client{
Transport: u,
}
return true
}
func (b *bazil) Teardown() (ok bool) {
if b.Config.CPUProfile != "" {
pprof.StopCPUProfile()
}
return true
}
// Bazil allows command-line callables access to global flags, such as
// verbosity.
var Bazil = bazil{}
func init() {
Bazil.BoolVar(&Bazil.Config.Verbose, "v", false, "verbose output")
Bazil.BoolVar(&Bazil.Config.Debug, "debug", false, "debug output")
Bazil.Config.DataDir = flagx.AbsPath(defaults.DataDir())
// ensure absolute path to make the control socket show up nicer
// in `ss` output
Bazil.Var(&Bazil.Config.DataDir, "data-dir", "path to filesystem state")
Bazil.StringVar(&Bazil.Config.CPUProfile, "cpuprofile", "", "write cpu profile to file")
subcommands.Register(&Bazil)
}
// Service is an interface that commands can implement to setup and
// teardown services for the subcommands below them.
//
// As Run and potential multiple Teardown failures makes having a
// single error return impossible, Setup and Teardown only get to
// signal a boolean success. Any detail should be exposed via log.
type Service interface {
Setup() (ok bool)
Teardown() (ok bool)
}
func run(result subcommands.Result) (ok bool) {
var cmd interface{}
for _, cmd = range result.ListCommands() {
if svc, isService := cmd.(Service); isService {
ok = svc.Setup()
if !ok {
return false
}
defer func() {
// Teardown failures can cause non-successful exit
if !svc.Teardown() {
ok = false
}
}()
}
}
run := cmd.(subcommands.Runner)
err := run.Run()
if err != nil {
log.Printf("error: %v", err)
return false
}
return true
}
// Main is primary entry point into the bazil command line
// application.
func Main() (exitstatus int) {
progName := filepath.Base(os.Args[0])
log.SetFlags(0)
log.SetPrefix(progName + ": ")
result, err := subcommands.Parse(&Bazil, progName, os.Args[1:])
if err == flag.ErrHelp {
result.Usage()
return 0
}
if err != nil {
fmt.Fprintf(os.Stderr, "%s: %v\n", result.Name(), err)
result.Usage()
return 2
}
ok := run(result)
if !ok {
return 1
}
return 0
}