tendermint/cmd/debora/main.go

155 lines
3.5 KiB
Go

package main
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"sync"
acm "github.com/tendermint/tendermint/account"
"github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
pcm "github.com/tendermint/tendermint/process"
"github.com/tendermint/tendermint/rpc"
)
var Routes = map[string]*rpc.RPCFunc{
"RunProcess": rpc.NewRPCFunc(RunProcess, []string{"wait", "label", "execPath", "args"}),
"ListProcesses": rpc.NewRPCFunc(ListProcesses, []string{}),
"StopProcess": rpc.NewRPCFunc(StopProcess, []string{"label", "kill"}),
// NOTE: also, two special non-JSONRPC routes called
// "download" and "upload".
}
type Validator struct {
VotingPower uint64
PubKey acm.PubKey
}
type Options struct {
Validators []Validator
ListenAddress string
}
// Global instance
var debora = struct {
mtx sync.Mutex
processes map[string]*pcm.Process
}{sync.Mutex{}, make(map[string]*pcm.Process)}
func main() {
// read options from stdin.
var err error
optionsBytes, err := ioutil.ReadAll(os.Stdin)
if err != nil {
panic(Fmt("Error reading input: %v", err))
}
options := binary.ReadJSON(&Options{}, optionsBytes, &err).(*Options)
if err != nil {
panic(Fmt("Error parsing input: %v", err))
}
// Debug.
fmt.Printf("Validators: %v\n", options.Validators)
// start rpc server.
mux := http.NewServeMux()
mux.HandleFunc("/download", ServeFile)
// TODO: mux.HandleFunc("/upload", UploadFile)
rpc.RegisterRPCFuncs(mux, Routes)
rpc.StartHTTPServer(options.ListenAddress, mux)
TrapSignal(func() {
fmt.Println("Debora shutting down")
})
}
//------------------------------------------------------------------------------
// RPC functions
type ResponseRunProcess struct {
}
func RunProcess(wait bool, label string, execPath string, args []string, input string) (*ResponseRunProcess, error) {
debora.mtx.Lock()
// First, see if there already is a process labeled 'label'
existing := debora.processes[label]
if existing != nil {
debora.mtx.Unlock()
return nil, Errorf("Process already exists: %v", label)
}
// Otherwise, create one.
proc := pcm.Create(pcm.ProcessModeDaemon, label, execPath, args, input)
debora.processes[label] = proc
debora.mtx.Unlock()
if wait {
exitErr := pcm.Wait(proc)
return nil, exitErr
} else {
return &ResponseRunProcess{}, nil
}
}
//--------------------------------------
type ResponseListProcesses struct {
Processes []*pcm.Process
}
func ListProcesses() (*ResponseListProcesses, error) {
var procs = []*pcm.Process{}
debora.mtx.Lock()
for _, proc := range debora.processes {
procs = append(procs, proc)
}
debora.mtx.Unlock()
return &ResponseListProcesses{
Processes: procs,
}, nil
}
//--------------------------------------
type ResponseStopProcess struct {
}
func StopProcess(label string, kill bool) (*ResponseStopProcess, error) {
debora.mtx.Lock()
proc := debora.processes[label]
debora.mtx.Unlock()
if proc == nil {
return nil, Errorf("Process does not exist: %v", label)
}
err := pcm.Stop(proc, kill)
return &ResponseStopProcess{}, err
}
//------------------------------------------------------------------------------
func ServeFile(w http.ResponseWriter, req *http.Request) {
path := req.FormValue("path")
if path == "" {
http.Error(w, "Must specify path", 400)
return
}
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
}
}