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.

289 lines
7.7 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
  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. "bytes"
  7. "errors"
  8. "flag"
  9. "fmt"
  10. "io"
  11. "io/ioutil"
  12. "net/http"
  13. "os"
  14. "reflect"
  15. "time"
  16. . "github.com/tendermint/tendermint/cmd/barak/types"
  17. . "github.com/tendermint/tendermint/common"
  18. cfg "github.com/tendermint/tendermint/config"
  19. pcm "github.com/tendermint/tendermint/process"
  20. "github.com/tendermint/tendermint/rpc/server"
  21. "github.com/tendermint/tendermint/wire"
  22. )
  23. const BarakVersion = "0.0.1"
  24. var Routes map[string]*rpcserver.RPCFunc
  25. func init() {
  26. Routes = map[string]*rpcserver.RPCFunc{
  27. "status": rpcserver.NewRPCFunc(Status, []string{}),
  28. "run": rpcserver.NewRPCFunc(Run, []string{"auth_command"}),
  29. // NOTE: also, two special non-JSONRPC routes called "download" and "upload"
  30. }
  31. }
  32. // Global instance
  33. var barak_ *Barak
  34. // Parse command-line options
  35. func parseFlags() (optionsFile string) {
  36. flag.StringVar(&optionsFile, "config", "", "Read config from file instead of stdin")
  37. flag.Parse()
  38. return
  39. }
  40. func main() {
  41. fmt.Printf("New Barak Process (PID: %d)\n", os.Getpid())
  42. // Apply bare tendermint/* configuration.
  43. cfg.ApplyConfig(cfg.MapConfig(map[string]interface{}{"log_level": "info"}))
  44. // Read options
  45. optionsFile := parseFlags()
  46. options := ReadBarakOptions(optionsFile)
  47. // Init barak
  48. barak_ = NewBarakFromOptions(options)
  49. barak_.StartRegisterRoutine()
  50. barak_.WritePidFile() // This should be last, before TrapSignal().
  51. TrapSignal(func() {
  52. fmt.Println("Barak shutting down")
  53. })
  54. }
  55. //------------------------------------------------------------------------------
  56. // RPC functions
  57. func Status() (*ResponseStatus, error) {
  58. barak_.mtx.Lock()
  59. pid := barak_.pid
  60. nonce := barak_.nonce
  61. validators := barak_.validators
  62. barak_.mtx.Unlock()
  63. return &ResponseStatus{
  64. Version: BarakVersion,
  65. Pid: pid,
  66. Nonce: nonce,
  67. Validators: validators,
  68. }, nil
  69. }
  70. func Run(authCommand AuthCommand) (interface{}, error) {
  71. command, err := parseValidateCommand(authCommand)
  72. if err != nil {
  73. return nil, err
  74. }
  75. log.Notice(Fmt("Run() received command %v:%v", reflect.TypeOf(command), command))
  76. // Issue command
  77. switch c := command.(type) {
  78. case CommandStartProcess:
  79. return StartProcess(c.Wait, c.Label, c.ExecPath, c.Args, c.Input)
  80. case CommandStopProcess:
  81. return StopProcess(c.Label, c.Kill)
  82. case CommandListProcesses:
  83. return ListProcesses()
  84. case CommandOpenListener:
  85. return OpenListener(c.Addr)
  86. case CommandCloseListener:
  87. return CloseListener(c.Addr)
  88. case CommandQuit:
  89. return Quit()
  90. default:
  91. return nil, errors.New("Invalid endpoint for command")
  92. }
  93. }
  94. func parseValidateCommandStr(authCommandStr string) (Command, error) {
  95. var err error
  96. authCommand := wire.ReadJSON(AuthCommand{}, []byte(authCommandStr), &err).(AuthCommand)
  97. if err != nil {
  98. fmt.Printf("Failed to parse auth_command")
  99. return nil, errors.New("AuthCommand parse error")
  100. }
  101. return parseValidateCommand(authCommand)
  102. }
  103. func parseValidateCommand(authCommand AuthCommand) (Command, error) {
  104. commandJSONStr := authCommand.CommandJSONStr
  105. signatures := authCommand.Signatures
  106. // Validate commandJSONStr
  107. if !validate([]byte(commandJSONStr), barak_.ListValidators(), signatures) {
  108. fmt.Printf("Failed validation attempt")
  109. return nil, errors.New("Validation error")
  110. }
  111. // Parse command
  112. var err error
  113. command := wire.ReadJSON(NoncedCommand{}, []byte(commandJSONStr), &err).(NoncedCommand)
  114. if err != nil {
  115. fmt.Printf("Failed to parse command")
  116. return nil, errors.New("Command parse error")
  117. }
  118. // Prevent replays
  119. barak_.CheckIncrNonce(command.Nonce)
  120. return command.Command, nil
  121. }
  122. //------------------------------------------------------------------------------
  123. // RPC base commands
  124. // WARNING Not validated, do not export to routes.
  125. func StartProcess(wait bool, label string, execPath string, args []string, input string) (*ResponseStartProcess, error) {
  126. // First, see if there already is a process labeled 'label'
  127. existing := barak_.GetProcess(label)
  128. if existing != nil && existing.EndTime.IsZero() {
  129. return nil, fmt.Errorf("Process already exists: %v", label)
  130. }
  131. // Otherwise, create one.
  132. err := EnsureDir(barak_.RootDir() + "/outputs")
  133. if err != nil {
  134. return nil, fmt.Errorf("Failed to create outputs dir: %v", err)
  135. }
  136. inFile := bytes.NewReader([]byte(input))
  137. outPath := Fmt("%v/outputs/%v_%v.out", barak_.RootDir(), label, time.Now().Format("2006_01_02_15_04_05_MST"))
  138. outFile, err := OpenAutoFile(outPath)
  139. if err != nil {
  140. return nil, err
  141. }
  142. proc, err := pcm.Create(label, execPath, args, inFile, outFile)
  143. if err != nil {
  144. return nil, err
  145. }
  146. barak_.AddProcess(label, proc)
  147. if wait {
  148. <-proc.WaitCh
  149. // read output from outPath
  150. outputBytes, err := ioutil.ReadFile(outPath)
  151. if err != nil {
  152. fmt.Sprintf("ERROR READING OUTPUT: %v", err)
  153. }
  154. output := string(outputBytes)
  155. // fmt.Println("Read output", output)
  156. if proc.ExitState == nil {
  157. return &ResponseStartProcess{
  158. Success: true,
  159. Output: output,
  160. }, nil
  161. } else {
  162. return &ResponseStartProcess{
  163. Success: proc.ExitState.Success(), // Would be always false?
  164. Output: output,
  165. }, nil
  166. }
  167. } else {
  168. return &ResponseStartProcess{
  169. Success: true,
  170. Output: "",
  171. }, nil
  172. }
  173. }
  174. func StopProcess(label string, kill bool) (*ResponseStopProcess, error) {
  175. err := barak_.StopProcess(label, kill)
  176. return &ResponseStopProcess{}, err
  177. }
  178. func ListProcesses() (*ResponseListProcesses, error) {
  179. procs := barak_.ListProcesses()
  180. return &ResponseListProcesses{
  181. Processes: procs,
  182. }, nil
  183. }
  184. func OpenListener(addr string) (*ResponseOpenListener, error) {
  185. listener, err := barak_.OpenListener(addr)
  186. if err != nil {
  187. return nil, err
  188. }
  189. return &ResponseOpenListener{
  190. Addr: listener.Addr().String(),
  191. }, nil
  192. }
  193. func CloseListener(addr string) (*ResponseCloseListener, error) {
  194. barak_.CloseListener(addr)
  195. return &ResponseCloseListener{}, nil
  196. }
  197. func Quit() (*ResponseQuit, error) {
  198. fmt.Println("Barak shutting down due to Quit()")
  199. go func() {
  200. time.Sleep(time.Second)
  201. os.Exit(0)
  202. }()
  203. return &ResponseQuit{}, nil
  204. }
  205. //--------------------------------------------------------------------------------
  206. // Another barak instance registering its external
  207. // address to a remote barak.
  208. func RegisterHandler(w http.ResponseWriter, req *http.Request) {
  209. registry, err := os.OpenFile(barak_.RootDir()+"/registry.log", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600)
  210. if err != nil {
  211. http.Error(w, "Could not open registry file. Please contact the administrator", 500)
  212. return
  213. }
  214. // TODO: Also check the X-FORWARDED-FOR or whatever it's called.
  215. registry.Write([]byte(Fmt("++ %v\n", req.RemoteAddr)))
  216. registry.Close()
  217. w.Header().Set("Content-Type", "application/json")
  218. w.WriteHeader(200)
  219. w.Write([]byte("Noted!"))
  220. }
  221. func ServeFileHandler(w http.ResponseWriter, req *http.Request) {
  222. authCommandStr := req.FormValue("auth_command")
  223. command, err := parseValidateCommandStr(authCommandStr)
  224. if err != nil {
  225. http.Error(w, Fmt("Invalid command: %v", err), 400)
  226. }
  227. serveCommand, ok := command.(CommandServeFile)
  228. if !ok {
  229. http.Error(w, "Invalid command", 400)
  230. }
  231. path := serveCommand.Path
  232. if path == "" {
  233. http.Error(w, "Must specify path", 400)
  234. return
  235. }
  236. if path[0] == '.' {
  237. // local paths must be explicitly local, e.g. "./xyz"
  238. } else if path[0] != '/' {
  239. // If not an absolute path, then is label
  240. proc := barak_.GetProcess(path)
  241. if proc == nil {
  242. http.Error(w, Fmt("Unknown process label: %v", path), 400)
  243. return
  244. }
  245. path = proc.OutputFile.(*os.File).Name()
  246. }
  247. file, err := os.Open(path)
  248. if err != nil {
  249. http.Error(w, Fmt("Error opening file: %v. %v", path, err), 400)
  250. return
  251. }
  252. _, err = io.Copy(w, file)
  253. if err != nil {
  254. fmt.Fprintf(os.Stderr, Fmt("Error serving file: %v. %v", path, err))
  255. return
  256. }
  257. }