Compare commits
4 Commits
00749b3465
...
d610b8cb61
Author | SHA1 | Date | |
---|---|---|---|
d610b8cb61 | |||
|
75ae3eea07 | ||
|
04e162b20f | ||
2b7148c3ae |
23
README.md
23
README.md
@ -172,6 +172,7 @@ move serviceman.exe %userprofile%\bin\serviceman.exe
|
|||||||
**All Others**
|
**All Others**
|
||||||
|
|
||||||
```
|
```
|
||||||
|
chmod a+x ./serviceman
|
||||||
sudo mv ./serviceman /usr/local/bin/
|
sudo mv ./serviceman /usr/local/bin/
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -364,9 +365,9 @@ Where `conf.json` looks something like
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"title": "Demo",
|
"title": "Demo",
|
||||||
"exec": "/Users/me/go-demo/demo",
|
"exec": "/Users/me/go-demo/demo",
|
||||||
"argv": ["--foo", "bar", "--baz", "qux"]
|
"argv": ["--foo", "bar", "--baz", "qux"]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -380,10 +381,10 @@ names and relative paths.
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"title": "Demo",
|
"title": "Demo",
|
||||||
"interpreter": "node.exe",
|
"interpreter": "node.exe",
|
||||||
"exec": "./bin/demo.js",
|
"exec": "./bin/demo.js",
|
||||||
"argv": ["--foo", "bar", "--baz", "qux"]
|
"argv": ["--foo", "bar", "--baz", "qux"]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -391,12 +392,12 @@ That's equivalent to this:
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"title": "Demo",
|
"title": "Demo",
|
||||||
|
|
||||||
"name": "demo",
|
"name": "demo",
|
||||||
|
|
||||||
"exec": "node.exe",
|
"exec": "node.exe",
|
||||||
"argv": ["./bin/demo.js", "--foo", "bar", "--baz", "qux"]
|
"argv": ["./bin/demo.js", "--foo", "bar", "--baz", "qux"]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -12,18 +12,20 @@
|
|||||||
# sudo journalctl {{ if not .System -}} --user {{ end -}} -xefu {{ .Name }}
|
# sudo journalctl {{ if not .System -}} --user {{ end -}} -xefu {{ .Name }}
|
||||||
|
|
||||||
[Unit]
|
[Unit]
|
||||||
Description={{ .Title }} - {{ .Desc }}
|
Description={{ .Title }} {{ if .Desc }}- {{ .Desc }}{{ end }}
|
||||||
|
{{ if .URL -}}
|
||||||
Documentation={{ .URL }}
|
Documentation={{ .URL }}
|
||||||
|
{{ end -}}
|
||||||
{{ if .System -}}
|
{{ if .System -}}
|
||||||
After=network-online.target
|
After=network-online.target
|
||||||
Wants=network-online.target systemd-networkd-wait-online.service
|
Wants=network-online.target systemd-networkd-wait-online.service
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
|
{{ end -}}
|
||||||
[Service]
|
[Service]
|
||||||
# Restart on crash (bad signal), but not on 'clean' failure (error exit code)
|
# Restart on crash (bad signal), but not on 'clean' failure (error exit code)
|
||||||
# Allow up to 3 restarts within 10 seconds
|
# Allow up to 3 restarts within 10 seconds
|
||||||
# (it's unlikely that a user or properly-running script will do this)
|
# (it's unlikely that a user or properly-running script will do this)
|
||||||
Restart=on-abnormal
|
Restart=always
|
||||||
StartLimitInterval=10
|
StartLimitInterval=10
|
||||||
StartLimitBurst=3
|
StartLimitBurst=3
|
||||||
|
|
||||||
@ -36,7 +38,7 @@ Group={{ .Group }}
|
|||||||
{{ if .Workdir -}}
|
{{ if .Workdir -}}
|
||||||
WorkingDirectory={{ .Workdir }}
|
WorkingDirectory={{ .Workdir }}
|
||||||
{{ end -}}
|
{{ end -}}
|
||||||
ExecStart={{if .Interpreter }}{{ .Interpreter }} {{ end }}{{ .Exec }} {{- range $arg := .Argv }}{{ $arg }} {{- end }}
|
ExecStart={{if .Interpreter }}{{ .Interpreter }} {{ end }}{{ .Exec }}{{ range $arg := .Argv }} {{ $arg }}{{ end }}
|
||||||
ExecReload=/bin/kill -USR1 $MAINPID
|
ExecReload=/bin/kill -USR1 $MAINPID
|
||||||
|
|
||||||
{{if .Production -}}
|
{{if .Production -}}
|
||||||
|
@ -6,13 +6,75 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"git.rootprojects.org/root/go-serviceman/manager/static"
|
"git.rootprojects.org/root/go-serviceman/manager/static"
|
||||||
"git.rootprojects.org/root/go-serviceman/service"
|
"git.rootprojects.org/root/go-serviceman/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
srvExt = ".plist"
|
||||||
|
srvSysPath = "/Library/LaunchDaemons"
|
||||||
|
srvUserPath = "Library/LaunchAgents"
|
||||||
|
)
|
||||||
|
|
||||||
|
var srvLen int
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
srvLen = len(srvExt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func start(system bool, home string, name string) error {
|
||||||
|
sys, user, err := getMatchingSrvs(home, name)
|
||||||
|
if nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var service string
|
||||||
|
if system {
|
||||||
|
service, err = getOneSysSrv(sys, user, name)
|
||||||
|
if nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
service = filepath.Join(srvSysPath, service)
|
||||||
|
} else {
|
||||||
|
service, err = getOneUserSrv(home, sys, user, name)
|
||||||
|
if nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
service = filepath.Join(home, srvUserPath, service)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmds := []Runnable{
|
||||||
|
Runnable{
|
||||||
|
Exec: "launchctl",
|
||||||
|
Args: []string{"unload", "-w", service},
|
||||||
|
Must: false,
|
||||||
|
},
|
||||||
|
Runnable{
|
||||||
|
Exec: "launchctl",
|
||||||
|
Args: []string{"load", "-w", service},
|
||||||
|
Must: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmds = adjustPrivs(system, cmds)
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("Starting launchd service...")
|
||||||
|
for i := range cmds {
|
||||||
|
exe := cmds[i]
|
||||||
|
fmt.Println("\t" + exe.String())
|
||||||
|
err := exe.Run()
|
||||||
|
if nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func install(c *service.Service) error {
|
func install(c *service.Service) error {
|
||||||
// Darwin-specific config options
|
// Darwin-specific config options
|
||||||
if c.PrivilegedPorts {
|
if c.PrivilegedPorts {
|
||||||
@ -20,9 +82,9 @@ func install(c *service.Service) error {
|
|||||||
return fmt.Errorf("You must use root-owned LaunchDaemons (not user-owned LaunchAgents) to use priveleged ports on OS X")
|
return fmt.Errorf("You must use root-owned LaunchDaemons (not user-owned LaunchAgents) to use priveleged ports on OS X")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
plistDir := "/Library/LaunchDaemons/"
|
plistDir := srvSysPath
|
||||||
if !c.System {
|
if !c.System {
|
||||||
plistDir = filepath.Join(c.Home, "Library/LaunchAgents")
|
plistDir = filepath.Join(c.Home, srvUserPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check paths first
|
// Check paths first
|
||||||
@ -53,19 +115,18 @@ func install(c *service.Service) error {
|
|||||||
plistName := c.ReverseDNS + ".plist"
|
plistName := c.ReverseDNS + ".plist"
|
||||||
plistPath := filepath.Join(plistDir, plistName)
|
plistPath := filepath.Join(plistDir, plistName)
|
||||||
if err := ioutil.WriteFile(plistPath, rw.Bytes(), 0644); err != nil {
|
if err := ioutil.WriteFile(plistPath, rw.Bytes(), 0644); err != nil {
|
||||||
|
return fmt.Errorf("Error writing %s: %v", plistPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
return fmt.Errorf("ioutil.WriteFile error: %v", err)
|
// TODO --no-start
|
||||||
|
err = start(c.System, c.Home, c.ReverseDNS)
|
||||||
|
if nil != err {
|
||||||
|
fmt.Printf("If things don't go well you should be able to get additional logging from launchctl:\n")
|
||||||
|
fmt.Printf("\tsudo launchctl log level debug\n")
|
||||||
|
fmt.Printf("\ttail -f /var/log/system.log\n")
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
fmt.Printf("Installed. To start '%s' run the following:\n", c.Name)
|
|
||||||
// TODO template config file
|
|
||||||
if "" != c.Home {
|
|
||||||
plistPath = strings.Replace(plistPath, c.Home, "~", 1)
|
|
||||||
}
|
|
||||||
sudo := ""
|
|
||||||
if c.System {
|
|
||||||
sudo = "sudo "
|
|
||||||
}
|
|
||||||
fmt.Printf("\t%slaunchctl load -w %s\n", sudo, plistPath)
|
|
||||||
|
|
||||||
|
fmt.Printf("Added and started '%s' as a launchctl service.\n", c.Name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,102 @@ import (
|
|||||||
"git.rootprojects.org/root/go-serviceman/service"
|
"git.rootprojects.org/root/go-serviceman/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
srvLen int
|
||||||
|
srvExt = ".service"
|
||||||
|
srvSysPath = "/etc/systemd/system"
|
||||||
|
|
||||||
|
// Not sure which of these it's supposed to be...
|
||||||
|
// * ~/.local/share/systemd/user/watchdog.service
|
||||||
|
// * ~/.config/systemd/user/watchdog.service
|
||||||
|
// https://wiki.archlinux.org/index.php/Systemd/User
|
||||||
|
// This seems to work on Ubuntu
|
||||||
|
srvUserPath = ".config/systemd/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
srvLen = len(srvExt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func start(system bool, home string, name string) error {
|
||||||
|
sys, user, err := getMatchingSrvs(home, name)
|
||||||
|
if nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var service string
|
||||||
|
if system {
|
||||||
|
service, err = getOneSysSrv(sys, user, name)
|
||||||
|
if nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
service = filepath.Join(srvSysPath, service)
|
||||||
|
} else {
|
||||||
|
service, err = getOneUserSrv(home, sys, user, name)
|
||||||
|
if nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
service = filepath.Join(home, srvUserPath, service)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmds []Runnable
|
||||||
|
if system {
|
||||||
|
cmds = []Runnable{
|
||||||
|
Runnable{
|
||||||
|
Exec: "systemctl",
|
||||||
|
Args: []string{"daemon-reload"},
|
||||||
|
Must: false,
|
||||||
|
},
|
||||||
|
Runnable{
|
||||||
|
Exec: "systemctl",
|
||||||
|
Args: []string{"stop", name + ".service"},
|
||||||
|
Must: false,
|
||||||
|
},
|
||||||
|
Runnable{
|
||||||
|
Exec: "systemctl",
|
||||||
|
Args: []string{"start", name + ".service"},
|
||||||
|
Badwords: []string{"not found", "failed"},
|
||||||
|
Must: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cmds = []Runnable{
|
||||||
|
Runnable{
|
||||||
|
Exec: "systemctl",
|
||||||
|
Args: []string{"--user", "daemon-reload"},
|
||||||
|
Must: false,
|
||||||
|
},
|
||||||
|
Runnable{
|
||||||
|
Exec: "systemctl",
|
||||||
|
Args: []string{"stop", "--user", name + ".service"},
|
||||||
|
Must: false,
|
||||||
|
},
|
||||||
|
Runnable{
|
||||||
|
Exec: "systemctl",
|
||||||
|
Args: []string{"start", "--user", name + ".service"},
|
||||||
|
Badwords: []string{"not found", "failed"},
|
||||||
|
Must: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmds = adjustPrivs(system, cmds)
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("Starting systemd service unit...")
|
||||||
|
for i := range cmds {
|
||||||
|
exe := cmds[i]
|
||||||
|
fmt.Println("\t" + exe.String())
|
||||||
|
err := exe.Run()
|
||||||
|
if nil != err {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func install(c *service.Service) error {
|
func install(c *service.Service) error {
|
||||||
// Linux-specific config options
|
// Linux-specific config options
|
||||||
if c.System {
|
if c.System {
|
||||||
@ -22,17 +118,12 @@ func install(c *service.Service) error {
|
|||||||
if "" == c.Group {
|
if "" == c.Group {
|
||||||
c.Group = c.User
|
c.Group = c.User
|
||||||
}
|
}
|
||||||
serviceDir := "/etc/systemd/system/"
|
|
||||||
|
|
||||||
// Check paths first
|
// Check paths first
|
||||||
serviceName := c.Name + ".service"
|
serviceDir := srvSysPath
|
||||||
if !c.System {
|
if !c.System {
|
||||||
// Not sure which of these it's supposed to be...
|
serviceDir = filepath.Join(c.Home, srvUserPath)
|
||||||
// * ~/.local/share/systemd/user/watchdog.service
|
err := os.MkdirAll(serviceDir, 0755)
|
||||||
// * ~/.config/systemd/user/watchdog.service
|
|
||||||
// https://wiki.archlinux.org/index.php/Systemd/User
|
|
||||||
serviceDir = filepath.Join(c.Home, ".local/share/systemd/user")
|
|
||||||
err := os.MkdirAll(filepath.Dir(serviceDir), 0755)
|
|
||||||
if nil != err {
|
if nil != err {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -56,22 +147,27 @@ func install(c *service.Service) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write the file out
|
// Write the file out
|
||||||
|
serviceName := c.Name + ".service"
|
||||||
servicePath := filepath.Join(serviceDir, serviceName)
|
servicePath := filepath.Join(serviceDir, serviceName)
|
||||||
if err := ioutil.WriteFile(servicePath, rw.Bytes(), 0644); err != nil {
|
if err := ioutil.WriteFile(servicePath, rw.Bytes(), 0644); err != nil {
|
||||||
return fmt.Errorf("ioutil.WriteFile error: %v", err)
|
return fmt.Errorf("Error writing %s: %v", servicePath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO template this as well?
|
// TODO --no-start
|
||||||
userspace := ""
|
err = start(c.System, c.Home, c.Name)
|
||||||
sudo := "sudo "
|
if nil != err {
|
||||||
if !c.System {
|
sudo := ""
|
||||||
userspace = "--user "
|
// --user-unit rather than --user --unit for older systemd
|
||||||
sudo = ""
|
unit := "--user-unit"
|
||||||
|
if c.System {
|
||||||
|
sudo = "sudo "
|
||||||
|
unit = "--unit"
|
||||||
|
}
|
||||||
|
fmt.Printf("If things don't go well you should be able to get additional logging from journalctl:\n")
|
||||||
|
fmt.Printf("\t%sjournalctl -xe %s %s.service\n", sudo, unit, c.Name)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
fmt.Printf("System service installed as '%s'.\n", servicePath)
|
|
||||||
fmt.Printf("Run the following to start '%s':\n", c.Name)
|
fmt.Printf("Added and started '%s' as a systemd service.\n", c.Name)
|
||||||
fmt.Printf("\t" + sudo + "systemctl " + userspace + "daemon-reload\n")
|
|
||||||
fmt.Printf("\t"+sudo+"systemctl "+userspace+"restart %s.service\n", c.Name)
|
|
||||||
fmt.Printf("\t"+sudo+"journalctl "+userspace+"-xefu %s\n", c.Name)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,17 @@ import (
|
|||||||
"golang.org/x/sys/windows/registry"
|
"golang.org/x/sys/windows/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
srvLen int
|
||||||
|
srvExt = ".json"
|
||||||
|
srvSysPath = "/opt/serviceman/etc"
|
||||||
|
srvUserPath = ".local/opt/serviceman/etc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
srvLen = len(srvExt)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO nab some goodness from https://github.com/takama/daemon
|
// TODO nab some goodness from https://github.com/takama/daemon
|
||||||
|
|
||||||
// TODO system service requires elevated privileges
|
// TODO system service requires elevated privileges
|
||||||
|
221
manager/start.go
Normal file
221
manager/start.go
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
package manager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Runnable defines a command to run, along with its arguments,
|
||||||
|
// and whether or not failing to exit successfully matters.
|
||||||
|
// It also defines whether certains words must exist (or not exist)
|
||||||
|
// in its output, apart from existing successfully, to determine
|
||||||
|
// whether or not it was actually successful.
|
||||||
|
type Runnable struct {
|
||||||
|
Exec string
|
||||||
|
Args []string
|
||||||
|
Must bool
|
||||||
|
Keywords []string
|
||||||
|
Badwords []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Runnable) Run() error {
|
||||||
|
cmd := exec.Command(x.Exec, x.Args...)
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if !x.Must {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
good := true
|
||||||
|
str := string(out)
|
||||||
|
for j := range x.Keywords {
|
||||||
|
if !strings.Contains(str, x.Keywords[j]) {
|
||||||
|
good = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if good && 0 != len(x.Badwords) {
|
||||||
|
for j := range x.Badwords {
|
||||||
|
if "" != x.Badwords[j] && !strings.Contains(str, x.Badwords[j]) {
|
||||||
|
good = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nil != err {
|
||||||
|
var comment string
|
||||||
|
if len(x.Keywords) > 0 {
|
||||||
|
comment += "# output must match all of:\n"
|
||||||
|
comment += "# \t" + strings.Join(x.Keywords, "\n#\t") + "\n"
|
||||||
|
}
|
||||||
|
if len(x.Badwords) > 0 {
|
||||||
|
comment += "# output must not match any of:\n"
|
||||||
|
comment += "# \t" + strings.Join(x.Badwords, "\n#\t") + "\n"
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Failed to run %s %s\n%s\n%s\n", x.Exec, strings.Join(x.Args, " "), str, comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Runnable) String() string {
|
||||||
|
var must = "true"
|
||||||
|
|
||||||
|
if x.Must {
|
||||||
|
must = "exit"
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimSpace(fmt.Sprintf(
|
||||||
|
"%s %s || %s\n",
|
||||||
|
x.Exec,
|
||||||
|
strings.Join(x.Args, " "),
|
||||||
|
must,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSrvs(dir string) ([]string, error) {
|
||||||
|
plists := []string{}
|
||||||
|
|
||||||
|
infos, err := ioutil.ReadDir(dir)
|
||||||
|
if nil != err {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range infos {
|
||||||
|
x := infos[i]
|
||||||
|
fname := strings.ToLower(x.Name())
|
||||||
|
if strings.HasSuffix(fname, srvExt) {
|
||||||
|
plists = append(plists, x.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return plists, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSystemSrvs() ([]string, error) {
|
||||||
|
return getSrvs(srvSysPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUserSrvs(home string) ([]string, error) {
|
||||||
|
dir := filepath.Join(home, srvUserPath)
|
||||||
|
return getSrvs(dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterMatchingSrvs(plists []string, name string) []string {
|
||||||
|
filtered := []string{}
|
||||||
|
|
||||||
|
for i := range plists {
|
||||||
|
pname := plists[i]
|
||||||
|
lname := strings.ToLower(pname)
|
||||||
|
n := len(lname)
|
||||||
|
if strings.HasSuffix(lname[:n-srvLen], strings.ToLower(name)) {
|
||||||
|
filtered = append(filtered, pname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMatchingSrvs(home string, name string) ([]string, []string, error) {
|
||||||
|
sysPlists, err := getSystemSrvs()
|
||||||
|
if nil != err {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var userPlists []string
|
||||||
|
if "" != home {
|
||||||
|
userPlists, err = getUserSrvs(home)
|
||||||
|
if nil != err {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filterMatchingSrvs(sysPlists, name), filterMatchingSrvs(userPlists, name), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getExactSrvMatch(srvs []string, name string) string {
|
||||||
|
for i := range srvs {
|
||||||
|
srv := srvs[i]
|
||||||
|
n := len(srv)
|
||||||
|
if srv[:n-srvLen] == strings.ToLower(name) {
|
||||||
|
return srv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOneSysSrv(sys []string, user []string, name string) (string, error) {
|
||||||
|
service := getExactSrvMatch(user, name)
|
||||||
|
if "" != service {
|
||||||
|
return service, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var errstr string
|
||||||
|
// system service was wanted
|
||||||
|
n := len(sys)
|
||||||
|
switch {
|
||||||
|
case 0 == n:
|
||||||
|
errstr += fmt.Sprintf("Didn't find user service matching %q\n", name)
|
||||||
|
if 0 != len(user) {
|
||||||
|
errstr += fmt.Sprintf("Did you intend to run a user service instead?\n\t%s\n", strings.Join(user, "\n\t"))
|
||||||
|
}
|
||||||
|
case n > 1:
|
||||||
|
errstr += fmt.Sprintf("Found more than one matching service:\n\t%s\n", strings.Join(sys, "\n\t"))
|
||||||
|
default:
|
||||||
|
service = filepath.Join(srvSysPath, sys[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if "" != errstr {
|
||||||
|
return "", fmt.Errorf(errstr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return service, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOneUserSrv(home string, sys []string, user []string, name string) (string, error) {
|
||||||
|
service := getExactSrvMatch(user, name)
|
||||||
|
if "" != service {
|
||||||
|
return service, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var errstr string
|
||||||
|
// user service was wanted
|
||||||
|
n := len(user)
|
||||||
|
switch {
|
||||||
|
case 0 == n:
|
||||||
|
errstr += fmt.Sprintf("Didn't find user service matching %q\n", name)
|
||||||
|
if 0 != len(sys) {
|
||||||
|
errstr += fmt.Sprintf("Did you intend to run a system service instead?\n\t%s\n", strings.Join(sys, "\n\t"))
|
||||||
|
}
|
||||||
|
case n > 1:
|
||||||
|
errstr += fmt.Sprintf("Found more than one matching service:\n\t%s\n", strings.Join(user, "\n\t"))
|
||||||
|
default:
|
||||||
|
service = filepath.Join(home, srvUserPath, user[0]+srvExt)
|
||||||
|
}
|
||||||
|
|
||||||
|
if "" != errstr {
|
||||||
|
return "", fmt.Errorf(errstr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return service, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func adjustPrivs(system bool, cmds []Runnable) []Runnable {
|
||||||
|
if !system || isPrivileged() {
|
||||||
|
return cmds
|
||||||
|
}
|
||||||
|
|
||||||
|
sudos := cmds
|
||||||
|
cmds = []Runnable{}
|
||||||
|
for i := range sudos {
|
||||||
|
exe := sudos[i]
|
||||||
|
exe.Args = append([]string{exe.Exec}, exe.Args...)
|
||||||
|
exe.Exec = "sudo"
|
||||||
|
cmds = append(cmds, exe)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmds
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
@ -126,15 +126,17 @@ func add() {
|
|||||||
conf.Normalize(force)
|
conf.Normalize(force)
|
||||||
|
|
||||||
//fmt.Printf("\n%#v\n\n", conf)
|
//fmt.Printf("\n%#v\n\n", conf)
|
||||||
|
if conf.System && !manager.IsPrivileged() {
|
||||||
|
fmt.Fprintf(os.Stderr, "Warning: You may need to use 'sudo' to add %q as a privileged system service.\n", conf.Name)
|
||||||
|
}
|
||||||
|
|
||||||
err = manager.Install(conf)
|
err = manager.Install(conf)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
fmt.Fprintf(os.Stderr, "%s\n", err)
|
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||||
fmt.Fprintf(os.Stderr, "Use 'sudo' to add service as a privileged system service.\n")
|
|
||||||
fmt.Fprintf(os.Stderr, "Use '--user' to add service as an user service.\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Once started, logs will be found at:\n\t%s\n", conf.Logdir)
|
printLogMessage(conf)
|
||||||
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|
||||||
func run() {
|
func run() {
|
||||||
|
11
serviceman_darwin.go
Normal file
11
serviceman_darwin.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.rootprojects.org/root/go-serviceman/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
func printLogMessage(conf *service.Service) {
|
||||||
|
fmt.Printf("If all went well the logs should have been created at:\n\t%s\n", conf.Logdir)
|
||||||
|
}
|
26
serviceman_linux.go
Normal file
26
serviceman_linux.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.rootprojects.org/root/go-serviceman/manager"
|
||||||
|
"git.rootprojects.org/root/go-serviceman/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
func printLogMessage(conf *service.Service) {
|
||||||
|
sudo := ""
|
||||||
|
unit := "--unit"
|
||||||
|
if conf.System {
|
||||||
|
if !manager.IsPrivileged() {
|
||||||
|
sudo = "sudo"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unit = "--user-unit"
|
||||||
|
}
|
||||||
|
fmt.Println("If all went well you should be able to see some goodies in the logs:")
|
||||||
|
fmt.Printf("\t%sjournalctl -xe %s %s.service\n", sudo, unit, conf.Name)
|
||||||
|
if !conf.System {
|
||||||
|
fmt.Println("\nIf that's not the case, see https://unix.stackexchange.com/a/486566/45554.")
|
||||||
|
fmt.Println("(you may need to run `systemctl restart systemd-journald`)")
|
||||||
|
}
|
||||||
|
}
|
11
serviceman_windows.go
Normal file
11
serviceman_windows.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.rootprojects.org/root/go-serviceman/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
func printLogMessage(conf *service.Service) {
|
||||||
|
fmt.Printf("If all went well the logs should have been created at:\n\t%s\n", conf.Logdir)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user