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 } }