From fa6d7afa054cbbfb543c5d95e0fe3f0acf9da799 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Wed, 3 Jul 2019 02:11:50 -0600 Subject: [PATCH] some windows fixes --- README.md | 18 ++++++++++++++++++ installer/install.go | 24 ++++++++---------------- installer/install_notwindows.go | 18 ------------------ installer/install_windows.go | 30 ++++++++---------------------- runner/runner.go | 22 ++++++++++++++++------ runner/runner_windows.go | 12 ++++++++++++ serviceman.go | 4 ++-- 7 files changed, 64 insertions(+), 64 deletions(-) delete mode 100644 installer/install_notwindows.go create mode 100644 runner/runner_windows.go diff --git a/README.md b/README.md index 54c9332..6c2c600 100644 --- a/README.md +++ b/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 +``` \ No newline at end of file diff --git a/installer/install.go b/installer/install.go index 02d21a2..18ffd4d 100644 --- a/installer/install.go +++ b/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)) } diff --git a/installer/install_notwindows.go b/installer/install_notwindows.go deleted file mode 100644 index bf3a07e..0000000 --- a/installer/install_notwindows.go +++ /dev/null @@ -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 -} diff --git a/installer/install_windows.go b/installer/install_windows.go index 11d7c20..2c2554c 100644 --- a/installer/install_windows.go +++ b/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 -} diff --git a/runner/runner.go b/runner/runner.go index fb86f7b..600db19 100644 --- a/runner/runner.go +++ b/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 } diff --git a/runner/runner_windows.go b/runner/runner_windows.go new file mode 100644 index 0000000..bd5b0bc --- /dev/null +++ b/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"} + } +} \ No newline at end of file diff --git a/serviceman.go b/serviceman.go index 75c2002..9d40a6e 100644 --- a/serviceman.go +++ b/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 }