You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

228 lines
5.6 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. package main
  2. // A note on the origin of the name.
  3. // http://en.wikipedia.org/wiki/Barak
  4. // TODO: Nonrepudiable command log
  5. import (
  6. "errors"
  7. "fmt"
  8. "io"
  9. "io/ioutil"
  10. "net/http"
  11. "os"
  12. "reflect"
  13. "sync"
  14. "github.com/tendermint/tendermint/binary"
  15. . "github.com/tendermint/tendermint/cmd/barak/types"
  16. . "github.com/tendermint/tendermint/common"
  17. pcm "github.com/tendermint/tendermint/process"
  18. "github.com/tendermint/tendermint/rpc"
  19. )
  20. var Routes = map[string]*rpc.RPCFunc{
  21. "status": rpc.NewRPCFunc(Status, []string{}),
  22. "run": rpc.NewRPCFunc(Run, []string{"auth_command"}),
  23. // NOTE: also, two special non-JSONRPC routes called "download" and "upload"
  24. }
  25. type Options struct {
  26. Validators []Validator
  27. ListenAddress string
  28. StartNonce uint64
  29. }
  30. // Global instance
  31. var barak = struct {
  32. mtx sync.Mutex
  33. processes map[string]*pcm.Process
  34. validators []Validator
  35. nonce uint64
  36. }{sync.Mutex{}, make(map[string]*pcm.Process), nil, 0}
  37. func main() {
  38. // read options from stdin.
  39. var err error
  40. optionsBytes, err := ioutil.ReadAll(os.Stdin)
  41. if err != nil {
  42. panic(Fmt("Error reading input: %v", err))
  43. }
  44. options := binary.ReadJSON(&Options{}, optionsBytes, &err).(*Options)
  45. if err != nil {
  46. panic(Fmt("Error parsing input: %v", err))
  47. }
  48. barak.nonce = options.StartNonce
  49. barak.validators = options.Validators
  50. // Debug.
  51. fmt.Printf("Options: %v\n", options)
  52. fmt.Printf("Barak: %v\n", barak)
  53. // Start rpc server.
  54. mux := http.NewServeMux()
  55. mux.HandleFunc("/download", ServeFile)
  56. // TODO: mux.HandleFunc("/upload", UploadFile)
  57. rpc.RegisterRPCFuncs(mux, Routes)
  58. rpc.StartHTTPServer(options.ListenAddress, mux)
  59. TrapSignal(func() {
  60. fmt.Println("Barak shutting down")
  61. })
  62. }
  63. //------------------------------------------------------------------------------
  64. // RPC functions
  65. func Status() (*ResponseStatus, error) {
  66. barak.mtx.Lock()
  67. nonce := barak.nonce
  68. validators := barak.validators
  69. barak.mtx.Unlock()
  70. return &ResponseStatus{
  71. Nonce: nonce,
  72. Validators: validators,
  73. }, nil
  74. }
  75. func Run(authCommand AuthCommand) (interface{}, error) {
  76. command, err := parseValidateCommand(authCommand)
  77. if err != nil {
  78. return nil, err
  79. }
  80. log.Info(Fmt("Run() received command %v", reflect.TypeOf(command)))
  81. // Issue command
  82. switch c := command.(type) {
  83. case CommandRunProcess:
  84. return RunProcess(c.Wait, c.Label, c.ExecPath, c.Args, c.Input)
  85. case CommandStopProcess:
  86. return StopProcess(c.Label, c.Kill)
  87. case CommandListProcesses:
  88. return ListProcesses()
  89. default:
  90. return nil, errors.New("Invalid endpoint for command")
  91. }
  92. }
  93. func parseValidateCommandStr(authCommandStr string) (Command, error) {
  94. var err error
  95. authCommand := binary.ReadJSON(AuthCommand{}, []byte(authCommandStr), &err).(AuthCommand)
  96. if err != nil {
  97. fmt.Printf("Failed to parse auth_command")
  98. return nil, errors.New("AuthCommand parse error")
  99. }
  100. return parseValidateCommand(authCommand)
  101. }
  102. func parseValidateCommand(authCommand AuthCommand) (Command, error) {
  103. commandJSONStr := authCommand.CommandJSONStr
  104. signatures := authCommand.Signatures
  105. // Validate commandJSONStr
  106. if !validate([]byte(commandJSONStr), barak.validators, signatures) {
  107. fmt.Printf("Failed validation attempt")
  108. return nil, errors.New("Validation error")
  109. }
  110. // Parse command
  111. var err error
  112. command := binary.ReadJSON(NoncedCommand{}, []byte(commandJSONStr), &err).(NoncedCommand)
  113. if err != nil {
  114. fmt.Printf("Failed to parse command")
  115. return nil, errors.New("Command parse error")
  116. }
  117. // Prevent replays
  118. if barak.nonce+1 != command.Nonce {
  119. return nil, errors.New("Replay error")
  120. } else {
  121. barak.nonce += 1
  122. }
  123. return command.Command, nil
  124. }
  125. //------------------------------------------------------------------------------
  126. // RPC base commands
  127. // WARNING Not validated, do not export to routes.
  128. func RunProcess(wait bool, label string, execPath string, args []string, input string) (*ResponseRunProcess, error) {
  129. barak.mtx.Lock()
  130. // First, see if there already is a process labeled 'label'
  131. existing := barak.processes[label]
  132. if existing != nil {
  133. barak.mtx.Unlock()
  134. return nil, fmt.Errorf("Process already exists: %v", label)
  135. }
  136. // Otherwise, create one.
  137. proc, err := pcm.Create(pcm.ProcessModeDaemon, label, execPath, args, input)
  138. if err == nil {
  139. barak.processes[label] = proc
  140. }
  141. barak.mtx.Unlock()
  142. if err != nil {
  143. return nil, err
  144. }
  145. if wait {
  146. exitErr := pcm.Wait(proc)
  147. return nil, exitErr
  148. } else {
  149. return &ResponseRunProcess{}, nil
  150. }
  151. }
  152. func StopProcess(label string, kill bool) (*ResponseStopProcess, error) {
  153. barak.mtx.Lock()
  154. proc := barak.processes[label]
  155. barak.mtx.Unlock()
  156. if proc == nil {
  157. return nil, fmt.Errorf("Process does not exist: %v", label)
  158. }
  159. err := pcm.Stop(proc, kill)
  160. return &ResponseStopProcess{}, err
  161. }
  162. func ListProcesses() (*ResponseListProcesses, error) {
  163. var procs = []*pcm.Process{}
  164. barak.mtx.Lock()
  165. fmt.Println("Processes: %v", barak.processes)
  166. for _, proc := range barak.processes {
  167. procs = append(procs, proc)
  168. }
  169. barak.mtx.Unlock()
  170. return &ResponseListProcesses{
  171. Processes: procs,
  172. }, nil
  173. }
  174. func ServeFile(w http.ResponseWriter, req *http.Request) {
  175. authCommandStr := req.FormValue("auth_command")
  176. command, err := parseValidateCommandStr(authCommandStr)
  177. if err != nil {
  178. http.Error(w, Fmt("Invalid command: %v", err), 400)
  179. }
  180. serveCommand, ok := command.(CommandServeFile)
  181. if !ok {
  182. http.Error(w, "Invalid command", 400)
  183. }
  184. path := serveCommand.Path
  185. if path == "" {
  186. http.Error(w, "Must specify path", 400)
  187. return
  188. }
  189. file, err := os.Open(path)
  190. if err != nil {
  191. http.Error(w, Fmt("Error opening file: %v. %v", path, err), 400)
  192. return
  193. }
  194. _, err = io.Copy(w, file)
  195. if err != nil {
  196. fmt.Fprintf(os.Stderr, Fmt("Error serving file: %v. %v", path, err))
  197. return
  198. }
  199. }