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  `