-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
806ae62
commit 391dc1a
Showing
11 changed files
with
931 additions
and
205 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package command | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"os/exec" | ||
"path" | ||
"time" | ||
|
||
"github.com/matthiasng/service-shark/service" | ||
) | ||
|
||
type Config struct { | ||
Name string | ||
Arguments []string | ||
} | ||
|
||
type Host struct { | ||
CmdConfig Config | ||
LogDirecotry string | ||
cmd *exec.Cmd | ||
logFile *os.File | ||
quitSignal chan struct{} | ||
quitCompleted chan struct{} | ||
} | ||
|
||
func (h *Host) Init(env service.Environment) error { | ||
h.cmd = exec.Command(h.CmdConfig.Name, h.CmdConfig.Arguments...) | ||
|
||
if env.IsWindowsService() { | ||
err := os.MkdirAll(h.LogDirecotry, os.ModePerm) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
name := "" // #todo | ||
|
||
logFileName := fmt.Sprintf("%s_%s.log", name, time.Now().Format("02-01-2006_15-04-05")) | ||
logFilePath := path.Join(h.LogDirecotry, logFileName) | ||
|
||
logFile, err := os.Create(logFilePath) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
h.cmd.Stdout = logFile | ||
h.cmd.Stderr = logFile | ||
} else { | ||
h.cmd.Stdout = os.Stdout | ||
h.cmd.Stderr = os.Stderr | ||
} | ||
|
||
return h.cmd.Start() | ||
} | ||
|
||
func (h *Host) Start() error { | ||
h.quitSignal = make(chan struct{}) | ||
h.quitCompleted = make(chan struct{}) | ||
|
||
go func() { | ||
<-h.quitSignal | ||
_ = h.cmd.Process.Kill() // #todo kill child process. Test Command -> "C:/Program Files/PowerShell/7-preview/preview/pwsh-preview.cmd" | ||
close(h.quitCompleted) | ||
}() | ||
|
||
go func() { | ||
_ = h.cmd.Wait() | ||
os.Exit(1) // service command stopped -> stop service | ||
}() | ||
|
||
return nil | ||
} | ||
|
||
func (h *Host) Stop() error { | ||
close(h.quitSignal) | ||
<-h.quitCompleted | ||
|
||
_ = h.logFile.Close() | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,5 @@ | ||
module github.com/matthiasng/service-wrapper | ||
module github.com/matthiasng/service-shark | ||
|
||
go 1.13 | ||
|
||
require ( | ||
github.com/akamensky/argparse v0.0.0-20191006154803-1427fe674291 | ||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 | ||
) | ||
require golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,2 @@ | ||
github.com/akamensky/argparse v0.0.0-20191006154803-1427fe674291 h1:EQ9p1v9+urDzbGQfAcBf9fGuDtYqFNE7YJHZ54TWPTk= | ||
github.com/akamensky/argparse v0.0.0-20191006154803-1427fe674291/go.mod h1:pdh+2piXurh466J9tqIqq39/9GO2Y8nZt6Cxzu18T9A= | ||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU= | ||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e h1:9vRrk9YW2BTzLP0VCB9ZDjU4cPqkg+IDWL7XgxA1yxQ= | ||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,86 +1,52 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"os" | ||
"path" | ||
"time" | ||
|
||
"github.com/akamensky/argparse" | ||
"github.com/matthiasng/service-wrapper/cli" | ||
"github.com/matthiasng/service-wrapper/service" | ||
"golang.org/x/sys/windows/svc" | ||
"github.com/matthiasng/service-shark/command" | ||
"github.com/matthiasng/service-shark/service" | ||
) | ||
|
||
func main() { | ||
parser := argparse.NewParser("service-wrapper", `Run a "-command" with "-arguments" as service`) | ||
|
||
serviceName := parser.String("n", "name", &argparse.Options{ | ||
Required: true, | ||
Help: "Servicename", | ||
}) | ||
logDirectory := parser.String("l", "logdirectory", &argparse.Options{ | ||
Required: true, | ||
Help: "Log directory", | ||
}) | ||
command := parser.String("c", "command", &argparse.Options{ | ||
Required: true, | ||
Help: "Command", | ||
}) | ||
arguments := parser.List("a", "arg", &argparse.Options{ | ||
Required: true, | ||
Help: `Command arguments. Example: '... -a "-key" -a "value" -a "--key2" -a "value"'`, | ||
}) | ||
|
||
err := parser.Parse(os.Args) | ||
if err != nil { | ||
fmt.Print(parser.Usage(err)) | ||
os.Exit(2) | ||
} | ||
|
||
// | ||
isAnInteractiveSession, err := svc.IsAnInteractiveSession() | ||
if err != nil { | ||
log.Fatalf("failed to determine if we are running in an interactive session: %v", err) | ||
} | ||
|
||
// | ||
logger := service.Logger{ | ||
Stdout: os.Stdout, | ||
Stderr: os.Stderr, | ||
} | ||
|
||
if !isAnInteractiveSession { | ||
err = os.MkdirAll(*logDirectory, os.ModePerm) | ||
if err != nil { | ||
log.Fatalf("Error - os.MkdirAll(%s, os.ModePerm): %v", *logDirectory, err) | ||
} | ||
// parser := argparse.NewParser("service-wrapper", `Run a "-command" with "-arguments" as service`) | ||
|
||
// serviceName := parser.String("n", "name", &argparse.Options{ | ||
// Required: true, | ||
// Help: "Servicename", | ||
// }) | ||
// logDirectory := parser.String("l", "logdirectory", &argparse.Options{ | ||
// Required: true, | ||
// Help: "Log directory", | ||
// }) | ||
// command := parser.String("c", "command", &argparse.Options{ | ||
// Required: true, | ||
// Help: "Command", | ||
// }) | ||
// arguments := parser.List("a", "arg", &argparse.Options{ | ||
// Required: true, | ||
// Help: `Command arguments. Example: '... -a "-key" -a "value" -a "--key2" -a "value"'`, | ||
// }) | ||
|
||
// err := parser.Parse(os.Args) | ||
// if err != nil { | ||
// fmt.Print(parser.Usage(err)) | ||
// os.Exit(2) | ||
// } | ||
|
||
// cli.BindArguments(*arguments), | ||
|
||
logFileName := fmt.Sprintf("%s_%s.log", *serviceName, time.Now().Format("02-01-2006_15-04-05")) | ||
logFilePath := path.Join(*logDirectory, logFileName) | ||
file, err := os.Create(logFilePath) | ||
if err != nil { | ||
log.Fatalf("Error - os.Create(%s): %v", logFilePath, err) | ||
} | ||
defer func() { _ = file.Close() }() | ||
|
||
logger.Stdout = file | ||
logger.Stderr = file | ||
} | ||
|
||
// | ||
wrapper := service.Wrapper{ | ||
Config: service.Configuration{ | ||
IsAnInteractiveSession: isAnInteractiveSession, | ||
ServiceName: *serviceName, | ||
Command: *command, | ||
Arguments: cli.BindArguments(*arguments), | ||
Logger: logger, | ||
func main() { | ||
host := command.Host{ | ||
CmdConfig: command.Config{ | ||
Name: "powershell", | ||
//Name: "C:/Program Files/PowerShell/7-preview/preview/pwsh-preview.cmd", | ||
Arguments: []string{ | ||
`P:\_dev\projects\service-shark\example\test-service.ps1`, | ||
}, | ||
}, | ||
LogDirecotry: "c:/tmp", | ||
} | ||
err = wrapper.Run() | ||
if err != nil { | ||
log.Fatalf("wrapper.Run(): %v", err) | ||
|
||
if err := service.Run(&host); err != nil { | ||
log.Fatal(err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,55 +1,35 @@ | ||
package service | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
"golang.org/x/sys/windows/svc" | ||
) | ||
|
||
type winService struct { | ||
wrapper *Wrapper | ||
import "os/signal" | ||
|
||
// Create variable signal.Notify function so we can mock it in tests | ||
var signalNotify = signal.Notify | ||
|
||
// Service interface contains Start and Stop methods which are called | ||
// when the service is started and stopped. The Init method is called | ||
// before the service is started, and after it's determined if the program | ||
// is running as a Windows Service. | ||
// | ||
// The Start and Init methods must be non-blocking. | ||
// | ||
// Implement this interface and pass it to the Run function to start your program. | ||
type Service interface { | ||
// Init is called before the program/service is started and after it's | ||
// determined if the program is running as a Windows Service. This method must | ||
// be non-blocking. | ||
Init(Environment) error | ||
|
||
// Start is called after Init. This method must be non-blocking. | ||
Start() error | ||
|
||
// Stop is called in response to syscall.SIGINT, syscall.SIGTERM, or when a | ||
// Windows Service is stopped. | ||
Stop() error | ||
} | ||
|
||
func (w *winService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) { | ||
changes <- svc.Status{State: svc.StartPending} | ||
|
||
err := w.wrapper.cmd.Start() | ||
if err != nil { | ||
_ = w.wrapper.eventLog.Error(1, fmt.Sprintf("Error - cmd.Start(): %v", err)) | ||
return true, 1 | ||
} | ||
|
||
go func() { | ||
err = w.wrapper.cmd.Wait() | ||
if err != nil { | ||
_ = w.wrapper.eventLog.Error(1, fmt.Sprintf("Error - cmd.Wait(): %v", err)) | ||
} | ||
os.Exit(1) // error because our service command stopped | ||
}() | ||
|
||
changes <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown} | ||
|
||
loop: | ||
for { | ||
c := <-r | ||
switch c.Cmd { | ||
case svc.Interrogate: | ||
changes <- c.CurrentStatus | ||
case svc.Stop, svc.Shutdown: | ||
changes <- svc.Status{State: svc.StopPending} | ||
|
||
err := w.wrapper.cmd.Process.Kill() // #todo kill child process. Test Command -> "C:/Program Files/PowerShell/7-preview/preview/pwsh-preview.cmd" | ||
if err != nil { | ||
_ = w.wrapper.eventLog.Error(1, fmt.Sprintf("Error - cmd.Process.Kill(): %v", err)) | ||
} | ||
|
||
changes <- svc.Status{State: svc.Stopped} | ||
break loop | ||
default: | ||
continue loop | ||
} | ||
} | ||
|
||
return false, 0 | ||
// Environment contains information about the environment | ||
// your application is running in. | ||
type Environment interface { | ||
// IsWindowsService reports whether the program is running as a Windows Service. | ||
IsWindowsService() bool | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// +build !windows | ||
|
||
package service | ||
|
||
import ( | ||
"os" | ||
"syscall" | ||
) | ||
|
||
// Run runs your Service. | ||
// | ||
// Run will block until one of the signals specified in sig is received. | ||
// If sig is empty syscall.SIGINT and syscall.SIGTERM are used by default. | ||
func Run(service Service) error { | ||
env := environment{} | ||
if err := service.Init(env); err != nil { | ||
return err | ||
} | ||
|
||
if err := service.Start(); err != nil { | ||
return err | ||
} | ||
|
||
sig := []os.Signal{syscall.SIGINT, syscall.SIGTERM} | ||
|
||
signalChan := make(chan os.Signal, 1) | ||
signalNotify(signalChan, sig...) | ||
<-signalChan | ||
|
||
return service.Stop() | ||
} | ||
|
||
type environment struct{} | ||
|
||
func (environment) IsWindowsService() bool { | ||
return false | ||
} |
Oops, something went wrong.