gitee.com/quant1x/gox@v1.21.2/daemon/daemon_darwin.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by 3 // license that can be found in the LICENSE file. 4 5 // Package daemon darwin (mac os x) version 6 package daemon 7 8 import ( 9 "os" 10 "os/exec" 11 "os/user" 12 "path/filepath" 13 "regexp" 14 "text/template" 15 ) 16 17 // darwinRecord - standard record (struct) for darwin version of daemon package 18 type darwinRecord struct { 19 name string 20 description string 21 kind Kind 22 dependencies []string 23 } 24 25 func newDaemon(name, description string, kind Kind, dependencies []string) (Daemon, error) { 26 27 return &darwinRecord{name, description, kind, dependencies}, nil 28 } 29 30 // Standard service path for system daemons 31 func (darwin *darwinRecord) servicePath() string { 32 var path string 33 34 switch darwin.kind { 35 case UserAgent: 36 usr, _ := user.Current() 37 path = usr.HomeDir + "/Library/LaunchAgents/" + darwin.name + ".plist" 38 case GlobalAgent: 39 path = "/Library/LaunchAgents/" + darwin.name + ".plist" 40 case GlobalDaemon: 41 path = "/Library/LaunchDaemons/" + darwin.name + ".plist" 42 } 43 44 return path 45 } 46 47 // Is a service installed 48 func (darwin *darwinRecord) isInstalled() bool { 49 50 if _, err := os.Stat(darwin.servicePath()); err == nil { 51 return true 52 } 53 54 return false 55 } 56 57 // Get executable path 58 func execPath() (string, error) { 59 return filepath.Abs(os.Args[0]) 60 } 61 62 // Check service is running 63 func (darwin *darwinRecord) checkRunning() (string, bool) { 64 output, err := exec.Command("launchctl", "list", darwin.name).Output() 65 if err == nil { 66 if matched, err := regexp.MatchString(darwin.name, string(output)); err == nil && matched { 67 reg := regexp.MustCompile("PID\" = ([0-9]+);") 68 data := reg.FindStringSubmatch(string(output)) 69 if len(data) > 1 { 70 return "Service (pid " + data[1] + ") is running...", true 71 } 72 return "Service is running...", true 73 } 74 } 75 76 return "Service is stopped", false 77 } 78 79 // Install the service 80 func (darwin *darwinRecord) Install(args ...string) (string, error) { 81 installAction := "Install " + darwin.description + ":" 82 83 ok, err := checkPrivileges() 84 if !ok && darwin.kind != UserAgent { 85 return installAction + failed, err 86 } 87 88 srvPath := darwin.servicePath() 89 90 if darwin.isInstalled() { 91 return installAction + failed, ErrAlreadyInstalled 92 } 93 94 file, err := os.Create(srvPath) 95 if err != nil { 96 return installAction + failed, err 97 } 98 defer file.Close() 99 100 execPatch, err := executablePath(darwin.name) 101 if err != nil { 102 return installAction + failed, err 103 } 104 105 templ, err := template.New("propertyList").Parse(propertyList) 106 if err != nil { 107 return installAction + failed, err 108 } 109 110 if err := templ.Execute( 111 file, 112 &struct { 113 Name, Path string 114 Args []string 115 }{darwin.name, execPatch, args}, 116 ); err != nil { 117 return installAction + failed, err 118 } 119 120 return installAction + success, nil 121 } 122 123 // Remove the service 124 func (darwin *darwinRecord) Remove() (string, error) { 125 removeAction := "Removing " + darwin.description + ":" 126 127 ok, err := checkPrivileges() 128 if !ok && darwin.kind != UserAgent { 129 return removeAction + failed, err 130 } 131 132 if !darwin.isInstalled() { 133 return removeAction + failed, ErrNotInstalled 134 } 135 136 if err := os.Remove(darwin.servicePath()); err != nil { 137 return removeAction + failed, err 138 } 139 140 return removeAction + success, nil 141 } 142 143 // Start the service 144 func (darwin *darwinRecord) Start() (string, error) { 145 startAction := "Starting " + darwin.description + ":" 146 147 ok, err := checkPrivileges() 148 if !ok && darwin.kind != UserAgent { 149 return startAction + failed, err 150 } 151 152 if !darwin.isInstalled() { 153 return startAction + failed, ErrNotInstalled 154 } 155 156 if _, ok := darwin.checkRunning(); ok { 157 return startAction + failed, ErrAlreadyRunning 158 } 159 160 if err := exec.Command("launchctl", "load", darwin.servicePath()).Run(); err != nil { 161 return startAction + failed, err 162 } 163 164 return startAction + success, nil 165 } 166 167 // Stop the service 168 func (darwin *darwinRecord) Stop() (string, error) { 169 stopAction := "Stopping " + darwin.description + ":" 170 171 ok, err := checkPrivileges() 172 if !ok && darwin.kind != UserAgent { 173 return stopAction + failed, err 174 } 175 176 if !darwin.isInstalled() { 177 return stopAction + failed, ErrNotInstalled 178 } 179 180 if _, ok := darwin.checkRunning(); !ok { 181 return stopAction + failed, ErrAlreadyStopped 182 } 183 184 if err := exec.Command("launchctl", "unload", darwin.servicePath()).Run(); err != nil { 185 return stopAction + failed, err 186 } 187 188 return stopAction + success, nil 189 } 190 191 // Status - Get service status 192 func (darwin *darwinRecord) Status() (string, error) { 193 194 ok, err := checkPrivileges() 195 if !ok && darwin.kind != UserAgent { 196 return "", err 197 } 198 199 if !darwin.isInstalled() { 200 return statNotInstalled, ErrNotInstalled 201 } 202 203 statusAction, _ := darwin.checkRunning() 204 205 return statusAction, nil 206 } 207 208 // Run - Run service 209 func (darwin *darwinRecord) Run(e Executable) (string, error) { 210 runAction := "Running " + darwin.description + ":" 211 e.Start() 212 e.Run() 213 return runAction + " completed.", nil 214 } 215 216 // GetTemplate - gets service config template 217 func (linux *darwinRecord) GetTemplate() string { 218 return propertyList 219 } 220 221 // SetTemplate - sets service config template 222 func (linux *darwinRecord) SetTemplate(tplStr string) error { 223 propertyList = tplStr 224 return nil 225 } 226 227 var propertyList = `<?xml version="1.0" encoding="UTF-8"?> 228 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 229 <plist version="1.0"> 230 <dict> 231 <key>KeepAlive</key> 232 <true/> 233 <key>Label</key> 234 <string>{{.Name}}</string> 235 <key>ProgramArguments</key> 236 <array> 237 <string>{{.Path}}</string> 238 {{range .Args}}<string>{{.}}</string> 239 {{end}} 240 </array> 241 <key>RunAtLoad</key> 242 <true/> 243 <key>WorkingDirectory</key> 244 <string>/usr/local/var</string> 245 <key>StandardErrorPath</key> 246 <string>/usr/local/var/log/{{.Name}}.err</string> 247 <key>StandardOutPath</key> 248 <string>/usr/local/var/log/{{.Name}}.log</string> 249 </dict> 250 </plist> 251 `