Browse Source

some windows fixes

smaller-size
AJ ONeal 5 years ago
parent
commit
fa6d7afa05
  1. 18
      README.md
  2. 24
      installer/install.go
  3. 18
      installer/install_notwindows.go
  4. 30
      installer/install_windows.go
  5. 22
      runner/runner.go
  6. 12
      runner/runner_windows.go
  7. 4
      serviceman.go

18
README.md

@ -15,3 +15,21 @@ serviceman install --user ./foo-app -- -c ./
```bash
serviceman install --user /usr/local/bin/node ./whatever.js -- -c ./
```
```bash
serviceman run --config conf.json
```
```json
{
"interpreter": "/Program Files (x86)/node/node.exe",
"exec": "/Users/aj/demo/demo.js",
"argv": ["--foo", "bar", "--baz", "qux"]
}
```
```bash
go generate -mod=vendor ./...
go build -mod=vendor -ldflags "-H=windowsgui"
.\\go-serviceman node ./demo.js -- --foo bar --baz qux
```

24
installer/install.go

@ -5,8 +5,8 @@ package installer
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"git.rootprojects.org/root/go-serviceman/service"
)
@ -42,26 +42,18 @@ func Install(c *service.Service) error {
return nil
}
// 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
// successfully run a system service.
func IsPrivileged() bool {
return isPrivileged()
}
func WhereIs(exec string) (string, error) {
// TODO use exec.LookPath instead
exec = filepath.ToSlash(exec)
if strings.Contains(exec, "/") {
// it's a path (so we don't allow filenames with slashes)
stat, err := os.Stat(exec)
if nil != err {
return "", err
}
if stat.IsDir() {
return "", fmt.Errorf("'%s' is not an executable file", exec)
}
return filepath.Abs(exec)
// WhereIs uses exec.LookPath to return an absolute filepath with forward slashes
func WhereIs(exe string) (string, error) {
exepath, err := exec.LookPath(exe)
if nil != err {
return "", err
}
return whereIs(exec)
return filepath.Abs(filepath.ToSlash(exepath))
}

18
installer/install_notwindows.go

@ -1,18 +0,0 @@
// +build !windows
package installer
import (
"os/exec"
"strings"
)
func whereIs(exe string) (string, error) {
// TODO use exec.LookPath instead
cmd := exec.Command("command", "-v", exe)
out, err := cmd.Output()
if nil != err {
return "", err
}
return strings.TrimSpace(string(out)), nil
}

30
installer/install_windows.go

@ -6,7 +6,6 @@ import (
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
@ -77,24 +76,21 @@ func install(c *service.Service) error {
// it can be "the main thing"
bin = exec
}
if 0 != len(args) {
// On Windows the /c acts kinda like -- does on *nix,
// at least for commands in the registry that have arguments
setArgs = ` /c `
}
// The final string ends up looking something like one of these:
// "C:\Users\aj\.local\opt\appname\appname.js /c -p 8080"
// "C:\Program Files (x64)\nodejs\node.exe /c C:\Users\aj\.local\opt\appname\appname.js -p 8080"
// `"C:\Users\aj\.local\opt\appname\appname.js" -p 8080`
// `"C:\Program Files (x64)\nodejs\node.exe" C:\Users\aj\.local\opt\appname\appname.js -p 8080`
regSZ := bin + setArgs + strings.Join(c.Argv, " ")
*/
regSZ := fmt.Sprintf("%s /c %s", args[0], strings.Join(args[1:], " "))
regSZ := fmt.Sprintf(`"%s" %s`, args[0], strings.Join(args[1:], " "))
if len(regSZ) > 260 {
return fmt.Errorf("data value is too long for registry entry")
}
fmt.Println("Set Registry Key:")
fmt.Println(autorunKey, c.Title, regSZ)
// In order for a windows gui program to not show a console,
// it has to not output any messages?
//fmt.Println("Set Registry Key:")
//fmt.Println(autorunKey, c.Title, regSZ)
k.SetStringValue(c.Title, regSZ)
return nil
@ -108,7 +104,7 @@ func installServiceman(c *service.Service) ([]string, error) {
// TODO support service level services (which probably wouldn't need serviceman)
smdir = filepath.Join(c.Home, ".local", smdir)
// for now we'll scope the runner to the name of the application
smbin := filepath.Join(smdir, `bin\serviceman.`+c.Name)
smbin := filepath.Join(smdir, `bin\serviceman.`+c.Name+`.exe`)
if smbin != self {
err := os.MkdirAll(filepath.Dir(smbin), 0755)
@ -148,13 +144,3 @@ func installServiceman(c *service.Service) ([]string, error) {
conffile,
}, nil
}
func whereIs(exe string) (string, error) {
// TODO use exec.LookPath instead
cmd := exec.Command("where.exe", exe)
out, err := cmd.Output()
if nil != err {
return "", err
}
return strings.TrimSpace(string(out)), nil
}

22
runner/runner.go

@ -5,11 +5,15 @@ import (
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"git.rootprojects.org/root/go-serviceman/service"
)
// Filled in on init by runner_windows.go
var shellArgs = []string{}
// Notes on spawning a child process
// https://groups.google.com/forum/#!topic/golang-nuts/shST-SDqIp4
@ -30,11 +34,17 @@ func Run(conf *service.Service) {
}
args = append(args, conf.Argv...)
if !conf.System && 0 != len(shellArgs) {
nargs := append(shellArgs[1:], binpath)
args = append(nargs, args...)
binpath = shellArgs[0]
}
for {
// setup the log
lf, err := os.OpenFile(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if nil != err {
fmt.Fprintf(os.Stderr, "Could not open log file %q\n", logfile)
fmt.Fprintf(os.Stderr, "[%s] Could not open log file %q\n", time.Now(), logfile)
lf = os.Stderr
} else {
defer lf.Close()
@ -42,6 +52,8 @@ func Run(conf *service.Service) {
start := time.Now()
cmd := exec.Command(binpath, args...)
fmt.Fprintf(lf, "[%s] Starting %q %s \n", time.Now(), binpath, strings.Join(args, " "))
cmd.Stdin = nil
cmd.Stdout = lf
cmd.Stderr = lf
@ -50,20 +62,18 @@ func Run(conf *service.Service) {
}
err = cmd.Start()
if nil != err {
fmt.Fprintf(lf, "Could not start %q process: %s\n", conf.Name, err)
fmt.Fprintf(lf, "[%s] Could not start %q process: %s\n", time.Now(), conf.Name, err)
} else {
err = cmd.Wait()
if nil != err {
fmt.Fprintf(lf, "Process %q failed with error: %s\n", conf.Name, err)
fmt.Fprintf(lf, "[%s] Process %q failed with error: %s\n", time.Now(), conf.Name, err)
} else {
fmt.Fprintf(lf, "Process %q exited cleanly\n", conf.Name)
fmt.Printf("Process %q exited cleanly\n", conf.Name)
fmt.Fprintf(lf, "[%s] Process %q exited cleanly\n", time.Now(), conf.Name)
}
}
// if this is a oneshot... so it is
if !conf.Restart {
fmt.Printf("Not restarting %q because `restart` set to `false`\n", conf.Name)
fmt.Fprintf(lf, "Not restarting %q because `restart` set to `false`\n", conf.Name)
break
}

12
runner/runner_windows.go

@ -0,0 +1,12 @@
package runner
import (
"os/exec"
)
func init() {
cmd, _ := exec.LookPath("cmd.exe")
if "" != cmd {
shellArgs = []string{cmd, "/c"}
}
}

4
serviceman.go

@ -178,7 +178,7 @@ func run() {
}
s.Normalize(false)
fmt.Fprintf(os.Stdout, "Logdir: %s\n", s.Logdir)
//fmt.Fprintf(os.Stdout, "Logdir: %s\n", s.Logdir)
err = os.MkdirAll(s.Logdir, 0755)
if nil != err {
fmt.Fprintf(os.Stderr, "%s\n", err)
@ -186,7 +186,7 @@ func run() {
}
if !daemonize {
fmt.Fprintf(os.Stdout, "Running %s %s %s\n", s.Interpreter, s.Exec, strings.Join(s.Argv, " "))
//fmt.Fprintf(os.Stdout, "Running %s %s %s\n", s.Interpreter, s.Exec, strings.Join(s.Argv, " "))
runner.Run(s)
return
}

Loading…
Cancel
Save