mirror of
https://github.com/tengge1/ShadowEditor.git
synced 2026-01-25 15:08:11 +00:00
win service
This commit is contained in:
parent
0cb8004ede
commit
b4cafc22a6
31
server/cmd/win/debug.go
Normal file
31
server/cmd/win/debug.go
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2017-2020 The ShadowEditor Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
//
|
||||
// For more information, please visit: https://github.com/tengge1/ShadowEditor
|
||||
// You can also visit: https://gitee.com/tengge1/ShadowEditor
|
||||
|
||||
// Reference URL: https://github.com/andrewkroh/sys/blob/master/windows/svc/example/main.go
|
||||
|
||||
// +build windows
|
||||
|
||||
package win
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/tengge1/shadoweditor/cmd"
|
||||
)
|
||||
|
||||
// debugCmd debug ShadowEditor service on Windows.
|
||||
var debugCmd = &cobra.Command{
|
||||
Use: "debug",
|
||||
Short: "debug ShadowEditor service",
|
||||
Long: `Debug ShadowEditor service on Windows.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
runService(ServiceName, true)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.AddCommand(installCmd)
|
||||
}
|
||||
93
server/cmd/win/install.go
Normal file
93
server/cmd/win/install.go
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright 2017-2020 The ShadowEditor Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
//
|
||||
// For more information, please visit: https://github.com/tengge1/ShadowEditor
|
||||
// You can also visit: https://gitee.com/tengge1/ShadowEditor
|
||||
|
||||
// Reference URL: https://github.com/andrewkroh/sys/blob/master/windows/svc/example/install.go
|
||||
|
||||
// +build windows
|
||||
|
||||
package win
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/tengge1/shadoweditor/cmd"
|
||||
"golang.org/x/sys/windows/svc/eventlog"
|
||||
"golang.org/x/sys/windows/svc/mgr"
|
||||
)
|
||||
|
||||
// installCmd install ShadowEditor as a service on Windows.
|
||||
var installCmd = &cobra.Command{
|
||||
Use: "install",
|
||||
Short: "install ShadowEditor as a service",
|
||||
Long: `Install ShadowEditor as a service on Windows.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if err := installService(ServiceName, ServiceDisplayName); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.AddCommand(installCmd)
|
||||
}
|
||||
|
||||
func installService(name, desc string) error {
|
||||
exepath, err := exePath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.Disconnect()
|
||||
s, err := m.OpenService(name)
|
||||
if err == nil {
|
||||
s.Close()
|
||||
return fmt.Errorf("service %s already exists", name)
|
||||
}
|
||||
s, err = m.CreateService(name, exepath, mgr.Config{DisplayName: desc}, "is", "auto-started")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
err = eventlog.InstallAsEventCreate(name, eventlog.Error|eventlog.Warning|eventlog.Info)
|
||||
if err != nil {
|
||||
s.Delete()
|
||||
return fmt.Errorf("SetupEventLogSource() failed: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func exePath() (string, error) {
|
||||
prog := os.Args[0]
|
||||
p, err := filepath.Abs(prog)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
fi, err := os.Stat(p)
|
||||
if err == nil {
|
||||
if !fi.Mode().IsDir() {
|
||||
return p, nil
|
||||
}
|
||||
err = fmt.Errorf("%s is directory", p)
|
||||
}
|
||||
if filepath.Ext(p) == "" {
|
||||
p += ".exe"
|
||||
fi, err := os.Stat(p)
|
||||
if err == nil {
|
||||
if !fi.Mode().IsDir() {
|
||||
return p, nil
|
||||
}
|
||||
err = fmt.Errorf("%s is directory", p)
|
||||
}
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
59
server/cmd/win/remove.go
Normal file
59
server/cmd/win/remove.go
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright 2017-2020 The ShadowEditor Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
//
|
||||
// For more information, please visit: https://github.com/tengge1/ShadowEditor
|
||||
// You can also visit: https://gitee.com/tengge1/ShadowEditor
|
||||
|
||||
// Reference URL: https://github.com/andrewkroh/sys/blob/master/windows/svc/example/install.go
|
||||
|
||||
// +build windows
|
||||
|
||||
package win
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/tengge1/shadoweditor/cmd"
|
||||
"golang.org/x/sys/windows/svc/eventlog"
|
||||
"golang.org/x/sys/windows/svc/mgr"
|
||||
)
|
||||
|
||||
// removeCmd remove ShadowEditor service on Windows.
|
||||
var removeCmd = &cobra.Command{
|
||||
Use: "remove",
|
||||
Short: "remove ShadowEditor service",
|
||||
Long: `Remove ShadowEditor service on Windows.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if err := removeService(ServiceName); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.AddCommand(removeCmd)
|
||||
}
|
||||
|
||||
func removeService(name string) error {
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.Disconnect()
|
||||
s, err := m.OpenService(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("service %s is not installed", name)
|
||||
}
|
||||
defer s.Close()
|
||||
err = s.Delete()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = eventlog.Remove(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("RemoveEventLogSource() failed: %s", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
54
server/cmd/win/start.go
Normal file
54
server/cmd/win/start.go
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2017-2020 The ShadowEditor Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
//
|
||||
// For more information, please visit: https://github.com/tengge1/ShadowEditor
|
||||
// You can also visit: https://gitee.com/tengge1/ShadowEditor
|
||||
|
||||
// Reference URL: https://github.com/andrewkroh/sys/blob/master/windows/svc/example/manage.go
|
||||
|
||||
// +build windows
|
||||
|
||||
package win
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/tengge1/shadoweditor/cmd"
|
||||
"golang.org/x/sys/windows/svc/mgr"
|
||||
)
|
||||
|
||||
// startCmd start ShadowEditor service on Windows.
|
||||
var startCmd = &cobra.Command{
|
||||
Use: "start",
|
||||
Short: "start ShadowEditor service",
|
||||
Long: `Start ShadowEditor service on Windows.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if err := startService(ServiceName); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.AddCommand(startCmd)
|
||||
}
|
||||
|
||||
func startService(name string) error {
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.Disconnect()
|
||||
s, err := m.OpenService(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not access service: %v", err)
|
||||
}
|
||||
defer s.Close()
|
||||
err = s.Start("is", "manual-started")
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not start service: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
67
server/cmd/win/stop.go
Normal file
67
server/cmd/win/stop.go
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2017-2020 The ShadowEditor Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
//
|
||||
// For more information, please visit: https://github.com/tengge1/ShadowEditor
|
||||
// You can also visit: https://gitee.com/tengge1/ShadowEditor
|
||||
|
||||
// Reference URL: https://github.com/andrewkroh/sys/blob/master/windows/svc/example/manage.go
|
||||
|
||||
// +build windows
|
||||
|
||||
package win
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/tengge1/shadoweditor/cmd"
|
||||
"golang.org/x/sys/windows/svc"
|
||||
"golang.org/x/sys/windows/svc/mgr"
|
||||
)
|
||||
|
||||
// stopCmd stop ShadowEditor service on Windows.
|
||||
var stopCmd = &cobra.Command{
|
||||
Use: "stop",
|
||||
Short: "stop ShadowEditor service",
|
||||
Long: `Stop ShadowEditor service on Windows.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if err := controlService(ServiceName, svc.Stop, svc.Stopped); err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmd.AddCommand(stopCmd)
|
||||
}
|
||||
|
||||
func controlService(name string, c svc.Cmd, to svc.State) error {
|
||||
m, err := mgr.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.Disconnect()
|
||||
s, err := m.OpenService(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not access service: %v", err)
|
||||
}
|
||||
defer s.Close()
|
||||
status, err := s.Control(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not send control=%d: %v", c, err)
|
||||
}
|
||||
timeout := time.Now().Add(10 * time.Second)
|
||||
for status.State != to {
|
||||
if timeout.Before(time.Now()) {
|
||||
return fmt.Errorf("timeout waiting for service to go to state=%d", to)
|
||||
}
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
status, err = s.Query()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not retrieve service status: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
89
server/cmd/win/win.go
Normal file
89
server/cmd/win/win.go
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright 2017-2020 The ShadowEditor Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
//
|
||||
// For more information, please visit: https://github.com/tengge1/ShadowEditor
|
||||
// You can also visit: https://gitee.com/tengge1/ShadowEditor
|
||||
|
||||
// Reference URL: https://github.com/andrewkroh/sys/blob/master/windows/svc/example/service.go
|
||||
|
||||
// +build windows
|
||||
|
||||
package win
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/windows/svc"
|
||||
"golang.org/x/sys/windows/svc/debug"
|
||||
"golang.org/x/sys/windows/svc/eventlog"
|
||||
)
|
||||
|
||||
const (
|
||||
// ServiceName is the default service name on Windows.
|
||||
ServiceName = "ShadowEditor"
|
||||
// ServiceDisplayName is the name show in the Service Manager.
|
||||
ServiceDisplayName = "Shadow Editor"
|
||||
)
|
||||
|
||||
var elog debug.Log
|
||||
|
||||
// Service is the ShadowEditor service model.
|
||||
type Service struct{}
|
||||
|
||||
// Execute execute the ShadowEditor service.
|
||||
func (m *Service) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
|
||||
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue
|
||||
changes <- svc.Status{State: svc.StartPending}
|
||||
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case c := <-r:
|
||||
switch c.Cmd {
|
||||
case svc.Interrogate:
|
||||
changes <- c.CurrentStatus
|
||||
// Testing deadlock from https://code.google.com/p/winsvc/issues/detail?id=4
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
changes <- c.CurrentStatus
|
||||
case svc.Stop, svc.Shutdown:
|
||||
// golang.org/x/sys/windows/svc.TestExample is verifying this output.
|
||||
testOutput := strings.Join(args, "-")
|
||||
testOutput += fmt.Sprintf("-%d", c.Context)
|
||||
elog.Info(1, testOutput)
|
||||
break loop
|
||||
default:
|
||||
elog.Error(1, fmt.Sprintf("unexpected control request #%d", c))
|
||||
}
|
||||
}
|
||||
}
|
||||
changes <- svc.Status{State: svc.StopPending}
|
||||
return
|
||||
}
|
||||
|
||||
func runService(name string, isDebug bool) {
|
||||
var err error
|
||||
if isDebug {
|
||||
elog = debug.New(name)
|
||||
} else {
|
||||
elog, err = eventlog.Open(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
defer elog.Close()
|
||||
|
||||
elog.Info(1, fmt.Sprintf("starting %s service", name))
|
||||
run := svc.Run
|
||||
if isDebug {
|
||||
run = debug.Run
|
||||
}
|
||||
err = run(name, &Service{})
|
||||
if err != nil {
|
||||
elog.Error(1, fmt.Sprintf("%s service failed: %v", name, err))
|
||||
return
|
||||
}
|
||||
elog.Info(1, fmt.Sprintf("%s service stopped", name))
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user