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.

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