diff --git a/outlet.go b/outlet.go index 83bb9c4..040bb94 100644 --- a/outlet.go +++ b/outlet.go @@ -6,6 +6,7 @@ import ( "fmt" "github.com/daviddengcn/go-colortext" "github.com/kr/pretty" + "io" "sync" ) @@ -36,7 +37,7 @@ func (o *Outlet) Write(b []byte) (num int, err error) { scanner := bufio.NewScanner(bytes.NewReader(b)) for scanner.Scan() { formatter := fmt.Sprintf("%%-%ds | ", LongestOutletName()) - ct.ChangeColor(o.Color, true, ct.None, true) + ct.ChangeColor(o.Color, false, ct.None, false) fmt.Printf(formatter, o.Name) if (o.IsError) { ct.ChangeColor(ct.Red, true, ct.None, true) @@ -50,6 +51,10 @@ func (o *Outlet) Write(b []byte) (num int, err error) { return } +func ProcessOutput(w io.Writer, str string) { + w.Write([]byte(str)) +} + var outlets = map[string]*Outlet{} func createOutlet(name string, index int, isError bool) *Outlet { @@ -58,8 +63,8 @@ func createOutlet(name string, index int, isError bool) *Outlet { } func LongestOutletName() (longest int) { - // kr? better way to do this? - longest = 0 + // kr? + longest = 7 // foreman is the shortest name for name, _ := range outlets { if len(name) > longest { longest = len(name) @@ -67,3 +72,12 @@ func LongestOutletName() (longest int) { } return } + +func SystemOutput(str string) { + ct.ChangeColor(ct.White, true, ct.None, true) + formatter := fmt.Sprintf("%%-%ds | ", LongestOutletName()) + fmt.Printf(formatter, "foreman") + ct.ResetColor() + fmt.Println(str) + ct.ResetColor() +} diff --git a/procfile.go b/procfile.go index 599bbc3..73fe03c 100644 --- a/procfile.go +++ b/procfile.go @@ -12,8 +12,8 @@ import ( var procfileEntryRegexp = regexp.MustCompile("^([A-Za-z0-9_]+):\\s*(.+)$") type ProcfileEntry struct { - name string - command string + Name string + Command string } type Procfile struct { diff --git a/signal.go b/signal.go index 3306de0..4be5448 100644 --- a/signal.go +++ b/signal.go @@ -10,12 +10,12 @@ var _ = fmt.Println // lol func init() { handler := make(chan os.Signal, 1) - fmt.Println("init") signal.Notify(handler, os.Interrupt) go func() { for sig := range handler { switch (sig) { case os.Interrupt: + fmt.Println(" | ctrl-c detected, shutting down") ShutdownProcesses() } } diff --git a/start.go b/start.go index 8e49127..f4cb066 100644 --- a/start.go +++ b/start.go @@ -9,19 +9,22 @@ import ( "sync" "syscall" "time" - "unsafe" ) +const shutdownGraceTime = 1 * time.Second + var _ = pretty.Println // lol var _ = os.Stdout var flagProcfile string var flagEnv string +var flagPort int + var wg sync.WaitGroup var cmdStart = &Command{ Run: runStart, - Usage: "start [-f procfile] [-e env] [-c concurrency]", + Usage: "start [-f procfile] [-e env] [-c concurrency] [-p port]", Short: "Start the application", Long: ` Start the application specified by a Procfile (defaults to ./Procfile) @@ -38,6 +41,7 @@ var processes = map[string]*exec.Cmd{} func init() { cmdStart.Flag.StringVar(&flagProcfile, "f", "Procfile", "procfile") cmdStart.Flag.StringVar(&flagEnv, "e", "", "env") + cmdStart.Flag.IntVar(&flagPort, "p", 5000, "port") } func runStart(cmd *Command, args []string) { @@ -57,53 +61,40 @@ func runStart(cmd *Command, args []string) { for idx, proc := range pf.entries { wg.Add(1) - go func(idx int, proc ProcfileEntry) { - command := []string{"/bin/bash", "-c", proc.command} - var attr syscall.ProcAttr - attr.Files = []uintptr{0,0,0} - attr.Files[1] = uintptr(unsafe.Pointer(createOutlet(proc.name, idx, false))) - attr.Files[2] = uintptr(unsafe.Pointer(createOutlet(proc.name, idx, true))) - attr.Env = ps_env - pid, err := syscall.ForkExec(command[0], command[1:], &attr) - pretty.Println("arg0", command[0], "argv", command[1:]) - fmt.Println("pid", pid, "err", err) - /* ps := exec.Command(command[0], command[1:]...)*/ - /* ps.Dir = root*/ - /* ps.Env = ps_env*/ - /* ps.Stdin = nil*/ - /* ps.Stdout = createOutlet(proc.name, idx, false)*/ - /* ps.Stderr = createOutlet(proc.name, idx, true)*/ - /* processes[proc.name] = ps*/ - /* ps.Start()*/ - /* syscall.Setpgid(ps.Process.Pid, -1 * ps.Process.Pid)*/ - fmt.Println("pid", pid) - var wait syscall.WaitStatus - var usage syscall.Rusage - syscall.Wait4(pid, &wait, 0, &usage) - fmt.Println("exitstatus", wait.ExitStatus()) - fmt.Println("process died") + command := []string{"/bin/bash", "-c", proc.Command} + ps := exec.Command(command[0], command[1:]...) + port := flagPort + (idx * 100) + processes[proc.Name] = ps + ps.Dir = root + ps.Env = append(ps_env, fmt.Sprintf("PORT=%d", port)) + ps.Stdin = nil + ps.Stdout = createOutlet(proc.Name, idx, false) + ps.Stderr = createOutlet(proc.Name, idx, true) + ps.SysProcAttr = &syscall.SysProcAttr{} + ps.SysProcAttr.Setsid = true + ps.Start() + SystemOutput(fmt.Sprintf("starting %s on port %d", proc.Name, port)) + go func(proc ProcfileEntry, ps *exec.Cmd) { + ps.Wait() wg.Done() - delete(processes, proc.name) - }(idx, proc) + delete(processes, proc.Name) + }(proc, ps) } wg.Wait() - fmt.Println("afterdone") } func ShutdownProcesses() { - for _, ps := range processes { - fmt.Println("killing", ps.Process) - group, _ := os.FindProcess(ps.Process.Pid) + for name, ps := range processes { + SystemOutput(fmt.Sprintf("sending SIGTERM to %s", name)) + group, _ := os.FindProcess(-1 * ps.Process.Pid) group.Signal(syscall.SIGTERM) } go func() { - time.Sleep(1 * time.Second) - fmt.Println("really killing") - for _, ps := range processes { - fmt.Println("killing", ps.Process) - ps.Process.Signal(syscall.SIGKILL) - b, _ := os.FindProcess(-1 * ps.Process.Pid) - b.Signal(syscall.SIGKILL) + time.Sleep(shutdownGraceTime) + for name, ps := range processes { + SystemOutput(fmt.Sprintf("sending SIGKILL to %s", name)) + group, _ := os.FindProcess(-1 * ps.Process.Pid) + group.Signal(syscall.SIGKILL) } }() }