github.com/ladydascalie/elvish@v0.0.0-20170703214355-2964dd3ece7f/daemon/daemon.go (about) 1 // Package exec provides the entry point of the daemon sub-program and helpers 2 // to spawn a daemon process. 3 package daemon 4 5 import ( 6 "errors" 7 "log" 8 "os" 9 "path" 10 "path/filepath" 11 "strconv" 12 "strings" 13 "syscall" 14 15 "github.com/elves/elvish/util" 16 ) 17 18 // Daemon keeps configurations for the daemon process. 19 type Daemon struct { 20 Forked int 21 BinPath string 22 DbPath string 23 SockPath string 24 LogPathPrefix string 25 } 26 27 // closeFd is used in syscall.ProcAttr.Files to signify closing a fd. 28 const closeFd = ^uintptr(0) 29 30 // Main is the entry point of the daemon sub-program. 31 func (d *Daemon) Main(serve func(string, string)) int { 32 switch d.Forked { 33 case 0: 34 errored := false 35 absify := func(f string, s *string) { 36 if *s == "" { 37 log.Println("flag", f, "is required for daemon") 38 errored = true 39 return 40 } 41 p, err := filepath.Abs(*s) 42 if err != nil { 43 log.Println("abs:", err) 44 errored = true 45 } else { 46 *s = p 47 } 48 } 49 absify("-bin", &d.BinPath) 50 absify("-db", &d.DbPath) 51 absify("-sock", &d.SockPath) 52 absify("-logprefix", &d.LogPathPrefix) 53 if errored { 54 return 2 55 } 56 57 syscall.Umask(0077) 58 return d.pseudoFork( 59 &syscall.ProcAttr{ 60 // cd to / 61 Dir: "/", 62 // empty environment 63 Env: nil, 64 // inherit stderr only for logging 65 Files: []uintptr{closeFd, closeFd, 2}, 66 Sys: &syscall.SysProcAttr{Setsid: true}, 67 }) 68 case 1: 69 return d.pseudoFork( 70 &syscall.ProcAttr{ 71 Files: []uintptr{closeFd, closeFd, 2}, 72 }) 73 case 2: 74 serve(d.SockPath, d.DbPath) 75 return 0 76 default: 77 return 2 78 } 79 } 80 81 // Spawn spawns a daemon in the background. It is supposed to be called from a 82 // client. 83 func (d *Daemon) Spawn() error { 84 binPath := d.BinPath 85 // Determine binPath. 86 if binPath == "" { 87 if len(os.Args) > 0 && path.IsAbs(os.Args[0]) { 88 binPath = os.Args[0] 89 } else { 90 // Find elvish in PATH 91 paths := strings.Split(os.Getenv("PATH"), ":") 92 result, err := util.Search(paths, "elvish") 93 if err != nil { 94 return errors.New("cannot find elvish: " + err.Error()) 95 } 96 binPath = result 97 } 98 } 99 return forkExec(nil, 0, binPath, d.DbPath, d.SockPath, d.LogPathPrefix) 100 } 101 102 // pseudoFork forks a daemon. It is supposed to be called from the daemon. 103 func (d *Daemon) pseudoFork(attr *syscall.ProcAttr) int { 104 err := forkExec(attr, d.Forked+1, d.BinPath, d.DbPath, d.SockPath, d.LogPathPrefix) 105 if err != nil { 106 return 2 107 } 108 return 0 109 } 110 111 func forkExec(attr *syscall.ProcAttr, forkLevel int, binPath, dbPath, sockPath, logPathPrefix string) error { 112 _, err := syscall.ForkExec(binPath, []string{ 113 binPath, 114 "-daemon", 115 "-forked", strconv.Itoa(forkLevel), 116 "-bin", binPath, 117 "-db", dbPath, 118 "-sock", sockPath, 119 "-logprefix", logPathPrefix, 120 }, attr) 121 return err 122 }