├── README.md ├── main.go ├── monitor_linux.go └── restart_linux.go /README.md: -------------------------------------------------------------------------------- 1 | go-superviser 2 | ============= 3 | 4 | go-superviser for restart a go service 5 | 6 | you can use the command: 7 | 8 | go-superviser -project=httpserver -run=true 9 | 10 | params: 11 | 12 | project: project to be supervised, it will find to $GOPATH/src/{$project} 13 | 14 | run: whether to run after build the project. 15 | if it is false or empty, the superviser will only build the project. 16 | if it is true, the superviser will build and run the project. -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "go/build" 6 | "log" 7 | ) 8 | 9 | var ( 10 | project = flag.String("project", "", "project to be supervised") 11 | withrun = flag.Bool("run", false, "whether to run after build the project") 12 | end = make(chan bool) 13 | ) 14 | 15 | func main() { 16 | flag.Parse() 17 | 18 | if *project == "" { 19 | println("Params Error:") 20 | flag.PrintDefaults() 21 | return 22 | } 23 | 24 | p, err := build.Default.Import(*project, "", build.FindOnly) 25 | if err != nil { 26 | log.Fatalf("Couldn't find project: %v", err) 27 | } 28 | 29 | root := p.Dir 30 | action := make(chan uint32) 31 | go monitor(root, action) 32 | 33 | restart(root) 34 | 35 | for { 36 | select { 37 | case <- action: 38 | out, err := restart(root) 39 | if err != nil { 40 | log.Println("===============notice================") 41 | log.Println("error out:", string(out)) 42 | log.Println("could not restart the project: %v", err) 43 | log.Println("======================================") 44 | } 45 | case <- end: 46 | log.Fatalf("superviser end") 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /monitor_linux.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | "log" 7 | ) 8 | 9 | func monitor(root string, action chan uint32) { 10 | fd, err := syscall.InotifyInit() 11 | if fd == -1 || err != nil { 12 | end <- true 13 | return 14 | } 15 | 16 | flags := syscall.IN_MODIFY | syscall.IN_CREATE | syscall.IN_DELETE // 2/128/512 17 | wd, _ := syscall.InotifyAddWatch(fd, root, uint32(flags)) 18 | if wd == -1 { 19 | end <- true 20 | return 21 | } 22 | var ( 23 | buf [syscall.SizeofInotifyEvent * 10]byte 24 | n int 25 | ) 26 | 27 | for { 28 | n, _ = syscall.Read(fd, buf[0:]) 29 | if n > syscall.SizeofInotifyEvent { 30 | var offset = 0 31 | for offset < n { 32 | raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset])) 33 | mask := uint32(raw.Mask) 34 | offset = offset + int(raw.Len) + syscall.SizeofInotifyEvent 35 | action <- mask 36 | 37 | log.Println("action:", mask) 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /restart_linux.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import( 4 | "bytes" 5 | "os/exec" 6 | "sync" 7 | "fmt" 8 | "go/build" 9 | "strings" 10 | "os" 11 | "log" 12 | ) 13 | 14 | func restart(root string) (out []byte, err error){ 15 | stopRun() 16 | 17 | context := build.Default 18 | splits := strings.Split(root, "/") 19 | bin := context.GOPATH + "/bin/" + splits[len(splits) - 1] 20 | os.Remove(bin) 21 | 22 | out, err = run(context.GOPATH + "/bin/", "go", "build", "-o", bin, splits[len(splits) - 1]) 23 | if err != nil { 24 | return out, err 25 | } 26 | if *withrun { 27 | go run("", bin) 28 | } 29 | return nil, nil 30 | } 31 | 32 | func stopRun() { 33 | running.Lock() 34 | if running.cmd != nil { 35 | log.Println("kill cmd:", running.cmd.Args) 36 | running.cmd.Process.Kill() 37 | running.cmd = nil 38 | } 39 | running.Unlock() 40 | } 41 | 42 | 43 | var running struct { 44 | sync.Mutex 45 | cmd *exec.Cmd 46 | } 47 | 48 | func run(dir string, args ...string) ([]byte, error) { 49 | var buf bytes.Buffer 50 | cmd := exec.Command(args[0], args[1:]...) 51 | log.Println("run cmd:", cmd.Args) 52 | 53 | cmd.Dir = dir 54 | cmd.Stdout = &buf 55 | cmd.Stderr = cmd.Stdout 56 | 57 | // Start command and leave in 'running'. 58 | running.Lock() 59 | 60 | if running.cmd != nil { 61 | log.Println("running cmd:", running.cmd.Args) 62 | defer running.Unlock() 63 | return nil, fmt.Errorf("already running %s", running.cmd.Path) 64 | } 65 | if err := cmd.Start(); err != nil { 66 | running.Unlock() 67 | return nil, err 68 | } 69 | running.cmd = cmd 70 | running.Unlock() 71 | 72 | // Wait for the command. Clean up, 73 | err := cmd.Wait() 74 | running.Lock() 75 | if running.cmd == cmd { 76 | running.cmd = nil 77 | } 78 | running.Unlock() 79 | return buf.Bytes(), err 80 | } --------------------------------------------------------------------------------