@ -1,4 +0,0 @@ | |||
```bash | |||
# Maybe in a screen session: | |||
cat cmd/barak/seed | ./build/barak | |||
``` |
@ -1,254 +0,0 @@ | |||
package main | |||
import ( | |||
"errors" | |||
"fmt" | |||
"io/ioutil" | |||
"net" | |||
"net/http" | |||
"os" | |||
"sync" | |||
"time" | |||
. "github.com/tendermint/go-common" | |||
pcm "github.com/tendermint/go-process" | |||
"github.com/tendermint/go-wire" | |||
. "github.com/tendermint/tendermint/cmd/barak/types" | |||
"github.com/tendermint/tendermint/rpc/server" | |||
) | |||
type BarakOptions struct { | |||
Validators []Validator | |||
ListenAddress string | |||
StartNonce int64 | |||
Registries []string | |||
} | |||
// Read options from a file, or stdin if optionsFile is "" | |||
func ReadBarakOptions(optFile string) *BarakOptions { | |||
var optBytes []byte | |||
var err error | |||
if optFile != "" { | |||
optBytes, err = ioutil.ReadFile(optFile) | |||
} else { | |||
optBytes, err = ioutil.ReadAll(os.Stdin) | |||
} | |||
if err != nil { | |||
panic(Fmt("Error reading input: %v", err)) | |||
} | |||
opt := wire.ReadJSON(&BarakOptions{}, optBytes, &err).(*BarakOptions) | |||
if err != nil { | |||
panic(Fmt("Error parsing input: %v", err)) | |||
} | |||
return opt | |||
} | |||
func ensureRootDir() (rootDir string) { | |||
rootDir = os.Getenv("BRKROOT") | |||
if rootDir == "" { | |||
rootDir = os.Getenv("HOME") + "/.barak" | |||
} | |||
err := EnsureDir(rootDir) | |||
if err != nil { | |||
panic(Fmt("Error creating barak rootDir: %v", err)) | |||
} | |||
return | |||
} | |||
func NewBarakFromOptions(opt *BarakOptions) *Barak { | |||
rootDir := ensureRootDir() | |||
barak := NewBarak(rootDir, opt.StartNonce, opt.Validators) | |||
for _, registry := range opt.Registries { | |||
barak.AddRegistry(registry) | |||
} | |||
barak.OpenListener(opt.ListenAddress) | |||
// Debug. | |||
fmt.Printf("Options: %v\n", opt) | |||
fmt.Printf("Barak: %v\n", barak) | |||
return barak | |||
} | |||
//-------------------------------------------------------------------------------- | |||
type Barak struct { | |||
mtx sync.Mutex | |||
pid int | |||
nonce int64 | |||
processes map[string]*pcm.Process | |||
validators []Validator | |||
listeners []net.Listener | |||
rootDir string | |||
registries []string | |||
} | |||
func NewBarak(rootDir string, nonce int64, validators []Validator) *Barak { | |||
return &Barak{ | |||
pid: os.Getpid(), | |||
nonce: nonce, | |||
processes: make(map[string]*pcm.Process), | |||
validators: validators, | |||
listeners: nil, | |||
rootDir: rootDir, | |||
registries: nil, | |||
} | |||
} | |||
func (brk *Barak) RootDir() string { | |||
brk.mtx.Lock() | |||
defer brk.mtx.Unlock() | |||
return brk.rootDir | |||
} | |||
func (brk *Barak) ListProcesses() []*pcm.Process { | |||
brk.mtx.Lock() | |||
defer brk.mtx.Unlock() | |||
processes := []*pcm.Process{} | |||
for _, process := range brk.processes { | |||
processes = append(processes, process) | |||
} | |||
return processes | |||
} | |||
func (brk *Barak) GetProcess(label string) *pcm.Process { | |||
brk.mtx.Lock() | |||
defer brk.mtx.Unlock() | |||
return brk.processes[label] | |||
} | |||
func (brk *Barak) AddProcess(label string, process *pcm.Process) error { | |||
brk.mtx.Lock() | |||
defer brk.mtx.Unlock() | |||
existing := brk.processes[label] | |||
if existing != nil && existing.EndTime.IsZero() { | |||
return fmt.Errorf("Process already exists: %v", label) | |||
} | |||
brk.processes[label] = process | |||
return nil | |||
} | |||
func (brk *Barak) StopProcess(label string, kill bool) error { | |||
brk.mtx.Lock() | |||
proc := brk.processes[label] | |||
brk.mtx.Unlock() | |||
if proc == nil { | |||
return fmt.Errorf("Process does not exist: %v", label) | |||
} | |||
err := proc.StopProcess(kill) | |||
return err | |||
} | |||
func (brk *Barak) ListValidators() []Validator { | |||
brk.mtx.Lock() | |||
defer brk.mtx.Unlock() | |||
return brk.validators | |||
} | |||
func (brk *Barak) ListListeners() []net.Listener { | |||
brk.mtx.Lock() | |||
defer brk.mtx.Unlock() | |||
return brk.listeners | |||
} | |||
func (brk *Barak) OpenListener(addr string) (net.Listener, error) { | |||
brk.mtx.Lock() | |||
defer brk.mtx.Unlock() | |||
// Start rpc server. | |||
mux := http.NewServeMux() | |||
mux.HandleFunc("/download", ServeFileHandler) | |||
mux.HandleFunc("/register", RegisterHandler) | |||
// TODO: mux.HandleFunc("/upload", UploadFile) | |||
rpcserver.RegisterRPCFuncs(mux, Routes) | |||
listener, err := rpcserver.StartHTTPServer(addr, mux) | |||
if err != nil { | |||
return nil, err | |||
} | |||
brk.listeners = append(brk.listeners, listener) | |||
return listener, nil | |||
} | |||
func (brk *Barak) CloseListener(addr string) { | |||
brk.mtx.Lock() | |||
defer brk.mtx.Unlock() | |||
filtered := []net.Listener{} | |||
for _, listener := range brk.listeners { | |||
if listener.Addr().String() == addr { | |||
err := listener.Close() | |||
if err != nil { | |||
fmt.Printf("Error closing listener: %v\n", err) | |||
} | |||
continue | |||
} | |||
filtered = append(filtered, listener) | |||
} | |||
brk.listeners = filtered | |||
} | |||
func (brk *Barak) GetRegistries() []string { | |||
brk.mtx.Lock() | |||
defer brk.mtx.Unlock() | |||
return brk.registries | |||
} | |||
func (brk *Barak) AddRegistry(registry string) { | |||
brk.mtx.Lock() | |||
defer brk.mtx.Unlock() | |||
brk.registries = append(brk.registries, registry) | |||
} | |||
func (brk *Barak) RemoveRegistry(registry string) { | |||
brk.mtx.Lock() | |||
defer brk.mtx.Unlock() | |||
filtered := []string{} | |||
for _, reg := range brk.registries { | |||
if registry == reg { | |||
continue | |||
} | |||
filtered = append(filtered, reg) | |||
} | |||
brk.registries = filtered | |||
} | |||
func (brk *Barak) StartRegisterRoutine() { | |||
// Register this barak with central listener | |||
go func() { | |||
// Workaround around issues when registries register on themselves upon startup. | |||
time.Sleep(3 * time.Second) | |||
for { | |||
// Every hour, register with the registries. | |||
for _, registry := range brk.registries { | |||
resp, err := http.Get(registry + "/register") | |||
if err != nil { | |||
fmt.Printf("Error registering to registry %v:\n %v\n", registry, err) | |||
} else if resp.StatusCode != 200 { | |||
body, _ := ioutil.ReadAll(resp.Body) | |||
fmt.Printf("Error registering to registry %v:\n %v\n", registry, string(body)) | |||
} else { | |||
body, _ := ioutil.ReadAll(resp.Body) | |||
fmt.Printf("Successfully registered with registry %v\n %v\n", registry, string(body)) | |||
} | |||
} | |||
time.Sleep(1 * time.Hour) | |||
} | |||
}() | |||
} | |||
// Write pid to file. | |||
func (brk *Barak) WritePidFile() { | |||
err := WriteFileAtomic(brk.rootDir+"/pidfile", []byte(Fmt("%v", brk.pid))) | |||
if err != nil { | |||
panic(Fmt("Error writing pidfile: %v", err)) | |||
} | |||
} | |||
func (brk *Barak) CheckIncrNonce(newNonce int64) error { | |||
brk.mtx.Lock() | |||
defer brk.mtx.Unlock() | |||
if brk.nonce+1 != newNonce { | |||
return errors.New("Replay error") | |||
} | |||
brk.nonce += 1 | |||
return nil | |||
} |
@ -1,7 +0,0 @@ | |||
package main | |||
import ( | |||
"github.com/tendermint/go-logger" | |||
) | |||
var log = logger.New("module", "main") |
@ -1,291 +0,0 @@ | |||
package main | |||
// A note on the origin of the name. | |||
// http://en.wikipedia.org/wiki/Barak | |||
// TODO: Nonrepudiable command log | |||
import ( | |||
"bytes" | |||
"errors" | |||
"flag" | |||
"fmt" | |||
"io" | |||
"io/ioutil" | |||
"net/http" | |||
"os" | |||
"reflect" | |||
"time" | |||
. "github.com/tendermint/go-common" | |||
cfg "github.com/tendermint/go-config" | |||
pcm "github.com/tendermint/go-process" | |||
"github.com/tendermint/go-wire" | |||
. "github.com/tendermint/tendermint/cmd/barak/types" | |||
"github.com/tendermint/tendermint/rpc/server" | |||
) | |||
const BarakVersion = "0.0.1" | |||
var Routes map[string]*rpcserver.RPCFunc | |||
func init() { | |||
Routes = map[string]*rpcserver.RPCFunc{ | |||
"status": rpcserver.NewRPCFunc(Status, []string{}), | |||
"run": rpcserver.NewRPCFunc(Run, []string{"auth_command"}), | |||
// NOTE: also, two special non-JSONRPC routes called "download" and "upload" | |||
} | |||
} | |||
// Global instance | |||
var barak_ *Barak | |||
// Parse command-line options | |||
func parseFlags() (optionsFile string) { | |||
flag.StringVar(&optionsFile, "config", "", "Read config from file instead of stdin") | |||
flag.Parse() | |||
return | |||
} | |||
func main() { | |||
fmt.Printf("New Barak Process (PID: %d)\n", os.Getpid()) | |||
// Apply bare tendermint/* configuration. | |||
config := cfg.NewMapConfig(nil) | |||
config.Set("log_level", "info") | |||
cfg.ApplyConfig(config) | |||
// Read options | |||
optionsFile := parseFlags() | |||
options := ReadBarakOptions(optionsFile) | |||
// Init barak | |||
barak_ = NewBarakFromOptions(options) | |||
barak_.StartRegisterRoutine() | |||
barak_.WritePidFile() // This should be last, before TrapSignal(). | |||
TrapSignal(func() { | |||
fmt.Println("Barak shutting down") | |||
}) | |||
} | |||
//------------------------------------------------------------------------------ | |||
// RPC functions | |||
func Status() (*ResponseStatus, error) { | |||
barak_.mtx.Lock() | |||
pid := barak_.pid | |||
nonce := barak_.nonce | |||
validators := barak_.validators | |||
barak_.mtx.Unlock() | |||
return &ResponseStatus{ | |||
Version: BarakVersion, | |||
Pid: pid, | |||
Nonce: nonce, | |||
Validators: validators, | |||
}, nil | |||
} | |||
func Run(authCommand AuthCommand) (interface{}, error) { | |||
command, err := parseValidateCommand(authCommand) | |||
if err != nil { | |||
return nil, err | |||
} | |||
log.Notice(Fmt("Run() received command %v:%v", reflect.TypeOf(command), command)) | |||
// Issue command | |||
switch c := command.(type) { | |||
case CommandStartProcess: | |||
return StartProcess(c.Wait, c.Label, c.ExecPath, c.Args, c.Input) | |||
case CommandStopProcess: | |||
return StopProcess(c.Label, c.Kill) | |||
case CommandListProcesses: | |||
return ListProcesses() | |||
case CommandOpenListener: | |||
return OpenListener(c.Addr) | |||
case CommandCloseListener: | |||
return CloseListener(c.Addr) | |||
case CommandQuit: | |||
return Quit() | |||
default: | |||
return nil, errors.New("Invalid endpoint for command") | |||
} | |||
} | |||
func parseValidateCommandStr(authCommandStr string) (Command, error) { | |||
var err error | |||
authCommand := wire.ReadJSON(AuthCommand{}, []byte(authCommandStr), &err).(AuthCommand) | |||
if err != nil { | |||
fmt.Printf("Failed to parse auth_command") | |||
return nil, errors.New("AuthCommand parse error") | |||
} | |||
return parseValidateCommand(authCommand) | |||
} | |||
func parseValidateCommand(authCommand AuthCommand) (Command, error) { | |||
commandJSONStr := authCommand.CommandJSONStr | |||
signatures := authCommand.Signatures | |||
// Validate commandJSONStr | |||
if !validate([]byte(commandJSONStr), barak_.ListValidators(), signatures) { | |||
fmt.Printf("Failed validation attempt") | |||
return nil, errors.New("Validation error") | |||
} | |||
// Parse command | |||
var err error | |||
command := wire.ReadJSON(NoncedCommand{}, []byte(commandJSONStr), &err).(NoncedCommand) | |||
if err != nil { | |||
fmt.Printf("Failed to parse command") | |||
return nil, errors.New("Command parse error") | |||
} | |||
// Prevent replays | |||
barak_.CheckIncrNonce(command.Nonce) | |||
return command.Command, nil | |||
} | |||
//------------------------------------------------------------------------------ | |||
// RPC base commands | |||
// WARNING Not validated, do not export to routes. | |||
func StartProcess(wait bool, label string, execPath string, args []string, input string) (*ResponseStartProcess, error) { | |||
// First, see if there already is a process labeled 'label' | |||
existing := barak_.GetProcess(label) | |||
if existing != nil && existing.EndTime.IsZero() { | |||
return nil, fmt.Errorf("Process already exists: %v", label) | |||
} | |||
// Otherwise, create one. | |||
err := EnsureDir(barak_.RootDir() + "/outputs") | |||
if err != nil { | |||
return nil, fmt.Errorf("Failed to create outputs dir: %v", err) | |||
} | |||
inFile := bytes.NewReader([]byte(input)) | |||
outPath := Fmt("%v/outputs/%v_%v.out", barak_.RootDir(), label, time.Now().Format("2006_01_02_15_04_05_MST")) | |||
outFile, err := OpenAutoFile(outPath) | |||
if err != nil { | |||
return nil, err | |||
} | |||
proc, err := pcm.StartProcess(label, execPath, args, inFile, outFile) | |||
if err != nil { | |||
return nil, err | |||
} | |||
barak_.AddProcess(label, proc) | |||
if wait { | |||
<-proc.WaitCh | |||
// read output from outPath | |||
outputBytes, err := ioutil.ReadFile(outPath) | |||
if err != nil { | |||
fmt.Sprintf("ERROR READING OUTPUT: %v", err) | |||
} | |||
output := string(outputBytes) | |||
// fmt.Println("Read output", output) | |||
if proc.ExitState == nil { | |||
return &ResponseStartProcess{ | |||
Success: true, | |||
Output: output, | |||
}, nil | |||
} else { | |||
return &ResponseStartProcess{ | |||
Success: proc.ExitState.Success(), // Would be always false? | |||
Output: output, | |||
}, nil | |||
} | |||
} else { | |||
return &ResponseStartProcess{ | |||
Success: true, | |||
Output: "", | |||
}, nil | |||
} | |||
} | |||
func StopProcess(label string, kill bool) (*ResponseStopProcess, error) { | |||
err := barak_.StopProcess(label, kill) | |||
return &ResponseStopProcess{}, err | |||
} | |||
func ListProcesses() (*ResponseListProcesses, error) { | |||
procs := barak_.ListProcesses() | |||
return &ResponseListProcesses{ | |||
Processes: procs, | |||
}, nil | |||
} | |||
func OpenListener(addr string) (*ResponseOpenListener, error) { | |||
listener, err := barak_.OpenListener(addr) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return &ResponseOpenListener{ | |||
Addr: listener.Addr().String(), | |||
}, nil | |||
} | |||
func CloseListener(addr string) (*ResponseCloseListener, error) { | |||
barak_.CloseListener(addr) | |||
return &ResponseCloseListener{}, nil | |||
} | |||
func Quit() (*ResponseQuit, error) { | |||
fmt.Println("Barak shutting down due to Quit()") | |||
go func() { | |||
time.Sleep(time.Second) | |||
os.Exit(0) | |||
}() | |||
return &ResponseQuit{}, nil | |||
} | |||
//-------------------------------------------------------------------------------- | |||
// Another barak instance registering its external | |||
// address to a remote barak. | |||
func RegisterHandler(w http.ResponseWriter, req *http.Request) { | |||
registry, err := os.OpenFile(barak_.RootDir()+"/registry.log", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600) | |||
if err != nil { | |||
http.Error(w, "Could not open registry file. Please contact the administrator", 500) | |||
return | |||
} | |||
// TODO: Also check the X-FORWARDED-FOR or whatever it's called. | |||
registry.Write([]byte(Fmt("++ %v\n", req.RemoteAddr))) | |||
registry.Close() | |||
w.Header().Set("Content-Type", "application/json") | |||
w.WriteHeader(200) | |||
w.Write([]byte("Noted!")) | |||
} | |||
func ServeFileHandler(w http.ResponseWriter, req *http.Request) { | |||
authCommandStr := req.FormValue("auth_command") | |||
command, err := parseValidateCommandStr(authCommandStr) | |||
if err != nil { | |||
http.Error(w, Fmt("Invalid command: %v", err), 400) | |||
} | |||
serveCommand, ok := command.(CommandServeFile) | |||
if !ok { | |||
http.Error(w, "Invalid command", 400) | |||
} | |||
path := serveCommand.Path | |||
if path == "" { | |||
http.Error(w, "Must specify path", 400) | |||
return | |||
} | |||
if path[0] == '.' { | |||
// local paths must be explicitly local, e.g. "./xyz" | |||
} else if path[0] != '/' { | |||
// If not an absolute path, then is label | |||
proc := barak_.GetProcess(path) | |||
if proc == nil { | |||
http.Error(w, Fmt("Unknown process label: %v", path), 400) | |||
return | |||
} | |||
path = proc.OutputFile.(*os.File).Name() | |||
} | |||
file, err := os.Open(path) | |||
if err != nil { | |||
http.Error(w, Fmt("Error opening file: %v. %v", path, err), 400) | |||
return | |||
} | |||
_, err = io.Copy(w, file) | |||
if err != nil { | |||
fmt.Fprintf(os.Stderr, Fmt("Error serving file: %v. %v", path, err)) | |||
return | |||
} | |||
} |
@ -1,12 +0,0 @@ | |||
{ | |||
"ListenAddress": "0.0.0.0:46660", | |||
"Validators": [ | |||
{ | |||
"VotingPower": 1, | |||
"PubKey": [1,"0A78709AF1FBB1118879E1C756834654E082A7C8240433FB9B22AB04710431A4"] | |||
} | |||
], | |||
"Registries": [ | |||
"http://navytoad.chaintest.net:46660" | |||
] | |||
} |
@ -1,12 +0,0 @@ | |||
{ | |||
"ListenAddress": "0.0.0.0:46660", | |||
"Validators": [ | |||
{ | |||
"VotingPower": 1, | |||
"PubKey": [1,"e56663353d01c58a1d4cdb4d14b70c2e3335be1ebb6c3f697af7882c03837962"] | |||
} | |||
], | |||
"Registries": [ | |||
"http://navytoad.chaintest.net:46660" | |||
] | |||
} |
@ -1,70 +0,0 @@ | |||
package types | |||
import ( | |||
"github.com/tendermint/go-crypto" | |||
"github.com/tendermint/go-wire" | |||
) | |||
type AuthCommand struct { | |||
CommandJSONStr string | |||
Signatures []crypto.Signature | |||
} | |||
type NoncedCommand struct { | |||
Nonce int64 | |||
Command | |||
} | |||
type Command interface{} | |||
const ( | |||
commandTypeStartProcess = 0x01 | |||
commandTypeStopProcess = 0x02 | |||
commandTypeListProcesses = 0x03 | |||
commandTypeServeFile = 0x04 | |||
commandTypeOpenListener = 0x05 | |||
commandTypeCloseListener = 0x06 | |||
commandTypeQuit = 0x07 | |||
) | |||
// for wire.readReflect | |||
var _ = wire.RegisterInterface( | |||
struct{ Command }{}, | |||
wire.ConcreteType{CommandStartProcess{}, commandTypeStartProcess}, | |||
wire.ConcreteType{CommandStopProcess{}, commandTypeStopProcess}, | |||
wire.ConcreteType{CommandListProcesses{}, commandTypeListProcesses}, | |||
wire.ConcreteType{CommandServeFile{}, commandTypeServeFile}, | |||
wire.ConcreteType{CommandOpenListener{}, commandTypeOpenListener}, | |||
wire.ConcreteType{CommandCloseListener{}, commandTypeCloseListener}, | |||
wire.ConcreteType{CommandQuit{}, commandTypeQuit}, | |||
) | |||
type CommandStartProcess struct { | |||
Wait bool | |||
Label string | |||
ExecPath string | |||
Args []string | |||
Input string | |||
} | |||
type CommandStopProcess struct { | |||
Label string | |||
Kill bool | |||
} | |||
type CommandListProcesses struct{} | |||
type CommandServeFile struct { | |||
Path string | |||
} | |||
type CommandOpenListener struct { | |||
Addr string | |||
} | |||
type CommandCloseListener struct { | |||
Addr string | |||
} | |||
type CommandQuit struct{} |
@ -1,37 +0,0 @@ | |||
package types | |||
import ( | |||
pcm "github.com/tendermint/go-process" | |||
) | |||
type ResponseStatus struct { | |||
Version string | |||
Pid int | |||
Nonce int64 | |||
Validators []Validator | |||
} | |||
type ResponseRegister struct { | |||
} | |||
type ResponseStartProcess struct { | |||
Success bool | |||
Output string | |||
} | |||
type ResponseStopProcess struct { | |||
} | |||
type ResponseListProcesses struct { | |||
Processes []*pcm.Process | |||
} | |||
type ResponseOpenListener struct { | |||
Addr string | |||
} | |||
type ResponseCloseListener struct { | |||
} | |||
type ResponseQuit struct { | |||
} |
@ -1,10 +0,0 @@ | |||
package types | |||
import ( | |||
"github.com/tendermint/go-crypto" | |||
) | |||
type Validator struct { | |||
VotingPower int64 | |||
PubKey crypto.PubKey | |||
} |
@ -1,56 +0,0 @@ | |||
package main | |||
import ( | |||
"github.com/tendermint/go-crypto" | |||
. "github.com/tendermint/tendermint/cmd/barak/types" | |||
) | |||
func validate(signBytes []byte, validators []Validator, signatures []crypto.Signature) bool { | |||
var signedPower int64 | |||
var totalPower int64 | |||
for i, val := range validators { | |||
if val.PubKey.VerifyBytes(signBytes, signatures[i]) { | |||
signedPower += val.VotingPower | |||
totalPower += val.VotingPower | |||
} else { | |||
totalPower += val.VotingPower | |||
} | |||
} | |||
return signedPower > totalPower*2/3 | |||
} | |||
/* | |||
NOTE: Not used, just here in case we want it later. | |||
func ValidateHandler(handler http.Handler, validators []Validator) http.Handler { | |||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |||
sigStrs := r.Header[http.CanonicalHeaderKey("signatures")] | |||
log.Info("Woot", "sigstrs", sigStrs, "len", len(sigStrs)) | |||
// from https://medium.com/@xoen/golang-read-from-an-io-readwriter-without-loosing-its-content-2c6911805361 | |||
// Read the content | |||
var bodyBytes []byte | |||
if r.Body != nil { | |||
bodyBytes, _ = ioutil.ReadAll(r.Body) | |||
} | |||
// Restore the io.ReadCloser to its original state | |||
r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes)) | |||
// Get body string | |||
bodyString := string(bodyBytes) | |||
// Also read the path+"?"+query | |||
pathQuery := fmt.Sprintf("%v?%v", r.URL.Path, r.URL.RawQuery) | |||
// Concatenate into tuple of two strings. | |||
tuple := struct { | |||
Body string | |||
Path string | |||
}{bodyString, pathQuery} | |||
// Get sign bytes | |||
signBytes := wire.BinaryBytes(tuple) | |||
// Validate the sign bytes. | |||
//if validate(signBytes, validators, | |||
log.Info("Should sign", "bytes", signBytes) | |||
// If validation fails | |||
// XXX | |||
// If validation passes | |||
handler.ServeHTTP(w, r) | |||
}) | |||
} | |||
*/ |
@ -1,13 +0,0 @@ | |||
### Example | |||
```bash | |||
# Upgrade barak. | |||
# We need to give it a new seed to prevent port conflicts. | |||
./build/debora run --input "`cat cmd/barak/seed2`" -- barak2 ./build/barak | |||
# Build tendermint from source | |||
./build/debora run -- build_tendermint bash -c "cd $GOPATH/src/github.com/tendermint/tendermint; make" | |||
# Build and run tendermint | |||
./build/debora run -- tendermint bash -c "cd \$GOPATH/src/github.com/tendermint/tendermint; make; ./build/tendermint node" | |||
``` |
@ -1,148 +0,0 @@ | |||
package main | |||
import ( | |||
"fmt" | |||
"io" | |||
"net/url" | |||
"os" | |||
"github.com/tendermint/go-crypto" | |||
"github.com/tendermint/go-wire" | |||
btypes "github.com/tendermint/tendermint/cmd/barak/types" | |||
. "github.com/tendermint/go-common" | |||
"github.com/tendermint/tendermint/rpc/client" | |||
"net/http" | |||
) | |||
// These are convenience functions for a single developer. | |||
// When multiple are involved, the workflow is different. | |||
// (First the command(s) are signed by all validators, | |||
// and then it is broadcast). | |||
// TODO: Implement a reasonable workflow with multiple validators. | |||
func StartProcess(privKey crypto.PrivKey, remote string, command btypes.CommandStartProcess) (response btypes.ResponseStartProcess, err error) { | |||
nonce, err := GetNonce(remote) | |||
if err != nil { | |||
return response, err | |||
} | |||
commandBytes, signature := SignCommand(privKey, nonce+1, command) | |||
_, err = RunAuthCommand(remote, commandBytes, []crypto.Signature{signature}, &response) | |||
return response, err | |||
} | |||
func StopProcess(privKey crypto.PrivKey, remote string, command btypes.CommandStopProcess) (response btypes.ResponseStopProcess, err error) { | |||
nonce, err := GetNonce(remote) | |||
if err != nil { | |||
return response, err | |||
} | |||
commandBytes, signature := SignCommand(privKey, nonce+1, command) | |||
_, err = RunAuthCommand(remote, commandBytes, []crypto.Signature{signature}, &response) | |||
return response, err | |||
} | |||
func ListProcesses(privKey crypto.PrivKey, remote string, command btypes.CommandListProcesses) (response btypes.ResponseListProcesses, err error) { | |||
nonce, err := GetNonce(remote) | |||
if err != nil { | |||
return response, err | |||
} | |||
commandBytes, signature := SignCommand(privKey, nonce+1, command) | |||
_, err = RunAuthCommand(remote, commandBytes, []crypto.Signature{signature}, &response) | |||
return response, err | |||
} | |||
func OpenListener(privKey crypto.PrivKey, remote string, command btypes.CommandOpenListener) (response btypes.ResponseOpenListener, err error) { | |||
nonce, err := GetNonce(remote) | |||
if err != nil { | |||
return response, err | |||
} | |||
commandBytes, signature := SignCommand(privKey, nonce+1, command) | |||
_, err = RunAuthCommand(remote, commandBytes, []crypto.Signature{signature}, &response) | |||
return response, err | |||
} | |||
func CloseListener(privKey crypto.PrivKey, remote string, command btypes.CommandCloseListener) (response btypes.ResponseCloseListener, err error) { | |||
nonce, err := GetNonce(remote) | |||
if err != nil { | |||
return response, err | |||
} | |||
commandBytes, signature := SignCommand(privKey, nonce+1, command) | |||
_, err = RunAuthCommand(remote, commandBytes, []crypto.Signature{signature}, &response) | |||
return response, err | |||
} | |||
func DownloadFile(privKey crypto.PrivKey, remote string, command btypes.CommandServeFile, outPath string) (n int64, err error) { | |||
// Create authCommandJSONBytes | |||
nonce, err := GetNonce(remote) | |||
if err != nil { | |||
return 0, err | |||
} | |||
commandBytes, signature := SignCommand(privKey, nonce+1, command) | |||
authCommand := btypes.AuthCommand{ | |||
CommandJSONStr: string(commandBytes), | |||
Signatures: []crypto.Signature{signature}, | |||
} | |||
authCommandJSONBytes := wire.JSONBytes(authCommand) | |||
// Make request and write to outPath. | |||
httpResponse, err := http.PostForm(remote+"/download", url.Values{"auth_command": {string(authCommandJSONBytes)}}) | |||
if err != nil { | |||
return 0, err | |||
} | |||
defer httpResponse.Body.Close() | |||
outFile, err := os.OpenFile(outPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600) | |||
if err != nil { | |||
return 0, err | |||
} | |||
defer outFile.Close() | |||
n, err = io.Copy(outFile, httpResponse.Body) | |||
if err != nil { | |||
return 0, err | |||
} | |||
return n, nil | |||
} | |||
func Quit(privKey crypto.PrivKey, remote string, command btypes.CommandQuit) (response btypes.ResponseQuit, err error) { | |||
nonce, err := GetNonce(remote) | |||
if err != nil { | |||
return response, err | |||
} | |||
commandBytes, signature := SignCommand(privKey, nonce+1, command) | |||
_, err = RunAuthCommand(remote, commandBytes, []crypto.Signature{signature}, &response) | |||
return response, err | |||
} | |||
//----------------------------------------------------------------------------- | |||
// Utility method to get nonce from the remote. | |||
// The next command should include the returned nonce+1 as nonce. | |||
func GetNonce(remote string) (int64, error) { | |||
response, err := GetStatus(remote) | |||
return response.Nonce, err | |||
} | |||
func GetStatus(remote string) (response btypes.ResponseStatus, err error) { | |||
_, err = rpcclient.Call(remote, "status", Arr(), &response) | |||
if err != nil { | |||
return response, fmt.Errorf("Error fetching nonce from remote %v:\n %v", remote, err) | |||
} | |||
return response, nil | |||
} | |||
// Each developer runs this | |||
func SignCommand(privKey crypto.PrivKey, nonce int64, command btypes.Command) ([]byte, crypto.Signature) { | |||
noncedCommand := btypes.NoncedCommand{ | |||
Nonce: nonce, | |||
Command: command, | |||
} | |||
commandJSONBytes := wire.JSONBytes(noncedCommand) | |||
signature := privKey.Sign(commandJSONBytes) | |||
return commandJSONBytes, signature | |||
} | |||
// Somebody aggregates the signatures and calls this. | |||
func RunAuthCommand(remote string, commandJSONBytes []byte, signatures []crypto.Signature, dest interface{}) (interface{}, error) { | |||
authCommand := btypes.AuthCommand{ | |||
CommandJSONStr: string(commandJSONBytes), | |||
Signatures: signatures, | |||
} | |||
return rpcclient.Call(remote, "run", Arr(authCommand), dest) | |||
} |
@ -1,12 +0,0 @@ | |||
{ | |||
"Remotes": [ | |||
"http://navytoad.chaintest.net:46662", | |||
"http://whiteferret.chaintest.net:46662", | |||
"http://magentagriffin.chaintest.net:46662", | |||
"http://greensalamander.chaintest.net:46662", | |||
"http://blackshadow.chaintest.net:46662", | |||
"http://pinkpenguin.chaintest.net:46662", | |||
"http://polkapig.chaintest.net:46662" | |||
], | |||
"PrivKey": [1, "YOURPRIVKEY"] <-- XXX | |||
} |
@ -1,7 +0,0 @@ | |||
package main | |||
import ( | |||
"github.com/tendermint/go-logger" | |||
) | |||
var log = logger.New("module", "main") |
@ -1,397 +0,0 @@ | |||
package main | |||
import ( | |||
"fmt" | |||
"github.com/codegangsta/cli" | |||
"io/ioutil" | |||
"net/url" | |||
"os" | |||
"regexp" | |||
"strings" | |||
"sync" | |||
"github.com/tendermint/go-crypto" | |||
btypes "github.com/tendermint/tendermint/cmd/barak/types" | |||
. "github.com/tendermint/go-common" | |||
cfg "github.com/tendermint/go-config" | |||
"github.com/tendermint/go-wire" | |||
) | |||
func remoteNick(remote string) string { | |||
u, err := url.Parse(remote) | |||
if err != nil { | |||
return regexp.MustCompile(`[[:^alnum:]]`).ReplaceAllString(remote, "_") | |||
} else { | |||
return regexp.MustCompile(`[[:^alnum:]]`).ReplaceAllString(u.Host, "_") | |||
} | |||
} | |||
var Config = struct { | |||
Remotes []string | |||
PrivKey crypto.PrivKey | |||
}{} | |||
func main() { | |||
fmt.Printf("New Debora Process (PID: %d)\n", os.Getpid()) | |||
// Apply bare tendermint/* configuration. | |||
config := cfg.NewMapConfig(nil) | |||
config.Set("log_level", "notice") | |||
cfg.ApplyConfig(config) | |||
rootDir := os.Getenv("DEBROOT") | |||
if rootDir == "" { | |||
rootDir = os.Getenv("HOME") + "/.debora" | |||
} | |||
var ( | |||
groupFlag = cli.StringFlag{ | |||
Name: "group", | |||
Value: "default", | |||
Usage: "uses ~/.debora/<group>.cfg", | |||
} | |||
labelFlag = cli.StringFlag{ | |||
Name: "label", | |||
Value: "_", | |||
Usage: "label of the process, or _ by default", | |||
} | |||
bgFlag = cli.BoolFlag{ | |||
Name: "bg", | |||
Usage: "if set, runs as a background daemon", | |||
} | |||
inputFlag = cli.StringFlag{ | |||
Name: "input", | |||
Value: "", | |||
Usage: "input to the program (e.g. stdin)", | |||
} | |||
) | |||
app := cli.NewApp() | |||
app.Name = "debora" | |||
app.Usage = "summons commands to barak" | |||
app.Version = "0.0.1" | |||
app.Email = "ethan@erisindustries.com,jae@tendermint.com" | |||
app.Flags = []cli.Flag{ | |||
groupFlag, | |||
} | |||
app.Before = func(c *cli.Context) error { | |||
configFile := rootDir + "/" + c.String("group") + ".cfg" | |||
fmt.Printf("Using configuration from %v\n", configFile) | |||
ReadConfig(configFile) | |||
return nil | |||
} | |||
app.Commands = []cli.Command{ | |||
cli.Command{ | |||
Name: "status", | |||
Usage: "shows remote status", | |||
Action: cliGetStatus, | |||
}, | |||
cli.Command{ | |||
Name: "run", | |||
Usage: "run process", | |||
Action: cliStartProcess, | |||
Flags: []cli.Flag{ | |||
labelFlag, | |||
bgFlag, | |||
inputFlag, | |||
}, | |||
}, | |||
cli.Command{ | |||
Name: "stop", | |||
Usage: "stop process", | |||
Action: cliStopProcess, | |||
}, | |||
cli.Command{ | |||
Name: "list", | |||
Usage: "list processes", | |||
Action: cliListProcesses, | |||
}, | |||
cli.Command{ | |||
Name: "open", | |||
Usage: "open barak listener", | |||
Action: cliOpenListener, | |||
}, | |||
cli.Command{ | |||
Name: "close", | |||
Usage: "close barka listener", | |||
Action: cliCloseListener, | |||
}, | |||
cli.Command{ | |||
Name: "download", | |||
Usage: "download file <remote-path> <local-path-prefix>", | |||
Action: cliDownloadFile, | |||
}, | |||
cli.Command{ | |||
Name: "quit", | |||
Usage: "quit barak", | |||
Action: cliQuit, | |||
}, | |||
} | |||
app.Run(os.Args) | |||
} | |||
func ReadConfig(configFilePath string) { | |||
configJSONBytes, err := ioutil.ReadFile(configFilePath) | |||
if err != nil { | |||
Exit(Fmt("Failed to read config file %v. %v\n", configFilePath, err)) | |||
} | |||
wire.ReadJSON(&Config, configJSONBytes, &err) | |||
if err != nil { | |||
Exit(Fmt("Failed to parse config. %v", err)) | |||
} | |||
} | |||
func cliGetStatus(c *cli.Context) { | |||
args := c.Args() | |||
if len(args) != 0 { | |||
fmt.Println("BTW, status takes no arguments.") | |||
} | |||
wg := sync.WaitGroup{} | |||
failed := 0 | |||
for _, remote := range Config.Remotes { | |||
wg.Add(1) | |||
go func(remote string) { | |||
defer wg.Done() | |||
response, err := GetStatus(remote) | |||
if err != nil { | |||
failed++ | |||
fmt.Printf("%v failure. %v\n", remote, err) | |||
} else { | |||
fmt.Printf("%v success. %v\n", remote, response) | |||
} | |||
}(remote) | |||
} | |||
wg.Wait() | |||
if 0 < failed { | |||
os.Exit(1) | |||
} | |||
} | |||
func cliStartProcess(c *cli.Context) { | |||
args := c.Args() | |||
if len(args) < 1 { | |||
Exit("Must specify <execPath> <args...>") | |||
} | |||
execPath := args[0] | |||
args = args[1:] | |||
command := btypes.CommandStartProcess{ | |||
Wait: !c.Bool("bg"), | |||
Label: c.String("label"), | |||
ExecPath: execPath, | |||
Args: args, | |||
Input: c.String("input"), | |||
} | |||
wg := sync.WaitGroup{} | |||
failed := 0 | |||
for _, remote := range Config.Remotes { | |||
wg.Add(1) | |||
go func(remote string) { | |||
defer wg.Done() | |||
response, err := StartProcess(Config.PrivKey, remote, command) | |||
if err != nil { | |||
failed++ | |||
fmt.Printf("%v failure. %v\n", remote, err) | |||
} else { | |||
fmt.Printf("%v success.\n", remote) | |||
if response.Output != "" { | |||
fmt.Println("--------------------------------------------------------------------------------") | |||
fmt.Println(response.Output) | |||
fmt.Println("--------------------------------------------------------------------------------") | |||
} else { | |||
fmt.Println("(no output)") | |||
} | |||
} | |||
}(remote) | |||
} | |||
wg.Wait() | |||
if 0 < failed { | |||
os.Exit(1) | |||
} | |||
} | |||
func cliStopProcess(c *cli.Context) { | |||
args := c.Args() | |||
if len(args) == 0 { | |||
Exit("Must specify label to stop") | |||
} | |||
label := args[0] | |||
command := btypes.CommandStopProcess{ | |||
Label: label, | |||
Kill: true, | |||
} | |||
wg := sync.WaitGroup{} | |||
failed := 0 | |||
for _, remote := range Config.Remotes { | |||
wg.Add(1) | |||
go func(remote string) { | |||
defer wg.Done() | |||
response, err := StopProcess(Config.PrivKey, remote, command) | |||
if err != nil { | |||
failed++ | |||
fmt.Printf("%v failure. %v\n", remote, err) | |||
} else { | |||
fmt.Printf("%v success. %v\n", remote, response) | |||
} | |||
}(remote) | |||
} | |||
wg.Wait() | |||
if 0 < failed { | |||
os.Exit(1) | |||
} | |||
} | |||
func cliListProcesses(c *cli.Context) { | |||
/* | |||
args := c.Args() | |||
if len(args) == 0 { | |||
log.Fatal("Must specify application name") | |||
} | |||
app := args[0] | |||
*/ | |||
command := btypes.CommandListProcesses{} | |||
wg := sync.WaitGroup{} | |||
failed := 0 | |||
for _, remote := range Config.Remotes { | |||
wg.Add(1) | |||
go func(remote string) { | |||
defer wg.Done() | |||
response, err := ListProcesses(Config.PrivKey, remote, command) | |||
if err != nil { | |||
failed++ | |||
fmt.Printf("%v failure. %v\n", Blue(remote), Red(err)) | |||
} else { | |||
fmt.Printf("%v processes:\n", Blue(remote)) | |||
for _, proc := range response.Processes { | |||
fmt.Printf(" \"%v\" => `%v %v` (%v)\n", Yellow(proc.Label), proc.ExecPath, strings.Join(proc.Args, ","), proc.Pid) | |||
fmt.Printf(" started at %v", proc.StartTime.String()) | |||
if proc.EndTime.IsZero() { | |||
fmt.Printf(", running still\n") | |||
} else { | |||
endTimeStr := proc.EndTime.String() | |||
fmt.Printf(", stopped at %v\n", Yellow(endTimeStr)) | |||
} | |||
} | |||
} | |||
}(remote) | |||
} | |||
wg.Wait() | |||
if 0 < failed { | |||
os.Exit(1) | |||
} | |||
} | |||
func cliOpenListener(c *cli.Context) { | |||
args := c.Args() | |||
if len(args) < 1 { | |||
Exit("Must specify <listenAddr e.g. [::]:46661>") | |||
} | |||
listenAddr := args[0] | |||
command := btypes.CommandOpenListener{ | |||
Addr: listenAddr, | |||
} | |||
wg := sync.WaitGroup{} | |||
failed := 0 | |||
for _, remote := range Config.Remotes { | |||
wg.Add(1) | |||
go func(remote string) { | |||
defer wg.Done() | |||
response, err := OpenListener(Config.PrivKey, remote, command) | |||
if err != nil { | |||
failed++ | |||
fmt.Printf("%v failure. %v\n", remote, err) | |||
} else { | |||
fmt.Printf("%v opened %v.\n", remote, response.Addr) | |||
} | |||
}(remote) | |||
} | |||
wg.Wait() | |||
if 0 < failed { | |||
os.Exit(1) | |||
} | |||
} | |||
func cliCloseListener(c *cli.Context) { | |||
args := c.Args() | |||
if len(args) == 0 { | |||
Exit("Must specify listenAddr to stop") | |||
} | |||
listenAddr := args[0] | |||
command := btypes.CommandCloseListener{ | |||
Addr: listenAddr, | |||
} | |||
wg := sync.WaitGroup{} | |||
failed := 0 | |||
for _, remote := range Config.Remotes { | |||
wg.Add(1) | |||
go func(remote string) { | |||
defer wg.Done() | |||
response, err := CloseListener(Config.PrivKey, remote, command) | |||
if err != nil { | |||
failed++ | |||
fmt.Printf("%v failure. %v\n", remote, err) | |||
} else { | |||
fmt.Printf("%v success. %v\n", remote, response) | |||
} | |||
}(remote) | |||
} | |||
wg.Wait() | |||
if 0 < failed { | |||
os.Exit(1) | |||
} | |||
} | |||
func cliDownloadFile(c *cli.Context) { | |||
args := c.Args() | |||
if len(args) != 2 { | |||
Exit("Must specify <remote-path> <local-path-prefix>") | |||
} | |||
remotePath := args[0] | |||
localPathPrefix := args[1] | |||
command := btypes.CommandServeFile{ | |||
Path: remotePath, | |||
} | |||
wg := sync.WaitGroup{} | |||
failed := 0 | |||
for _, remote := range Config.Remotes { | |||
wg.Add(1) | |||
go func(remote string, localPath string) { | |||
defer wg.Done() | |||
n, err := DownloadFile(Config.PrivKey, remote, command, localPath) | |||
if err != nil { | |||
failed++ | |||
fmt.Printf("%v failure. %v\n", remote, err) | |||
} else { | |||
fmt.Printf("%v success. Wrote %v bytes to %v\n", remote, n, localPath) | |||
} | |||
}(remote, Fmt("%v_%v", localPathPrefix, remoteNick(remote))) | |||
} | |||
wg.Wait() | |||
if 0 < failed { | |||
os.Exit(1) | |||
} | |||
} | |||
func cliQuit(c *cli.Context) { | |||
command := btypes.CommandQuit{} | |||
wg := sync.WaitGroup{} | |||
failed := 0 | |||
for _, remote := range Config.Remotes { | |||
wg.Add(1) | |||
go func(remote string) { | |||
defer wg.Done() | |||
response, err := Quit(Config.PrivKey, remote, command) | |||
if err != nil { | |||
failed++ | |||
fmt.Printf("%v failure. %v\n", remote, err) | |||
} else { | |||
fmt.Printf("%v success. %v\n", remote, response) | |||
} | |||
}(remote) | |||
} | |||
wg.Wait() | |||
if 0 < failed { | |||
os.Exit(1) | |||
} | |||
} |
@ -1,11 +0,0 @@ | |||
A custom version of logrotate that doesn't rely on sudo access to /etc/logrotate.d. | |||
This will be the second process aside from "tendermint" managed by "debora/barak". | |||
```bash | |||
logjack -chopSize="10M" -limitSize="1G" $HOME/.tendermint/logs/tendermint.log | |||
``` | |||
The above example chops the log file and moves it, e.g. to $HOME/.tendermint/logs/tendermint.log.000, | |||
when the base file (tendermint.log) exceeds 10M in size. If the total size of tendermint.log.XXX exceeds 1G in size, | |||
the older files are removed. |
@ -1,148 +0,0 @@ | |||
package main | |||
import ( | |||
"flag" | |||
"fmt" | |||
"os" | |||
"path/filepath" | |||
"regexp" | |||
"strconv" | |||
"strings" | |||
"time" | |||
. "github.com/tendermint/go-common" | |||
) | |||
const Version = "0.0.1" | |||
const sleepSeconds = 1 // Every second | |||
// Parse command-line options | |||
func parseFlags() (chopSize int64, limitSize int64, version bool, logFiles []string) { | |||
var chopSizeStr, limitSizeStr string | |||
flag.StringVar(&chopSizeStr, "chopSize", "1M", "Move file if greater than this") | |||
flag.StringVar(&limitSizeStr, "limitSize", "1G", "Only keep this much (for each specified file). Remove old files.") | |||
flag.BoolVar(&version, "version", false, "Version") | |||
flag.Parse() | |||
logFiles = flag.Args() | |||
chopSize = parseBytesize(chopSizeStr) | |||
limitSize = parseBytesize(limitSizeStr) | |||
return | |||
} | |||
func main() { | |||
// Read options | |||
chopSize, limitSize, version, logFiles := parseFlags() | |||
if version { | |||
fmt.Println(Fmt("logjack version %v", Version)) | |||
return | |||
} | |||
// Print args. | |||
// fmt.Println(chopSize, limitSiz,e version, logFiles) | |||
go func() { | |||
for { | |||
for _, logFilePath := range logFiles { | |||
minIndex, maxIndex, totalSize, baseSize := readLogInfo(logFilePath) | |||
if chopSize < baseSize { | |||
moveLog(logFilePath, Fmt("%v.%03d", logFilePath, maxIndex+1)) | |||
} | |||
if limitSize < totalSize { | |||
// NOTE: we only remove one file at a time. | |||
removeLog(Fmt("%v.%03d", logFilePath, minIndex)) | |||
} | |||
} | |||
time.Sleep(sleepSeconds * time.Second) | |||
} | |||
}() | |||
// Trap signal | |||
TrapSignal(func() { | |||
fmt.Println("logjack shutting down") | |||
}) | |||
} | |||
func moveLog(oldPath, newPath string) { | |||
err := os.Rename(oldPath, newPath) | |||
if err != nil { | |||
panic(err) | |||
} | |||
} | |||
func removeLog(path string) { | |||
err := os.Remove(path) | |||
if err != nil { | |||
panic(err) | |||
} | |||
} | |||
// This is a strange function. Refactor everything to make it less strange? | |||
func readLogInfo(logPath string) (minIndex, maxIndex int, totalSize int64, baseSize int64) { | |||
logDir := filepath.Dir(logPath) | |||
logFile := filepath.Base(logPath) | |||
minIndex, maxIndex = -1, -1 | |||
dir, err := os.Open(logDir) | |||
if err != nil { | |||
panic(err) | |||
} | |||
fi, err := dir.Readdir(0) | |||
if err != nil { | |||
panic(err) | |||
} | |||
for _, fileInfo := range fi { | |||
indexedFilePattern := regexp.MustCompile("^.+\\.([0-9]{3,})$") | |||
if fileInfo.Name() == logFile { | |||
baseSize = fileInfo.Size() | |||
continue | |||
} else if strings.HasPrefix(fileInfo.Name(), logFile) { | |||
totalSize += fileInfo.Size() | |||
submatch := indexedFilePattern.FindSubmatch([]byte(fileInfo.Name())) | |||
if len(submatch) != 0 { | |||
// Matches | |||
logIndex, err := strconv.Atoi(string(submatch[1])) | |||
if err != nil { | |||
panic(err) | |||
} | |||
if maxIndex < logIndex { | |||
maxIndex = logIndex | |||
} | |||
if minIndex == -1 || logIndex < minIndex { | |||
minIndex = logIndex | |||
} | |||
} | |||
} | |||
} | |||
return minIndex, maxIndex, totalSize, baseSize | |||
} | |||
func parseBytesize(chopSize string) int64 { | |||
// Handle suffix multiplier | |||
var multiplier int64 = 1 | |||
if strings.HasSuffix(chopSize, "T") { | |||
multiplier = 1042 * 1024 * 1024 * 1024 | |||
chopSize = chopSize[:len(chopSize)-1] | |||
} | |||
if strings.HasSuffix(chopSize, "G") { | |||
multiplier = 1042 * 1024 * 1024 | |||
chopSize = chopSize[:len(chopSize)-1] | |||
} | |||
if strings.HasSuffix(chopSize, "M") { | |||
multiplier = 1042 * 1024 | |||
chopSize = chopSize[:len(chopSize)-1] | |||
} | |||
if strings.HasSuffix(chopSize, "K") { | |||
multiplier = 1042 | |||
chopSize = chopSize[:len(chopSize)-1] | |||
} | |||
// Parse the numeric part | |||
chopSizeInt, err := strconv.Atoi(chopSize) | |||
if err != nil { | |||
panic(err) | |||
} | |||
return int64(chopSizeInt) * multiplier | |||
} |
@ -1,6 +0,0 @@ | |||
stdinwriter reads from stdin and writes to the specified file, in a way compatible for logrotate to move around. | |||
(see tendermint/common/os#AutoFile) | |||
```bash | |||
some_command arg1 arg2 2>&1 | stdinwriter -o path_to_log.log | |||
``` |
@ -1,61 +0,0 @@ | |||
package main | |||
import ( | |||
"flag" | |||
"fmt" | |||
"io" | |||
"os" | |||
. "github.com/tendermint/go-common" | |||
) | |||
const Version = "0.0.1" | |||
const readBufferSize = 1024 | |||
// Parse command-line options | |||
func parseFlags() (outpath string, version bool) { | |||
flag.StringVar(&outpath, "outpath", "stdinwriter.out", "Output file name") | |||
flag.BoolVar(&version, "version", false, "Version") | |||
flag.Parse() | |||
return | |||
} | |||
func main() { | |||
// Read options | |||
outpath, version := parseFlags() | |||
if version { | |||
fmt.Println(Fmt("stdinwriter version %v", Version)) | |||
return | |||
} | |||
outfile, err := OpenAutoFile(outpath) | |||
if err != nil { | |||
Exit(Fmt("stdinwriter couldn't create outfile %v", outfile)) | |||
} | |||
go writeToOutfile(outfile) | |||
// Trap signal | |||
TrapSignal(func() { | |||
outfile.Close() | |||
fmt.Println("stdinwriter shutting down") | |||
}) | |||
} | |||
func writeToOutfile(outfile *AutoFile) { | |||
// Forever, read from stdin and write to AutoFile. | |||
buf := make([]byte, readBufferSize) | |||
for { | |||
n, err := os.Stdin.Read(buf) | |||
outfile.Write(buf[:n]) | |||
if err != nil { | |||
outfile.Close() | |||
if err == io.EOF { | |||
os.Exit(0) | |||
} else { | |||
Exit("stdinwriter errored") | |||
} | |||
} | |||
} | |||
} |