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.

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