Compare commits
6 Commits
b3cbca14c6
...
6c6c0123ed
Author | SHA1 | Date | |
---|---|---|---|
6c6c0123ed | |||
|
b7989893cd | ||
c84dc517a9 | |||
34ed9cc065 | |||
f03a0755af | |||
cc0176e058 |
@ -1,4 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Generated for serviceman. Edit as you wish, but leave this line. -->
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# Generated for serviceman. Edit as you wish, but leave this line.
|
||||||
# Pre-req
|
# Pre-req
|
||||||
# sudo mkdir -p {{ .Local }}/opt/{{ .Name }}/ {{ .Local }}/var/log/{{ .Name }}
|
# sudo mkdir -p {{ .Local }}/opt/{{ .Name }}/ {{ .Local }}/var/log/{{ .Name }}
|
||||||
{{ if .System -}}
|
{{ if .System -}}
|
||||||
|
@ -51,6 +51,10 @@ func Stop(conf *service.Service) error {
|
|||||||
return stop(conf)
|
return stop(conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func List(conf *service.Service) ([]string, []string, []error) {
|
||||||
|
return list(conf)
|
||||||
|
}
|
||||||
|
|
||||||
// IsPrivileged returns true if we suspect that the current user (or process) will be able
|
// IsPrivileged returns true if we suspect that the current user (or process) will be able
|
||||||
// to write to system folders, bind to privileged ports, and otherwise
|
// to write to system folders, bind to privileged ports, and otherwise
|
||||||
// successfully run a system service.
|
// successfully run a system service.
|
||||||
@ -67,6 +71,16 @@ func WhereIs(exe string) (string, error) {
|
|||||||
return filepath.Abs(filepath.ToSlash(exepath))
|
return filepath.Abs(filepath.ToSlash(exepath))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ManageError struct {
|
||||||
|
Name string
|
||||||
|
Hint string
|
||||||
|
Parent error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ManageError) Error() string {
|
||||||
|
return e.Name + ": " + e.Hint + ": " + e.Parent.Error()
|
||||||
|
}
|
||||||
|
|
||||||
type ErrDaemonize struct {
|
type ErrDaemonize struct {
|
||||||
DaemonArgs []string
|
DaemonArgs []string
|
||||||
error string
|
error string
|
||||||
|
@ -108,6 +108,7 @@ func stop(conf *service.Service) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render will create a launchd .plist file using the simple internal template
|
||||||
func Render(c *service.Service) ([]byte, error) {
|
func Render(c *service.Service) ([]byte, error) {
|
||||||
// Create service file from template
|
// Create service file from template
|
||||||
b, err := static.ReadFile("dist/Library/LaunchDaemons/_rdns_.plist.tmpl")
|
b, err := static.ReadFile("dist/Library/LaunchDaemons/_rdns_.plist.tmpl")
|
||||||
@ -142,7 +143,7 @@ func install(c *service.Service) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check paths first
|
// Check paths first
|
||||||
err := os.MkdirAll(filepath.Dir(plistDir), 0755)
|
err := os.MkdirAll(plistDir, 0755)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,7 @@ func stop(conf *service.Service) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render will create a systemd .service file using a simple the internal template
|
// Render will create a systemd .service file using the simple internal template
|
||||||
func Render(c *service.Service) ([]byte, error) {
|
func Render(c *service.Service) ([]byte, error) {
|
||||||
defaultUserGroup(c)
|
defaultUserGroup(c)
|
||||||
|
|
||||||
|
74
manager/install_nixes.go
Normal file
74
manager/install_nixes.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package manager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.rootprojects.org/root/go-serviceman/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
// this code is shared between Mac and Linux, but may diverge in the future
|
||||||
|
func list(c *service.Service) ([]string, []string, []error) {
|
||||||
|
confDir := srvSysPath
|
||||||
|
if !c.System {
|
||||||
|
confDir = filepath.Join(c.Home, srvUserPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enuser path exists
|
||||||
|
err := os.MkdirAll(confDir, 0755)
|
||||||
|
if nil != err {
|
||||||
|
return nil, nil, []error{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
fis, err := ioutil.ReadDir(confDir)
|
||||||
|
if nil != err {
|
||||||
|
return nil, nil, []error{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
managed := []string{}
|
||||||
|
others := []string{}
|
||||||
|
errs := []error{}
|
||||||
|
b := make([]byte, 256)
|
||||||
|
for i := range fis {
|
||||||
|
fi := fis[i]
|
||||||
|
if !strings.HasSuffix(strings.ToLower(fi.Name()), srvExt) || len(fi.Name()) <= srvLen {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
confFile := filepath.Join(confDir, fi.Name())
|
||||||
|
r, err := os.Open(confFile)
|
||||||
|
if nil != err {
|
||||||
|
errs = append(errs, &ManageError{
|
||||||
|
Name: confFile,
|
||||||
|
Hint: "Open file",
|
||||||
|
Parent: err,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := r.Read(b)
|
||||||
|
if nil != err {
|
||||||
|
errs = append(errs, &ManageError{
|
||||||
|
Name: confFile,
|
||||||
|
Hint: "Read file",
|
||||||
|
Parent: err,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
b = b[:n]
|
||||||
|
|
||||||
|
name := fi.Name()[:len(fi.Name())-srvLen]
|
||||||
|
if bytes.Contains(b, []byte("for serviceman.")) {
|
||||||
|
managed = append(managed, name)
|
||||||
|
} else {
|
||||||
|
others = append(others, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return managed, others, errs
|
||||||
|
}
|
@ -130,6 +130,48 @@ func stop(conf *service.Service) error {
|
|||||||
return runner.Stop(conf)
|
return runner.Stop(conf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func list(c *service.Service) ([]string, []string, []error) {
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
regs, err := listRegistry(c)
|
||||||
|
if nil != err {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfgs, errors := listConfigs(c)
|
||||||
|
if 0 != len(errors) {
|
||||||
|
errs = append(errs, errors...)
|
||||||
|
}
|
||||||
|
|
||||||
|
managed := []string{}
|
||||||
|
for i := range cfgs {
|
||||||
|
managed = append(managed, cfgs[i].Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
others := []string{}
|
||||||
|
for i := range regs {
|
||||||
|
reg := regs[i]
|
||||||
|
if 0 == len(cfgs) {
|
||||||
|
others = append(others, reg)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var found bool
|
||||||
|
for j := range cfgs {
|
||||||
|
cfg := cfgs[j]
|
||||||
|
// Registry Value Names are case-insensitive
|
||||||
|
if strings.ToLower(reg) == strings.ToLower(cfg.Title) {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
others = append(others, reg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return managed, others, errs
|
||||||
|
}
|
||||||
|
|
||||||
func getRunnerArgs(c *service.Service) []string {
|
func getRunnerArgs(c *service.Service) []string {
|
||||||
self := os.Args[0]
|
self := os.Args[0]
|
||||||
debug := ""
|
debug := ""
|
||||||
@ -154,6 +196,84 @@ func getRunnerArgs(c *service.Service) []string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type winConf struct {
|
||||||
|
Filename string `json:"-"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func listConfigs(c *service.Service) ([]winConf, []error) {
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
smdir := `\opt\serviceman`
|
||||||
|
if !c.System {
|
||||||
|
smdir = filepath.Join(c.Home, ".local", smdir)
|
||||||
|
}
|
||||||
|
confpath := filepath.Join(smdir, `etc`)
|
||||||
|
|
||||||
|
infos, err := ioutil.ReadDir(confpath)
|
||||||
|
if nil != err {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
errs = append(errs, &ManageError{
|
||||||
|
Name: confpath,
|
||||||
|
Hint: "Read directory",
|
||||||
|
Parent: err,
|
||||||
|
})
|
||||||
|
return nil, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO report active status
|
||||||
|
srvs := []winConf{}
|
||||||
|
for i := range infos {
|
||||||
|
filename := strings.ToLower(infos[i].Name())
|
||||||
|
if len(filename) <= srvLen || !strings.HasSuffix(filename, srvExt) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
name := filename[:len(filename)-srvLen]
|
||||||
|
b, err := ioutil.ReadFile(filepath.Join(confpath, filename))
|
||||||
|
if nil != err {
|
||||||
|
errs = append(errs, &ManageError{
|
||||||
|
Name: name,
|
||||||
|
Hint: "Read file",
|
||||||
|
Parent: err,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cfg := winConf{Filename: filename}
|
||||||
|
err = json.Unmarshal(b, &cfg)
|
||||||
|
if nil != err {
|
||||||
|
errs = append(errs, &ManageError{
|
||||||
|
Name: name,
|
||||||
|
Hint: "Parse JSON",
|
||||||
|
Parent: err,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
srvs = append(srvs, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return srvs, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func listRegistry(c *service.Service) ([]string, error) {
|
||||||
|
autorunKey := `SOFTWARE\Microsoft\Windows\CurrentVersion\Run`
|
||||||
|
k, _, err := registry.CreateKey(
|
||||||
|
registry.CURRENT_USER,
|
||||||
|
autorunKey,
|
||||||
|
registry.QUERY_VALUE,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer k.Close()
|
||||||
|
|
||||||
|
return k.ReadValueNames(-1)
|
||||||
|
}
|
||||||
|
|
||||||
// copies self to install path and returns config path
|
// copies self to install path and returns config path
|
||||||
func installServiceman(c *service.Service) ([]string, error) {
|
func installServiceman(c *service.Service) ([]string, error) {
|
||||||
// TODO check version and upgrade or dismiss
|
// TODO check version and upgrade or dismiss
|
||||||
|
File diff suppressed because one or more lines are too long
@ -30,6 +30,7 @@ func usage() {
|
|||||||
fmt.Println("\tserviceman <command> --help")
|
fmt.Println("\tserviceman <command> --help")
|
||||||
fmt.Println("\tserviceman add ./foo-app -- --foo-arg")
|
fmt.Println("\tserviceman add ./foo-app -- --foo-arg")
|
||||||
fmt.Println("\tserviceman run --config ./foo-app.json")
|
fmt.Println("\tserviceman run --config ./foo-app.json")
|
||||||
|
fmt.Println("\tserviceman list --all")
|
||||||
fmt.Println("\tserviceman start <name>")
|
fmt.Println("\tserviceman start <name>")
|
||||||
fmt.Println("\tserviceman stop <name>")
|
fmt.Println("\tserviceman stop <name>")
|
||||||
}
|
}
|
||||||
@ -54,6 +55,8 @@ func main() {
|
|||||||
start()
|
start()
|
||||||
case "stop":
|
case "stop":
|
||||||
stop()
|
stop()
|
||||||
|
case "list":
|
||||||
|
list()
|
||||||
default:
|
default:
|
||||||
fmt.Fprintf(os.Stderr, "Unknown argument %s\n", top)
|
fmt.Fprintf(os.Stderr, "Unknown argument %s\n", top)
|
||||||
usage()
|
usage()
|
||||||
@ -85,13 +88,6 @@ func add() {
|
|||||||
flag.StringVar(&conf.Group, "groupname", "", "run the service as this group")
|
flag.StringVar(&conf.Group, "groupname", "", "run the service as this group")
|
||||||
flag.BoolVar(&conf.PrivilegedPorts, "cap-net-bind", false, "this service should have access to privileged ports")
|
flag.BoolVar(&conf.PrivilegedPorts, "cap-net-bind", false, "this service should have access to privileged ports")
|
||||||
flag.BoolVar(&dryrun, "dryrun", false, "output the service file without modifying anything on disk")
|
flag.BoolVar(&dryrun, "dryrun", false, "output the service file without modifying anything on disk")
|
||||||
flag.Usage = func() {
|
|
||||||
fmt.Fprintf(os.Stderr, "Usage of %s:\n\n", os.Args[0])
|
|
||||||
|
|
||||||
flag.PrintDefaults()
|
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, "Flags and arguments after \"--\" will be completely ignored by serviceman\n", os.Args[0])
|
|
||||||
}
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
flagargs := flag.Args()
|
flagargs := flag.Args()
|
||||||
|
|
||||||
@ -310,7 +306,7 @@ func add() {
|
|||||||
servicemode = "SYSTEM"
|
servicemode = "SYSTEM"
|
||||||
}
|
}
|
||||||
fmt.Printf(
|
fmt.Printf(
|
||||||
"SUCCESS:\n\n\t%q started as a %q %s service, running as %q\n",
|
"SUCCESS:\n\n\t%q started as a %s %s service, running as %q\n",
|
||||||
conf.Name,
|
conf.Name,
|
||||||
servicetype,
|
servicetype,
|
||||||
servicemode,
|
servicemode,
|
||||||
@ -319,6 +315,62 @@ func add() {
|
|||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func list() {
|
||||||
|
var verbose bool
|
||||||
|
forUser := false
|
||||||
|
forSystem := false
|
||||||
|
flag.BoolVar(&forSystem, "system", false, "attempt to add system service as an unprivileged/unelevated user")
|
||||||
|
flag.BoolVar(&forUser, "user", false, "add user space / user mode service even when admin/root/sudo/elevated")
|
||||||
|
flag.BoolVar(&verbose, "all", false, "show all services (even those not managed by serviceman)")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if forUser && forSystem {
|
||||||
|
fmt.Println("Pfff! You can't --user AND --system! What are you trying to pull?")
|
||||||
|
os.Exit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conf := &service.Service{}
|
||||||
|
if forUser {
|
||||||
|
conf.System = false
|
||||||
|
} else if forSystem {
|
||||||
|
conf.System = true
|
||||||
|
} else {
|
||||||
|
conf.System = manager.IsPrivileged()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pretty much just for HomeDir
|
||||||
|
conf.NormalizeWithoutPath()
|
||||||
|
|
||||||
|
managed, others, errs := manager.List(conf)
|
||||||
|
for i := range errs {
|
||||||
|
fmt.Fprintf(os.Stderr, "possible error: %s\n", errs[i])
|
||||||
|
}
|
||||||
|
if len(errs) > 0 {
|
||||||
|
fmt.Fprintf(os.Stderr, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("serviceman-managed services:\n")
|
||||||
|
for i := range managed {
|
||||||
|
fmt.Println("\t" + managed[i])
|
||||||
|
}
|
||||||
|
if 0 == len(managed) {
|
||||||
|
fmt.Println("\t(none)")
|
||||||
|
}
|
||||||
|
fmt.Println("")
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
fmt.Println("other services:\n")
|
||||||
|
for i := range others {
|
||||||
|
fmt.Println("\t" + others[i])
|
||||||
|
}
|
||||||
|
if 0 == len(others) {
|
||||||
|
fmt.Println("\t(none)")
|
||||||
|
}
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func findExec(exe string, force bool) (string, error) {
|
func findExec(exe string, force bool) (string, error) {
|
||||||
// ex: node => /usr/local/bin/node
|
// ex: node => /usr/local/bin/node
|
||||||
// ex: ./demo.js => /Users/aj/project/demo.js
|
// ex: ./demo.js => /Users/aj/project/demo.js
|
||||||
|
Loading…
x
Reference in New Issue
Block a user