Skip to content

Commit

Permalink
Merge branch 'release/0.10.3'
Browse files Browse the repository at this point in the history
  • Loading branch information
takama committed Mar 17, 2018
2 parents 57605ba + 904cd17 commit 29a7daa
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 24 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ See `examples/cron/cron_job.go`
- [0X8C - Demired](https://github.com/Demired)
- [Maximus](https://github.com/maximus12793)
- [AlgorathDev](https://github.com/AlgorathDev)
- [Alexis Camilleri](https://github.com/krysennn)

All the contributors are welcome. If you would like to be the contributor please accept some rules.

Expand Down
2 changes: 1 addition & 1 deletion daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.

/*
Package daemon 0.10.2 for use with Go (golang) services.
Package daemon 0.10.3 for use with Go (golang) services.
Package daemon provides primitives for daemonization of golang services.
This package is not provide implementation of user daemon,
Expand Down
57 changes: 34 additions & 23 deletions daemon_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@ import (
"errors"
"fmt"
"os/exec"
"regexp"
"syscall"
"unicode/utf16"
"unsafe"
)

var ErrWindowsUnsupported = errors.New("Adding as a service failed. Download and place nssm.exe in the path to install this service as an service. NSSM url: https://nssm.cc/")

// windowsRecord - standard record (struct) for windows version of daemon package
type windowsRecord struct {
name string
Expand All @@ -31,59 +30,50 @@ func newDaemon(name, description string, dependencies []string) (Daemon, error)
// Install the service
func (windows *windowsRecord) Install(args ...string) (string, error) {
installAction := "Install " + windows.description + ":"
adminAccessNecessary := "Administrator access is needed to install a service."

execp, err := execPath()

if err != nil {
return installAction + failed, err
}

cmdArgs := []string{"install", windows.name, execp}
cmdArgs := []string{"create", windows.name, "start=auto", "binPath=" + execp}
cmdArgs = append(cmdArgs, args...)

cmd := exec.Command("nssm.exe", cmdArgs...)
out, err := cmd.Output()
cmd := exec.Command("sc", cmdArgs...)
_, err = cmd.Output()
if err != nil {
if len(out) > 0 {
fmt.Println(string(out))
} else {
fmt.Println("No output. Probably service already exists. Try uninstall first.")
}
return installAction + failed, err
}
if len(out) == 0 {
return adminAccessNecessary, errors.New(adminAccessNecessary)
return installAction + failed, getWindowsError(err)
}
return installAction + " completed.", nil
}

// Remove the service
func (windows *windowsRecord) Remove() (string, error) {
removeAction := "Removing " + windows.description + ":"
cmd := exec.Command("nssm.exe", "remove", windows.name, "confirm")
cmd := exec.Command("sc", "delete", windows.name, "confirm")
err := cmd.Run()
if err != nil {
return removeAction + failed, err
return removeAction + failed, getWindowsError(err)
}
return removeAction + " completed.", nil
}

// Start the service
func (windows *windowsRecord) Start() (string, error) {
startAction := "Starting " + windows.description + ":"
cmd := exec.Command("nssm.exe", "start", windows.name)
cmd := exec.Command("sc", "start", windows.name)
err := cmd.Run()
if err != nil {
return startAction + failed, err
return startAction + failed, getWindowsError(err)
}
return startAction + " completed.", nil
}

// Stop the service
func (windows *windowsRecord) Stop() (string, error) {
stopAction := "Stopping " + windows.description + ":"
cmd := exec.Command("nssm.exe", "stop", windows.name)
cmd := exec.Command("sc", "stop", windows.name)
err := cmd.Run()
if err != nil {
return stopAction + failed, err
Expand All @@ -93,12 +83,12 @@ func (windows *windowsRecord) Stop() (string, error) {

// Status - Get service status
func (windows *windowsRecord) Status() (string, error) {
cmd := exec.Command("nssm.exe", "status", windows.name)
cmd := exec.Command("sc", "query", windows.name)
out, err := cmd.Output()
if err != nil {
return "Getting status:" + failed, err
return "Getting status:" + failed, getWindowsError(err)
}
return "Status: " + string(out), nil
return "Status: " + "SERVICE_" + getWindowsServiceState(out), nil
}

// Get executable path
Expand All @@ -118,3 +108,24 @@ func execPath() (string, error) {
}
return string(utf16.Decode(b[0:n])), nil
}

// Get windows error
func getWindowsError(inputError error) error {
if exiterr, ok := inputError.(*exec.ExitError); ok {
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
if sysErr, ok := WinErrCode[status.ExitStatus()]; ok {
return errors.New(fmt.Sprintf("\n %s: %s \n %s", sysErr.Title, sysErr.Description, sysErr.Action))
}
}
}

return inputError
}

// Get windows service state
func getWindowsServiceState(out []byte) string {
regex := regexp.MustCompile("STATE.*: (?P<state_code>[0-9]) (?P<state>.*) ")
service := regex.FindAllStringSubmatch(string(out), -1)[0]

return service[2]
}
128 changes: 128 additions & 0 deletions helper_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by
// license that can be found in the LICENSE file.

package daemon

// SystemError contains error description and corresponded action helper to fix it
type SystemError struct {
Title string
Description string
Action string
}

var (
// WinErrCode - List of system errors from Microsoft source:
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms681385(v=vs.85).aspx
WinErrCode = map[int]SystemError{
5: SystemError{
Title: "ERROR_ACCESS_DENIED",
Description: "Access denied.",
Action: "Administrator access is needed to install a service.",
},
1051: SystemError{
Title: "ERROR_DEPENDENT_SERVICES_RUNNING",
Description: "A stop control has been sent to a service that other running services are dependent on.",
},
1052: SystemError{
Title: "ERROR_INVALID_SERVICE_CONTROL",
Description: "The requested control is not valid for this service.",
},
1053: SystemError{
Title: "ERROR_SERVICE_REQUEST_TIMEOUT",
Description: "The service did not respond to the start or control request in a timely fashion.",
},
1054: SystemError{
Title: "ERROR_SERVICE_NO_THREAD",
Description: "A thread could not be created for the service.",
},
1055: SystemError{
Title: "ERROR_SERVICE_DATABASE_LOCKED",
Description: "The service database is locked.",
},
1056: SystemError{
Title: "ERROR_SERVICE_ALREADY_RUNNING",
Description: "An instance of the service is already running.",
},
1057: SystemError{
Title: "ERROR_INVALID_SERVICE_ACCOUNT",
Description: "The account name is invalid or does not exist, or the password is invalid for the account name specified.",
},
1058: SystemError{
Title: "ERROR_SERVICE_DISABLED",
Description: "The service cannot be started, either because it is disabled or because it has no enabled devices associated with it.",
},
1060: SystemError{
Title: "ERROR_SERVICE_DOES_NOT_EXIST",
Description: "The specified service does not exist as an installed service.",
},
1061: SystemError{
Title: "ERROR_SERVICE_CANNOT_ACCEPT_CTRL",
Description: "The service cannot accept control messages at this time.",
},
1062: SystemError{
Title: "ERROR_SERVICE_NOT_ACTIVE",
Description: "The service has not been started.",
},
1063: SystemError{
Title: "ERROR_FAILED_SERVICE_CONTROLLER_CONNECT",
Description: "The service process could not connect to the service controller.",
},
1064: SystemError{
Title: "ERROR_EXCEPTION_IN_SERVICE",
Description: "An exception occurred in the service when handling the control request.",
},
1066: SystemError{
Title: "ERROR_SERVICE_SPECIFIC_ERROR",
Description: "The service has returned a service-specific error code.",
},
1068: SystemError{
Title: "ERROR_SERVICE_DEPENDENCY_FAIL",
Description: "The dependency service or group failed to start.",
},
1069: SystemError{
Title: "ERROR_SERVICE_LOGON_FAILED",
Description: "The service did not start due to a logon failure.",
},
1070: SystemError{
Title: "ERROR_SERVICE_START_HANG",
Description: "After starting, the service hung in a start-pending state.",
},
1071: SystemError{
Title: "ERROR_INVALID_SERVICE_LOCK",
Description: "The specified service database lock is invalid.",
},
1072: SystemError{
Title: "ERROR_SERVICE_MARKED_FOR_DELETE",
Description: "The specified service has been marked for deletion.",
},
1073: SystemError{
Title: "ERROR_SERVICE_EXISTS",
Description: "The specified service already exists.",
},
1075: SystemError{
Title: "ERROR_SERVICE_DEPENDENCY_DELETED",
Description: "The dependency service does not exist or has been marked for deletion.",
},
1077: SystemError{
Title: "ERROR_SERVICE_NEVER_STARTED",
Description: "No attempts to start the service have been made since the last boot.",
},
1078: SystemError{
Title: "ERROR_DUPLICATE_SERVICE_NAME",
Description: "The name is already in use as either a service name or a service display name.",
},
1079: SystemError{
Title: "ERROR_DIFFERENT_SERVICE_ACCOUNT",
Description: "The account specified for this service is different from the account specified for other services running in the same process.",
},
1083: SystemError{
Title: "ERROR_SERVICE_NOT_IN_EXE",
Description: "The executable program that this service is configured to run in does not implement the service.",
},
1084: SystemError{
Title: "ERROR_NOT_SAFEBOOT_SERVICE",
Description: "This service cannot be started in Safe Mode.",
},
}
)

0 comments on commit 29a7daa

Please sign in to comment.