github.com/unirita/cuto@v0.9.8-0.20160830082821-aa6652f877b7/realtime/network/exec.go (about) 1 package network 2 3 import ( 4 "bufio" 5 "fmt" 6 "io" 7 "os/exec" 8 "path/filepath" 9 "regexp" 10 "strconv" 11 "strings" 12 "time" 13 14 "github.com/unirita/cuto/util" 15 ) 16 17 // Command represents a master command which executes realtime network. 18 type Command struct { 19 cmd *exec.Cmd 20 pid int 21 networkName string 22 } 23 24 // NewCommand creates Command object with unique network name. 25 // Network name is generate from realtimeName and current timestamp. 26 func NewCommand(realtimeName string) *Command { 27 c := new(Command) 28 timestamp := time.Now().Format("20060102150405") 29 if realtimeName == "" { 30 c.networkName = fmt.Sprintf("realtime_%s", timestamp) 31 } else { 32 c.networkName = fmt.Sprintf("realtime_%s_%s", realtimeName, timestamp) 33 } 34 35 masterPath := filepath.Join(util.GetRootPath(), "bin", "master") 36 configPath := filepath.Join(util.GetRootPath(), "bin", "master.ini") 37 c.cmd = exec.Command(masterPath, "-n", c.networkName, "-s", "-c", configPath) 38 return c 39 } 40 41 // GetNetworkName returns network name. 42 func (c *Command) GetNetworkName() string { 43 return c.networkName 44 } 45 46 // GetPID returns master process ID. 47 // If Run is not called, returns 0. 48 func (c *Command) GetPID() int { 49 return c.pid 50 } 51 52 // Run runs the master command and gets its instance id. 53 func (c *Command) Run() (int, error) { 54 stdoutReader, err := c.cmd.StdoutPipe() 55 if err != nil { 56 return 0, err 57 } 58 if err := c.cmd.Start(); err != nil { 59 return 0, err 60 } 61 c.pid = c.cmd.Process.Pid 62 63 lineCh := make(chan string, 1) 64 waitCh := make(chan struct{}, 1) 65 idCh := make(chan string, 1) 66 errCh := make(chan string, 1) 67 68 go c.monitorStdout(lineCh, stdoutReader) 69 go c.waitProcess(waitCh) 70 go c.waitID(idCh, errCh, lineCh, waitCh) 71 72 select { 73 case idStr := <-idCh: 74 id, err := strconv.Atoi(idStr) 75 if err != nil { 76 return 0, fmt.Errorf("Invalid instance ID[%s] received.", idStr) 77 } 78 return id, nil 79 case errMsg := <-errCh: 80 return 0, fmt.Errorf("Master error: %s", errMsg) 81 } 82 } 83 84 // Release releases any resources associated with the master command process. 85 // It is recommended that call this function if you do not wait end of process. 86 func (c *Command) Release() error { 87 return c.cmd.Process.Release() 88 } 89 90 func (c *Command) monitorStdout(lineCh chan<- string, reader io.Reader) { 91 scanner := bufio.NewScanner(reader) 92 for scanner.Scan() { 93 line := scanner.Text() 94 if line != "" { 95 lineCh <- line 96 } 97 } 98 } 99 100 func (c *Command) waitProcess(waitCh chan<- struct{}) { 101 c.cmd.Wait() 102 close(waitCh) 103 } 104 105 func (c *Command) waitID(idCh, errCh chan<- string, lineCh <-chan string, waitCh <-chan struct{}) { 106 matcher := regexp.MustCompile(`INSTANCE \[\d+`) 107 output := "" 108 for { 109 select { 110 case line := <-lineCh: 111 id := matcher.FindString(line) 112 if id != "" { 113 id = strings.Replace(id, "INSTANCE [", "", 1) 114 idCh <- id 115 return 116 } 117 output += line + "\n" 118 case <-waitCh: 119 errCh <- output 120 return 121 } 122 } 123 }