Windows: Allow running as a service
Signed-off-by: John Howard <jhoward@microsoft.com> Allows containerd.exe to run as a Windows service. eg Register: `.\containerd.exe --register-service` Start: `net start containerd` ... Stop: `net stop containerd` Unregister: `.\containerd.exe --unregister-service` When running as a service, logs will go to the Windows application event log.
This commit is contained in:
parent
dcb82064d3
commit
40d898a820
@ -91,6 +91,7 @@ func App() *cli.App {
|
|||||||
Usage: "containerd state directory",
|
Usage: "containerd state directory",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
app.Flags = append(app.Flags, serviceFlags()...)
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
configCommand,
|
configCommand,
|
||||||
publishCommand,
|
publishCommand,
|
||||||
@ -105,18 +106,34 @@ func App() *cli.App {
|
|||||||
config = defaultConfig()
|
config = defaultConfig()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if err := srvconfig.LoadConfig(context.GlobalString("config"), config); err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply flags to the config
|
||||||
|
if err := applyFlags(context, config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure top-level directories are created early.
|
||||||
|
if err := server.CreateTopLevelDirectories(config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop if we are registering or unregistering against Windows SCM.
|
||||||
|
stop, err := registerUnregisterService(config.Root)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
if stop {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
done := handleSignals(ctx, signals, serverC)
|
done := handleSignals(ctx, signals, serverC)
|
||||||
// start the signal handler as soon as we can to make sure that
|
// start the signal handler as soon as we can to make sure that
|
||||||
// we don't miss any signals during boot
|
// we don't miss any signals during boot
|
||||||
signal.Notify(signals, handledSignals...)
|
signal.Notify(signals, handledSignals...)
|
||||||
|
|
||||||
if err := srvconfig.LoadConfig(context.GlobalString("config"), config); err != nil && !os.IsNotExist(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// apply flags to the config
|
|
||||||
if err := applyFlags(context, config); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// cleanup temp mounts
|
// cleanup temp mounts
|
||||||
if err := mount.SetTempMountLocation(filepath.Join(config.Root, "tmpmounts")); err != nil {
|
if err := mount.SetTempMountLocation(filepath.Join(config.Root, "tmpmounts")); err != nil {
|
||||||
return errors.Wrap(err, "creating temp mount location")
|
return errors.Wrap(err, "creating temp mount location")
|
||||||
@ -142,7 +159,14 @@ func App() *cli.App {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Launch as a Windows Service if necessary
|
||||||
|
if err := launchService(server, done); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
serverC <- server
|
serverC <- server
|
||||||
|
|
||||||
if config.Debug.Address != "" {
|
if config.Debug.Address != "" {
|
||||||
var l net.Listener
|
var l net.Listener
|
||||||
if filepath.IsAbs(config.Debug.Address) {
|
if filepath.IsAbs(config.Debug.Address) {
|
||||||
@ -215,6 +239,9 @@ func applyFlags(context *cli.Context, config *srvconfig.Config) error {
|
|||||||
*v.d = s
|
*v.d = s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applyPlatformFlags(context)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
44
cmd/containerd/command/service_unsupported.go
Normal file
44
cmd/containerd/command/service_unsupported.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/containerd/containerd/services/server"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
// serviceFlags returns an array of flags for configuring containerd to run
|
||||||
|
// as a service. Only relevant on Windows.
|
||||||
|
func serviceFlags() []cli.Flag {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// applyPlatformFlags applys platform-specific flags.
|
||||||
|
func applyPlatformFlags(context *cli.Context) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// registerUnregisterService is only relevant on Windows.
|
||||||
|
func registerUnregisterService(root string) (bool, error) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// launchService is only relevant on Windows.
|
||||||
|
func launchService(s *server.Server, done chan struct{}) error {
|
||||||
|
return nil
|
||||||
|
}
|
467
cmd/containerd/command/service_windows.go
Normal file
467
cmd/containerd/command/service_windows.go
Normal file
@ -0,0 +1,467 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/services/server"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
"golang.org/x/sys/windows/svc"
|
||||||
|
"golang.org/x/sys/windows/svc/debug"
|
||||||
|
"golang.org/x/sys/windows/svc/eventlog"
|
||||||
|
"golang.org/x/sys/windows/svc/mgr"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
serviceNameFlag string
|
||||||
|
registerServiceFlag bool
|
||||||
|
unregisterServiceFlag bool
|
||||||
|
runServiceFlag bool
|
||||||
|
|
||||||
|
setStdHandle = windows.NewLazySystemDLL("kernel32.dll").NewProc("SetStdHandle")
|
||||||
|
oldStderr windows.Handle
|
||||||
|
panicFile *os.File
|
||||||
|
|
||||||
|
service *handler
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// These should match the values in event_messages.mc.
|
||||||
|
eventInfo = 1
|
||||||
|
eventWarn = 1
|
||||||
|
eventError = 1
|
||||||
|
eventDebug = 2
|
||||||
|
eventPanic = 3
|
||||||
|
eventFatal = 4
|
||||||
|
|
||||||
|
eventExtraOffset = 10 // Add this to any event to get a string that supports extended data
|
||||||
|
)
|
||||||
|
|
||||||
|
// serviceFlags returns an array of flags for configuring containerd to run
|
||||||
|
// as a Windows service under control of SCM.
|
||||||
|
func serviceFlags() []cli.Flag {
|
||||||
|
return []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "service-name",
|
||||||
|
Usage: "Set the Windows service name",
|
||||||
|
Value: "containerd",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "register-service",
|
||||||
|
Usage: "Register the service and exit",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "unregister-service",
|
||||||
|
Usage: "Unregister the service and exit",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "run-service",
|
||||||
|
Usage: "",
|
||||||
|
Hidden: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// applyPlatformFlags applys platform-specific flags.
|
||||||
|
func applyPlatformFlags(context *cli.Context) {
|
||||||
|
|
||||||
|
if s := context.GlobalString("service-name"); s != "" {
|
||||||
|
serviceNameFlag = s
|
||||||
|
}
|
||||||
|
for _, v := range []struct {
|
||||||
|
name string
|
||||||
|
d *bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "register-service",
|
||||||
|
d: ®isterServiceFlag,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unregister-service",
|
||||||
|
d: &unregisterServiceFlag,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "run-service",
|
||||||
|
d: &runServiceFlag,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
*v.d = context.GlobalBool(v.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type handler struct {
|
||||||
|
fromsvc chan error
|
||||||
|
s *server.Server
|
||||||
|
done chan struct{} // Indicates back to app main to quit
|
||||||
|
}
|
||||||
|
|
||||||
|
type etwHook struct {
|
||||||
|
log *eventlog.Log
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *etwHook) Levels() []logrus.Level {
|
||||||
|
return []logrus.Level{
|
||||||
|
logrus.PanicLevel,
|
||||||
|
logrus.FatalLevel,
|
||||||
|
logrus.ErrorLevel,
|
||||||
|
logrus.WarnLevel,
|
||||||
|
logrus.InfoLevel,
|
||||||
|
logrus.DebugLevel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *etwHook) Fire(e *logrus.Entry) error {
|
||||||
|
var (
|
||||||
|
etype uint16
|
||||||
|
eid uint32
|
||||||
|
)
|
||||||
|
|
||||||
|
switch e.Level {
|
||||||
|
case logrus.PanicLevel:
|
||||||
|
etype = windows.EVENTLOG_ERROR_TYPE
|
||||||
|
eid = eventPanic
|
||||||
|
case logrus.FatalLevel:
|
||||||
|
etype = windows.EVENTLOG_ERROR_TYPE
|
||||||
|
eid = eventFatal
|
||||||
|
case logrus.ErrorLevel:
|
||||||
|
etype = windows.EVENTLOG_ERROR_TYPE
|
||||||
|
eid = eventError
|
||||||
|
case logrus.WarnLevel:
|
||||||
|
etype = windows.EVENTLOG_WARNING_TYPE
|
||||||
|
eid = eventWarn
|
||||||
|
case logrus.InfoLevel:
|
||||||
|
etype = windows.EVENTLOG_INFORMATION_TYPE
|
||||||
|
eid = eventInfo
|
||||||
|
case logrus.DebugLevel:
|
||||||
|
etype = windows.EVENTLOG_INFORMATION_TYPE
|
||||||
|
eid = eventDebug
|
||||||
|
default:
|
||||||
|
return errors.New("unknown level")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is additional data, include it as a second string.
|
||||||
|
exts := ""
|
||||||
|
if len(e.Data) > 0 {
|
||||||
|
fs := bytes.Buffer{}
|
||||||
|
for k, v := range e.Data {
|
||||||
|
fs.WriteString(k)
|
||||||
|
fs.WriteByte('=')
|
||||||
|
fmt.Fprint(&fs, v)
|
||||||
|
fs.WriteByte(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
exts = fs.String()[:fs.Len()-1]
|
||||||
|
eid += eventExtraOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.log == nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s [%s]\n", e.Message, exts)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ss [2]*uint16
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
ss[0], err = windows.UTF16PtrFromString(e.Message)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
count := uint16(1)
|
||||||
|
if exts != "" {
|
||||||
|
ss[1], err = windows.UTF16PtrFromString(exts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
|
||||||
|
return windows.ReportEvent(h.log.Handle, etype, 0, eid, 0, count, 0, &ss[0], nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getServicePath() (string, error) {
|
||||||
|
p, err := exec.LookPath(os.Args[0])
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filepath.Abs(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerService() error {
|
||||||
|
p, err := getServicePath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m, err := mgr.Connect()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer m.Disconnect()
|
||||||
|
|
||||||
|
c := mgr.Config{
|
||||||
|
ServiceType: windows.SERVICE_WIN32_OWN_PROCESS,
|
||||||
|
StartType: mgr.StartAutomatic,
|
||||||
|
ErrorControl: mgr.ErrorNormal,
|
||||||
|
DisplayName: "Containerd",
|
||||||
|
Description: "Container runtime",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the service to launch with the arguments that were just passed.
|
||||||
|
args := []string{"--run-service"}
|
||||||
|
for _, a := range os.Args[1:] {
|
||||||
|
if a != "--register-service" && a != "--unregister-service" {
|
||||||
|
args = append(args, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := m.CreateService(serviceNameFlag, p, c, args...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
// See http://stackoverflow.com/questions/35151052/how-do-i-configure-failure-actions-of-a-windows-service-written-in-go
|
||||||
|
const (
|
||||||
|
scActionNone = 0
|
||||||
|
scActionRestart = 1
|
||||||
|
|
||||||
|
serviceConfigFailureActions = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
type serviceFailureActions struct {
|
||||||
|
ResetPeriod uint32
|
||||||
|
RebootMsg *uint16
|
||||||
|
Command *uint16
|
||||||
|
ActionsCount uint32
|
||||||
|
Actions uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type scAction struct {
|
||||||
|
Type uint32
|
||||||
|
Delay uint32
|
||||||
|
}
|
||||||
|
t := []scAction{
|
||||||
|
{Type: scActionRestart, Delay: uint32(60 * time.Second / time.Millisecond)},
|
||||||
|
{Type: scActionRestart, Delay: uint32(60 * time.Second / time.Millisecond)},
|
||||||
|
{Type: scActionNone},
|
||||||
|
}
|
||||||
|
lpInfo := serviceFailureActions{ResetPeriod: uint32(24 * time.Hour / time.Second), ActionsCount: uint32(3), Actions: uintptr(unsafe.Pointer(&t[0]))}
|
||||||
|
err = windows.ChangeServiceConfig2(s.Handle, serviceConfigFailureActions, (*byte)(unsafe.Pointer(&lpInfo)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return eventlog.Install(serviceNameFlag, p, false, eventlog.Info|eventlog.Warning|eventlog.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unregisterService() error {
|
||||||
|
m, err := mgr.Connect()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer m.Disconnect()
|
||||||
|
|
||||||
|
s, err := m.OpenService(serviceNameFlag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
eventlog.Remove(serviceNameFlag)
|
||||||
|
err = s.Delete()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// registerUnregisterService is an entrypoint early in the daemon startup
|
||||||
|
// to handle (un-)registering against Windows Service Control Manager (SCM).
|
||||||
|
// It returns an indication to stop on successful SCM operation, and an error.
|
||||||
|
func registerUnregisterService(root string) (bool, error) {
|
||||||
|
|
||||||
|
if unregisterServiceFlag {
|
||||||
|
if registerServiceFlag {
|
||||||
|
return true, errors.New("--register-service and --unregister-service cannot be used together")
|
||||||
|
}
|
||||||
|
return true, unregisterService()
|
||||||
|
}
|
||||||
|
|
||||||
|
if registerServiceFlag {
|
||||||
|
return true, registerService()
|
||||||
|
}
|
||||||
|
|
||||||
|
if runServiceFlag {
|
||||||
|
if err := initPanicFile(filepath.Join(root, "panic.log")); err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
interactive, err := svc.IsAnInteractiveSession()
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var log *eventlog.Log
|
||||||
|
if !interactive {
|
||||||
|
log, err = eventlog.Open(serviceNameFlag)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.AddHook(&etwHook{log})
|
||||||
|
logrus.SetOutput(ioutil.Discard)
|
||||||
|
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// launchService is the entry point for running the daemon under SCM.
|
||||||
|
func launchService(s *server.Server, done chan struct{}) error {
|
||||||
|
|
||||||
|
if !runServiceFlag {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
h := &handler{
|
||||||
|
fromsvc: make(chan error),
|
||||||
|
s: s,
|
||||||
|
done: done,
|
||||||
|
}
|
||||||
|
|
||||||
|
interactive, err := svc.IsAnInteractiveSession()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
service = h
|
||||||
|
go func() {
|
||||||
|
if interactive {
|
||||||
|
err = debug.Run(serviceNameFlag, h)
|
||||||
|
} else {
|
||||||
|
err = svc.Run(serviceNameFlag, h)
|
||||||
|
}
|
||||||
|
h.fromsvc <- err
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait for the first signal from the service handler.
|
||||||
|
err = <-h.fromsvc
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handler) Execute(_ []string, r <-chan svc.ChangeRequest, s chan<- svc.Status) (bool, uint32) {
|
||||||
|
s <- svc.Status{State: svc.StartPending, Accepts: 0}
|
||||||
|
// Unblock launchService()
|
||||||
|
h.fromsvc <- nil
|
||||||
|
|
||||||
|
s <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown | svc.Accepted(windows.SERVICE_ACCEPT_PARAMCHANGE)}
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case c := <-r:
|
||||||
|
switch c.Cmd {
|
||||||
|
case svc.Interrogate:
|
||||||
|
s <- c.CurrentStatus
|
||||||
|
case svc.Stop, svc.Shutdown:
|
||||||
|
s <- svc.Status{State: svc.StopPending, Accepts: 0}
|
||||||
|
h.s.Stop()
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removePanicFile()
|
||||||
|
close(h.done)
|
||||||
|
return false, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func initPanicFile(path string) error {
|
||||||
|
var err error
|
||||||
|
panicFile, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
st, err := panicFile.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are contents in the file already, move the file out of the way
|
||||||
|
// and replace it.
|
||||||
|
if st.Size() > 0 {
|
||||||
|
panicFile.Close()
|
||||||
|
os.Rename(path, path+".old")
|
||||||
|
panicFile, err = os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update STD_ERROR_HANDLE to point to the panic file so that Go writes to
|
||||||
|
// it when it panics. Remember the old stderr to restore it before removing
|
||||||
|
// the panic file.
|
||||||
|
sh := windows.STD_ERROR_HANDLE
|
||||||
|
h, err := windows.GetStdHandle(uint32(sh))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
oldStderr = h
|
||||||
|
|
||||||
|
r, _, err := setStdHandle.Call(uintptr(sh), panicFile.Fd())
|
||||||
|
if r == 0 && err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset os.Stderr to the panic file (so fmt.Fprintf(os.Stderr,...) actually gets redirected)
|
||||||
|
os.Stderr = os.NewFile(panicFile.Fd(), "/dev/stderr")
|
||||||
|
|
||||||
|
// Force threads that panic to write to stderr (the panicFile handle now), otherwise it will go into the ether
|
||||||
|
log.SetOutput(os.Stderr)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removePanicFile() {
|
||||||
|
if st, err := panicFile.Stat(); err == nil {
|
||||||
|
if st.Size() == 0 {
|
||||||
|
sh := windows.STD_ERROR_HANDLE
|
||||||
|
setStdHandle.Call(uintptr(sh), uintptr(oldStderr))
|
||||||
|
panicFile.Close()
|
||||||
|
os.Remove(panicFile.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -50,23 +50,28 @@ import (
|
|||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// New creates and initializes a new containerd server
|
// CreateTopLevelDirectories creates the top-level root and state directories.
|
||||||
func New(ctx context.Context, config *srvconfig.Config) (*Server, error) {
|
func CreateTopLevelDirectories(config *srvconfig.Config) error {
|
||||||
switch {
|
switch {
|
||||||
case config.Root == "":
|
case config.Root == "":
|
||||||
return nil, errors.New("root must be specified")
|
return errors.New("root must be specified")
|
||||||
case config.State == "":
|
case config.State == "":
|
||||||
return nil, errors.New("state must be specified")
|
return errors.New("state must be specified")
|
||||||
case config.Root == config.State:
|
case config.Root == config.State:
|
||||||
return nil, errors.New("root and state must be different paths")
|
return errors.New("root and state must be different paths")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.MkdirAll(config.Root, 0711); err != nil {
|
if err := os.MkdirAll(config.Root, 0711); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
if err := os.MkdirAll(config.State, 0711); err != nil {
|
if err := os.MkdirAll(config.State, 0711); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates and initializes a new containerd server
|
||||||
|
func New(ctx context.Context, config *srvconfig.Config) (*Server, error) {
|
||||||
if err := apply(ctx, config); err != nil {
|
if err := apply(ctx, config); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
srvconfig "github.com/containerd/containerd/services/server/config"
|
srvconfig "github.com/containerd/containerd/services/server/config"
|
||||||
@ -25,9 +24,9 @@ import (
|
|||||||
is "gotest.tools/assert/cmp"
|
is "gotest.tools/assert/cmp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewErrorsWithSamePathForRootAndState(t *testing.T) {
|
func TestCreateTopLevelDirectoriesErrorsWithSamePathForRootAndState(t *testing.T) {
|
||||||
path := "/tmp/path/for/testing"
|
path := "/tmp/path/for/testing"
|
||||||
_, err := New(context.Background(), &srvconfig.Config{
|
err := CreateTopLevelDirectories(&srvconfig.Config{
|
||||||
Root: path,
|
Root: path,
|
||||||
State: path,
|
State: path,
|
||||||
})
|
})
|
||||||
|
198
vendor/golang.org/x/sys/windows/registry/key.go
generated
vendored
Normal file
198
vendor/golang.org/x/sys/windows/registry/key.go
generated
vendored
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
// Package registry provides access to the Windows registry.
|
||||||
|
//
|
||||||
|
// Here is a simple example, opening a registry key and reading a string value from it.
|
||||||
|
//
|
||||||
|
// k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatal(err)
|
||||||
|
// }
|
||||||
|
// defer k.Close()
|
||||||
|
//
|
||||||
|
// s, _, err := k.GetStringValue("SystemRoot")
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatal(err)
|
||||||
|
// }
|
||||||
|
// fmt.Printf("Windows system root is %q\n", s)
|
||||||
|
//
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Registry key security and access rights.
|
||||||
|
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724878.aspx
|
||||||
|
// for details.
|
||||||
|
ALL_ACCESS = 0xf003f
|
||||||
|
CREATE_LINK = 0x00020
|
||||||
|
CREATE_SUB_KEY = 0x00004
|
||||||
|
ENUMERATE_SUB_KEYS = 0x00008
|
||||||
|
EXECUTE = 0x20019
|
||||||
|
NOTIFY = 0x00010
|
||||||
|
QUERY_VALUE = 0x00001
|
||||||
|
READ = 0x20019
|
||||||
|
SET_VALUE = 0x00002
|
||||||
|
WOW64_32KEY = 0x00200
|
||||||
|
WOW64_64KEY = 0x00100
|
||||||
|
WRITE = 0x20006
|
||||||
|
)
|
||||||
|
|
||||||
|
// Key is a handle to an open Windows registry key.
|
||||||
|
// Keys can be obtained by calling OpenKey; there are
|
||||||
|
// also some predefined root keys such as CURRENT_USER.
|
||||||
|
// Keys can be used directly in the Windows API.
|
||||||
|
type Key syscall.Handle
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Windows defines some predefined root keys that are always open.
|
||||||
|
// An application can use these keys as entry points to the registry.
|
||||||
|
// Normally these keys are used in OpenKey to open new keys,
|
||||||
|
// but they can also be used anywhere a Key is required.
|
||||||
|
CLASSES_ROOT = Key(syscall.HKEY_CLASSES_ROOT)
|
||||||
|
CURRENT_USER = Key(syscall.HKEY_CURRENT_USER)
|
||||||
|
LOCAL_MACHINE = Key(syscall.HKEY_LOCAL_MACHINE)
|
||||||
|
USERS = Key(syscall.HKEY_USERS)
|
||||||
|
CURRENT_CONFIG = Key(syscall.HKEY_CURRENT_CONFIG)
|
||||||
|
PERFORMANCE_DATA = Key(syscall.HKEY_PERFORMANCE_DATA)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Close closes open key k.
|
||||||
|
func (k Key) Close() error {
|
||||||
|
return syscall.RegCloseKey(syscall.Handle(k))
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenKey opens a new key with path name relative to key k.
|
||||||
|
// It accepts any open key, including CURRENT_USER and others,
|
||||||
|
// and returns the new key and an error.
|
||||||
|
// The access parameter specifies desired access rights to the
|
||||||
|
// key to be opened.
|
||||||
|
func OpenKey(k Key, path string, access uint32) (Key, error) {
|
||||||
|
p, err := syscall.UTF16PtrFromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
var subkey syscall.Handle
|
||||||
|
err = syscall.RegOpenKeyEx(syscall.Handle(k), p, 0, access, &subkey)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return Key(subkey), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenRemoteKey opens a predefined registry key on another
|
||||||
|
// computer pcname. The key to be opened is specified by k, but
|
||||||
|
// can only be one of LOCAL_MACHINE, PERFORMANCE_DATA or USERS.
|
||||||
|
// If pcname is "", OpenRemoteKey returns local computer key.
|
||||||
|
func OpenRemoteKey(pcname string, k Key) (Key, error) {
|
||||||
|
var err error
|
||||||
|
var p *uint16
|
||||||
|
if pcname != "" {
|
||||||
|
p, err = syscall.UTF16PtrFromString(`\\` + pcname)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var remoteKey syscall.Handle
|
||||||
|
err = regConnectRegistry(p, syscall.Handle(k), &remoteKey)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return Key(remoteKey), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadSubKeyNames returns the names of subkeys of key k.
|
||||||
|
// The parameter n controls the number of returned names,
|
||||||
|
// analogous to the way os.File.Readdirnames works.
|
||||||
|
func (k Key) ReadSubKeyNames(n int) ([]string, error) {
|
||||||
|
names := make([]string, 0)
|
||||||
|
// Registry key size limit is 255 bytes and described there:
|
||||||
|
// https://msdn.microsoft.com/library/windows/desktop/ms724872.aspx
|
||||||
|
buf := make([]uint16, 256) //plus extra room for terminating zero byte
|
||||||
|
loopItems:
|
||||||
|
for i := uint32(0); ; i++ {
|
||||||
|
if n > 0 {
|
||||||
|
if len(names) == n {
|
||||||
|
return names, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l := uint32(len(buf))
|
||||||
|
for {
|
||||||
|
err := syscall.RegEnumKeyEx(syscall.Handle(k), i, &buf[0], &l, nil, nil, nil, nil)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err == syscall.ERROR_MORE_DATA {
|
||||||
|
// Double buffer size and try again.
|
||||||
|
l = uint32(2 * len(buf))
|
||||||
|
buf = make([]uint16, l)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err == _ERROR_NO_MORE_ITEMS {
|
||||||
|
break loopItems
|
||||||
|
}
|
||||||
|
return names, err
|
||||||
|
}
|
||||||
|
names = append(names, syscall.UTF16ToString(buf[:l]))
|
||||||
|
}
|
||||||
|
if n > len(names) {
|
||||||
|
return names, io.EOF
|
||||||
|
}
|
||||||
|
return names, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateKey creates a key named path under open key k.
|
||||||
|
// CreateKey returns the new key and a boolean flag that reports
|
||||||
|
// whether the key already existed.
|
||||||
|
// The access parameter specifies the access rights for the key
|
||||||
|
// to be created.
|
||||||
|
func CreateKey(k Key, path string, access uint32) (newk Key, openedExisting bool, err error) {
|
||||||
|
var h syscall.Handle
|
||||||
|
var d uint32
|
||||||
|
err = regCreateKeyEx(syscall.Handle(k), syscall.StringToUTF16Ptr(path),
|
||||||
|
0, nil, _REG_OPTION_NON_VOLATILE, access, nil, &h, &d)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false, err
|
||||||
|
}
|
||||||
|
return Key(h), d == _REG_OPENED_EXISTING_KEY, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteKey deletes the subkey path of key k and its values.
|
||||||
|
func DeleteKey(k Key, path string) error {
|
||||||
|
return regDeleteKey(syscall.Handle(k), syscall.StringToUTF16Ptr(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
// A KeyInfo describes the statistics of a key. It is returned by Stat.
|
||||||
|
type KeyInfo struct {
|
||||||
|
SubKeyCount uint32
|
||||||
|
MaxSubKeyLen uint32 // size of the key's subkey with the longest name, in Unicode characters, not including the terminating zero byte
|
||||||
|
ValueCount uint32
|
||||||
|
MaxValueNameLen uint32 // size of the key's longest value name, in Unicode characters, not including the terminating zero byte
|
||||||
|
MaxValueLen uint32 // longest data component among the key's values, in bytes
|
||||||
|
lastWriteTime syscall.Filetime
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModTime returns the key's last write time.
|
||||||
|
func (ki *KeyInfo) ModTime() time.Time {
|
||||||
|
return time.Unix(0, ki.lastWriteTime.Nanoseconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat retrieves information about the open key k.
|
||||||
|
func (k Key) Stat() (*KeyInfo, error) {
|
||||||
|
var ki KeyInfo
|
||||||
|
err := syscall.RegQueryInfoKey(syscall.Handle(k), nil, nil, nil,
|
||||||
|
&ki.SubKeyCount, &ki.MaxSubKeyLen, nil, &ki.ValueCount,
|
||||||
|
&ki.MaxValueNameLen, &ki.MaxValueLen, nil, &ki.lastWriteTime)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &ki, nil
|
||||||
|
}
|
7
vendor/golang.org/x/sys/windows/registry/mksyscall.go
generated
vendored
Normal file
7
vendor/golang.org/x/sys/windows/registry/mksyscall.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall.go
|
32
vendor/golang.org/x/sys/windows/registry/syscall.go
generated
vendored
Normal file
32
vendor/golang.org/x/sys/windows/registry/syscall.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
const (
|
||||||
|
_REG_OPTION_NON_VOLATILE = 0
|
||||||
|
|
||||||
|
_REG_CREATED_NEW_KEY = 1
|
||||||
|
_REG_OPENED_EXISTING_KEY = 2
|
||||||
|
|
||||||
|
_ERROR_NO_MORE_ITEMS syscall.Errno = 259
|
||||||
|
)
|
||||||
|
|
||||||
|
func LoadRegLoadMUIString() error {
|
||||||
|
return procRegLoadMUIStringW.Find()
|
||||||
|
}
|
||||||
|
|
||||||
|
//sys regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) = advapi32.RegCreateKeyExW
|
||||||
|
//sys regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) = advapi32.RegDeleteKeyW
|
||||||
|
//sys regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype uint32, buf *byte, bufsize uint32) (regerrno error) = advapi32.RegSetValueExW
|
||||||
|
//sys regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegEnumValueW
|
||||||
|
//sys regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) = advapi32.RegDeleteValueW
|
||||||
|
//sys regLoadMUIString(key syscall.Handle, name *uint16, buf *uint16, buflen uint32, buflenCopied *uint32, flags uint32, dir *uint16) (regerrno error) = advapi32.RegLoadMUIStringW
|
||||||
|
//sys regConnectRegistry(machinename *uint16, key syscall.Handle, result *syscall.Handle) (regerrno error) = advapi32.RegConnectRegistryW
|
||||||
|
|
||||||
|
//sys expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) = kernel32.ExpandEnvironmentStringsW
|
384
vendor/golang.org/x/sys/windows/registry/value.go
generated
vendored
Normal file
384
vendor/golang.org/x/sys/windows/registry/value.go
generated
vendored
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Registry value types.
|
||||||
|
NONE = 0
|
||||||
|
SZ = 1
|
||||||
|
EXPAND_SZ = 2
|
||||||
|
BINARY = 3
|
||||||
|
DWORD = 4
|
||||||
|
DWORD_BIG_ENDIAN = 5
|
||||||
|
LINK = 6
|
||||||
|
MULTI_SZ = 7
|
||||||
|
RESOURCE_LIST = 8
|
||||||
|
FULL_RESOURCE_DESCRIPTOR = 9
|
||||||
|
RESOURCE_REQUIREMENTS_LIST = 10
|
||||||
|
QWORD = 11
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrShortBuffer is returned when the buffer was too short for the operation.
|
||||||
|
ErrShortBuffer = syscall.ERROR_MORE_DATA
|
||||||
|
|
||||||
|
// ErrNotExist is returned when a registry key or value does not exist.
|
||||||
|
ErrNotExist = syscall.ERROR_FILE_NOT_FOUND
|
||||||
|
|
||||||
|
// ErrUnexpectedType is returned by Get*Value when the value's type was unexpected.
|
||||||
|
ErrUnexpectedType = errors.New("unexpected key value type")
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetValue retrieves the type and data for the specified value associated
|
||||||
|
// with an open key k. It fills up buffer buf and returns the retrieved
|
||||||
|
// byte count n. If buf is too small to fit the stored value it returns
|
||||||
|
// ErrShortBuffer error along with the required buffer size n.
|
||||||
|
// If no buffer is provided, it returns true and actual buffer size n.
|
||||||
|
// If no buffer is provided, GetValue returns the value's type only.
|
||||||
|
// If the value does not exist, the error returned is ErrNotExist.
|
||||||
|
//
|
||||||
|
// GetValue is a low level function. If value's type is known, use the appropriate
|
||||||
|
// Get*Value function instead.
|
||||||
|
func (k Key) GetValue(name string, buf []byte) (n int, valtype uint32, err error) {
|
||||||
|
pname, err := syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
var pbuf *byte
|
||||||
|
if len(buf) > 0 {
|
||||||
|
pbuf = (*byte)(unsafe.Pointer(&buf[0]))
|
||||||
|
}
|
||||||
|
l := uint32(len(buf))
|
||||||
|
err = syscall.RegQueryValueEx(syscall.Handle(k), pname, nil, &valtype, pbuf, &l)
|
||||||
|
if err != nil {
|
||||||
|
return int(l), valtype, err
|
||||||
|
}
|
||||||
|
return int(l), valtype, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k Key) getValue(name string, buf []byte) (date []byte, valtype uint32, err error) {
|
||||||
|
p, err := syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
var t uint32
|
||||||
|
n := uint32(len(buf))
|
||||||
|
for {
|
||||||
|
err = syscall.RegQueryValueEx(syscall.Handle(k), p, nil, &t, (*byte)(unsafe.Pointer(&buf[0])), &n)
|
||||||
|
if err == nil {
|
||||||
|
return buf[:n], t, nil
|
||||||
|
}
|
||||||
|
if err != syscall.ERROR_MORE_DATA {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
if n <= uint32(len(buf)) {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
buf = make([]byte, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStringValue retrieves the string value for the specified
|
||||||
|
// value name associated with an open key k. It also returns the value's type.
|
||||||
|
// If value does not exist, GetStringValue returns ErrNotExist.
|
||||||
|
// If value is not SZ or EXPAND_SZ, it will return the correct value
|
||||||
|
// type and ErrUnexpectedType.
|
||||||
|
func (k Key) GetStringValue(name string) (val string, valtype uint32, err error) {
|
||||||
|
data, typ, err2 := k.getValue(name, make([]byte, 64))
|
||||||
|
if err2 != nil {
|
||||||
|
return "", typ, err2
|
||||||
|
}
|
||||||
|
switch typ {
|
||||||
|
case SZ, EXPAND_SZ:
|
||||||
|
default:
|
||||||
|
return "", typ, ErrUnexpectedType
|
||||||
|
}
|
||||||
|
if len(data) == 0 {
|
||||||
|
return "", typ, nil
|
||||||
|
}
|
||||||
|
u := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[:]
|
||||||
|
return syscall.UTF16ToString(u), typ, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMUIStringValue retrieves the localized string value for
|
||||||
|
// the specified value name associated with an open key k.
|
||||||
|
// If the value name doesn't exist or the localized string value
|
||||||
|
// can't be resolved, GetMUIStringValue returns ErrNotExist.
|
||||||
|
// GetMUIStringValue panics if the system doesn't support
|
||||||
|
// regLoadMUIString; use LoadRegLoadMUIString to check if
|
||||||
|
// regLoadMUIString is supported before calling this function.
|
||||||
|
func (k Key) GetMUIStringValue(name string) (string, error) {
|
||||||
|
pname, err := syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]uint16, 1024)
|
||||||
|
var buflen uint32
|
||||||
|
var pdir *uint16
|
||||||
|
|
||||||
|
err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
|
||||||
|
if err == syscall.ERROR_FILE_NOT_FOUND { // Try fallback path
|
||||||
|
|
||||||
|
// Try to resolve the string value using the system directory as
|
||||||
|
// a DLL search path; this assumes the string value is of the form
|
||||||
|
// @[path]\dllname,-strID but with no path given, e.g. @tzres.dll,-320.
|
||||||
|
|
||||||
|
// This approach works with tzres.dll but may have to be revised
|
||||||
|
// in the future to allow callers to provide custom search paths.
|
||||||
|
|
||||||
|
var s string
|
||||||
|
s, err = ExpandString("%SystemRoot%\\system32\\")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
pdir, err = syscall.UTF16PtrFromString(s)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
|
||||||
|
}
|
||||||
|
|
||||||
|
for err == syscall.ERROR_MORE_DATA { // Grow buffer if needed
|
||||||
|
if buflen <= uint32(len(buf)) {
|
||||||
|
break // Buffer not growing, assume race; break
|
||||||
|
}
|
||||||
|
buf = make([]uint16, buflen)
|
||||||
|
err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return syscall.UTF16ToString(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpandString expands environment-variable strings and replaces
|
||||||
|
// them with the values defined for the current user.
|
||||||
|
// Use ExpandString to expand EXPAND_SZ strings.
|
||||||
|
func ExpandString(value string) (string, error) {
|
||||||
|
if value == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
p, err := syscall.UTF16PtrFromString(value)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
r := make([]uint16, 100)
|
||||||
|
for {
|
||||||
|
n, err := expandEnvironmentStrings(p, &r[0], uint32(len(r)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if n <= uint32(len(r)) {
|
||||||
|
u := (*[1 << 29]uint16)(unsafe.Pointer(&r[0]))[:]
|
||||||
|
return syscall.UTF16ToString(u), nil
|
||||||
|
}
|
||||||
|
r = make([]uint16, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStringsValue retrieves the []string value for the specified
|
||||||
|
// value name associated with an open key k. It also returns the value's type.
|
||||||
|
// If value does not exist, GetStringsValue returns ErrNotExist.
|
||||||
|
// If value is not MULTI_SZ, it will return the correct value
|
||||||
|
// type and ErrUnexpectedType.
|
||||||
|
func (k Key) GetStringsValue(name string) (val []string, valtype uint32, err error) {
|
||||||
|
data, typ, err2 := k.getValue(name, make([]byte, 64))
|
||||||
|
if err2 != nil {
|
||||||
|
return nil, typ, err2
|
||||||
|
}
|
||||||
|
if typ != MULTI_SZ {
|
||||||
|
return nil, typ, ErrUnexpectedType
|
||||||
|
}
|
||||||
|
if len(data) == 0 {
|
||||||
|
return nil, typ, nil
|
||||||
|
}
|
||||||
|
p := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[:len(data)/2]
|
||||||
|
if len(p) == 0 {
|
||||||
|
return nil, typ, nil
|
||||||
|
}
|
||||||
|
if p[len(p)-1] == 0 {
|
||||||
|
p = p[:len(p)-1] // remove terminating null
|
||||||
|
}
|
||||||
|
val = make([]string, 0, 5)
|
||||||
|
from := 0
|
||||||
|
for i, c := range p {
|
||||||
|
if c == 0 {
|
||||||
|
val = append(val, string(utf16.Decode(p[from:i])))
|
||||||
|
from = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val, typ, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIntegerValue retrieves the integer value for the specified
|
||||||
|
// value name associated with an open key k. It also returns the value's type.
|
||||||
|
// If value does not exist, GetIntegerValue returns ErrNotExist.
|
||||||
|
// If value is not DWORD or QWORD, it will return the correct value
|
||||||
|
// type and ErrUnexpectedType.
|
||||||
|
func (k Key) GetIntegerValue(name string) (val uint64, valtype uint32, err error) {
|
||||||
|
data, typ, err2 := k.getValue(name, make([]byte, 8))
|
||||||
|
if err2 != nil {
|
||||||
|
return 0, typ, err2
|
||||||
|
}
|
||||||
|
switch typ {
|
||||||
|
case DWORD:
|
||||||
|
if len(data) != 4 {
|
||||||
|
return 0, typ, errors.New("DWORD value is not 4 bytes long")
|
||||||
|
}
|
||||||
|
return uint64(*(*uint32)(unsafe.Pointer(&data[0]))), DWORD, nil
|
||||||
|
case QWORD:
|
||||||
|
if len(data) != 8 {
|
||||||
|
return 0, typ, errors.New("QWORD value is not 8 bytes long")
|
||||||
|
}
|
||||||
|
return uint64(*(*uint64)(unsafe.Pointer(&data[0]))), QWORD, nil
|
||||||
|
default:
|
||||||
|
return 0, typ, ErrUnexpectedType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBinaryValue retrieves the binary value for the specified
|
||||||
|
// value name associated with an open key k. It also returns the value's type.
|
||||||
|
// If value does not exist, GetBinaryValue returns ErrNotExist.
|
||||||
|
// If value is not BINARY, it will return the correct value
|
||||||
|
// type and ErrUnexpectedType.
|
||||||
|
func (k Key) GetBinaryValue(name string) (val []byte, valtype uint32, err error) {
|
||||||
|
data, typ, err2 := k.getValue(name, make([]byte, 64))
|
||||||
|
if err2 != nil {
|
||||||
|
return nil, typ, err2
|
||||||
|
}
|
||||||
|
if typ != BINARY {
|
||||||
|
return nil, typ, ErrUnexpectedType
|
||||||
|
}
|
||||||
|
return data, typ, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k Key) setValue(name string, valtype uint32, data []byte) error {
|
||||||
|
p, err := syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(data) == 0 {
|
||||||
|
return regSetValueEx(syscall.Handle(k), p, 0, valtype, nil, 0)
|
||||||
|
}
|
||||||
|
return regSetValueEx(syscall.Handle(k), p, 0, valtype, &data[0], uint32(len(data)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDWordValue sets the data and type of a name value
|
||||||
|
// under key k to value and DWORD.
|
||||||
|
func (k Key) SetDWordValue(name string, value uint32) error {
|
||||||
|
return k.setValue(name, DWORD, (*[4]byte)(unsafe.Pointer(&value))[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetQWordValue sets the data and type of a name value
|
||||||
|
// under key k to value and QWORD.
|
||||||
|
func (k Key) SetQWordValue(name string, value uint64) error {
|
||||||
|
return k.setValue(name, QWORD, (*[8]byte)(unsafe.Pointer(&value))[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k Key) setStringValue(name string, valtype uint32, value string) error {
|
||||||
|
v, err := syscall.UTF16FromString(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
buf := (*[1 << 29]byte)(unsafe.Pointer(&v[0]))[:len(v)*2]
|
||||||
|
return k.setValue(name, valtype, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetStringValue sets the data and type of a name value
|
||||||
|
// under key k to value and SZ. The value must not contain a zero byte.
|
||||||
|
func (k Key) SetStringValue(name, value string) error {
|
||||||
|
return k.setStringValue(name, SZ, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetExpandStringValue sets the data and type of a name value
|
||||||
|
// under key k to value and EXPAND_SZ. The value must not contain a zero byte.
|
||||||
|
func (k Key) SetExpandStringValue(name, value string) error {
|
||||||
|
return k.setStringValue(name, EXPAND_SZ, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetStringsValue sets the data and type of a name value
|
||||||
|
// under key k to value and MULTI_SZ. The value strings
|
||||||
|
// must not contain a zero byte.
|
||||||
|
func (k Key) SetStringsValue(name string, value []string) error {
|
||||||
|
ss := ""
|
||||||
|
for _, s := range value {
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if s[i] == 0 {
|
||||||
|
return errors.New("string cannot have 0 inside")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ss += s + "\x00"
|
||||||
|
}
|
||||||
|
v := utf16.Encode([]rune(ss + "\x00"))
|
||||||
|
buf := (*[1 << 29]byte)(unsafe.Pointer(&v[0]))[:len(v)*2]
|
||||||
|
return k.setValue(name, MULTI_SZ, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBinaryValue sets the data and type of a name value
|
||||||
|
// under key k to value and BINARY.
|
||||||
|
func (k Key) SetBinaryValue(name string, value []byte) error {
|
||||||
|
return k.setValue(name, BINARY, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteValue removes a named value from the key k.
|
||||||
|
func (k Key) DeleteValue(name string) error {
|
||||||
|
return regDeleteValue(syscall.Handle(k), syscall.StringToUTF16Ptr(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadValueNames returns the value names of key k.
|
||||||
|
// The parameter n controls the number of returned names,
|
||||||
|
// analogous to the way os.File.Readdirnames works.
|
||||||
|
func (k Key) ReadValueNames(n int) ([]string, error) {
|
||||||
|
ki, err := k.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
names := make([]string, 0, ki.ValueCount)
|
||||||
|
buf := make([]uint16, ki.MaxValueNameLen+1) // extra room for terminating null character
|
||||||
|
loopItems:
|
||||||
|
for i := uint32(0); ; i++ {
|
||||||
|
if n > 0 {
|
||||||
|
if len(names) == n {
|
||||||
|
return names, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l := uint32(len(buf))
|
||||||
|
for {
|
||||||
|
err := regEnumValue(syscall.Handle(k), i, &buf[0], &l, nil, nil, nil, nil)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err == syscall.ERROR_MORE_DATA {
|
||||||
|
// Double buffer size and try again.
|
||||||
|
l = uint32(2 * len(buf))
|
||||||
|
buf = make([]uint16, l)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err == _ERROR_NO_MORE_ITEMS {
|
||||||
|
break loopItems
|
||||||
|
}
|
||||||
|
return names, err
|
||||||
|
}
|
||||||
|
names = append(names, syscall.UTF16ToString(buf[:l]))
|
||||||
|
}
|
||||||
|
if n > len(names) {
|
||||||
|
return names, io.EOF
|
||||||
|
}
|
||||||
|
return names, nil
|
||||||
|
}
|
120
vendor/golang.org/x/sys/windows/registry/zsyscall_windows.go
generated
vendored
Normal file
120
vendor/golang.org/x/sys/windows/registry/zsyscall_windows.go
generated
vendored
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
// Code generated by 'go generate'; DO NOT EDIT.
|
||||||
|
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
// Do the interface allocations only once for common
|
||||||
|
// Errno values.
|
||||||
|
const (
|
||||||
|
errnoERROR_IO_PENDING = 997
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||||
|
)
|
||||||
|
|
||||||
|
// errnoErr returns common boxed Errno values, to prevent
|
||||||
|
// allocations at runtime.
|
||||||
|
func errnoErr(e syscall.Errno) error {
|
||||||
|
switch e {
|
||||||
|
case 0:
|
||||||
|
return nil
|
||||||
|
case errnoERROR_IO_PENDING:
|
||||||
|
return errERROR_IO_PENDING
|
||||||
|
}
|
||||||
|
// TODO: add more here, after collecting data on the common
|
||||||
|
// error values see on Windows. (perhaps when running
|
||||||
|
// all.bat?)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
||||||
|
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||||
|
|
||||||
|
procRegCreateKeyExW = modadvapi32.NewProc("RegCreateKeyExW")
|
||||||
|
procRegDeleteKeyW = modadvapi32.NewProc("RegDeleteKeyW")
|
||||||
|
procRegSetValueExW = modadvapi32.NewProc("RegSetValueExW")
|
||||||
|
procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW")
|
||||||
|
procRegDeleteValueW = modadvapi32.NewProc("RegDeleteValueW")
|
||||||
|
procRegLoadMUIStringW = modadvapi32.NewProc("RegLoadMUIStringW")
|
||||||
|
procRegConnectRegistryW = modadvapi32.NewProc("RegConnectRegistryW")
|
||||||
|
procExpandEnvironmentStringsW = modkernel32.NewProc("ExpandEnvironmentStringsW")
|
||||||
|
)
|
||||||
|
|
||||||
|
func regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) {
|
||||||
|
r0, _, _ := syscall.Syscall9(procRegCreateKeyExW.Addr(), 9, uintptr(key), uintptr(unsafe.Pointer(subkey)), uintptr(reserved), uintptr(unsafe.Pointer(class)), uintptr(options), uintptr(desired), uintptr(unsafe.Pointer(sa)), uintptr(unsafe.Pointer(result)), uintptr(unsafe.Pointer(disposition)))
|
||||||
|
if r0 != 0 {
|
||||||
|
regerrno = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procRegDeleteKeyW.Addr(), 2, uintptr(key), uintptr(unsafe.Pointer(subkey)), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
regerrno = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype uint32, buf *byte, bufsize uint32) (regerrno error) {
|
||||||
|
r0, _, _ := syscall.Syscall6(procRegSetValueExW.Addr(), 6, uintptr(key), uintptr(unsafe.Pointer(valueName)), uintptr(reserved), uintptr(vtype), uintptr(unsafe.Pointer(buf)), uintptr(bufsize))
|
||||||
|
if r0 != 0 {
|
||||||
|
regerrno = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) {
|
||||||
|
r0, _, _ := syscall.Syscall9(procRegEnumValueW.Addr(), 8, uintptr(key), uintptr(index), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(valtype)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(buflen)), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
regerrno = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procRegDeleteValueW.Addr(), 2, uintptr(key), uintptr(unsafe.Pointer(name)), 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
regerrno = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func regLoadMUIString(key syscall.Handle, name *uint16, buf *uint16, buflen uint32, buflenCopied *uint32, flags uint32, dir *uint16) (regerrno error) {
|
||||||
|
r0, _, _ := syscall.Syscall9(procRegLoadMUIStringW.Addr(), 7, uintptr(key), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(unsafe.Pointer(buflenCopied)), uintptr(flags), uintptr(unsafe.Pointer(dir)), 0, 0)
|
||||||
|
if r0 != 0 {
|
||||||
|
regerrno = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func regConnectRegistry(machinename *uint16, key syscall.Handle, result *syscall.Handle) (regerrno error) {
|
||||||
|
r0, _, _ := syscall.Syscall(procRegConnectRegistryW.Addr(), 3, uintptr(unsafe.Pointer(machinename)), uintptr(key), uintptr(unsafe.Pointer(result)))
|
||||||
|
if r0 != 0 {
|
||||||
|
regerrno = syscall.Errno(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall(procExpandEnvironmentStringsW.Addr(), 3, uintptr(unsafe.Pointer(src)), uintptr(unsafe.Pointer(dst)), uintptr(size))
|
||||||
|
n = uint32(r0)
|
||||||
|
if n == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
56
vendor/golang.org/x/sys/windows/svc/debug/log.go
generated
vendored
Normal file
56
vendor/golang.org/x/sys/windows/svc/debug/log.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package debug
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Log interface allows different log implementations to be used.
|
||||||
|
type Log interface {
|
||||||
|
Close() error
|
||||||
|
Info(eid uint32, msg string) error
|
||||||
|
Warning(eid uint32, msg string) error
|
||||||
|
Error(eid uint32, msg string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConsoleLog provides access to the console.
|
||||||
|
type ConsoleLog struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates new ConsoleLog.
|
||||||
|
func New(source string) *ConsoleLog {
|
||||||
|
return &ConsoleLog{Name: source}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes console log l.
|
||||||
|
func (l *ConsoleLog) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *ConsoleLog) report(kind string, eid uint32, msg string) error {
|
||||||
|
s := l.Name + "." + kind + "(" + strconv.Itoa(int(eid)) + "): " + msg + "\n"
|
||||||
|
_, err := os.Stdout.Write([]byte(s))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info writes an information event msg with event id eid to the console l.
|
||||||
|
func (l *ConsoleLog) Info(eid uint32, msg string) error {
|
||||||
|
return l.report("info", eid, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warning writes an warning event msg with event id eid to the console l.
|
||||||
|
func (l *ConsoleLog) Warning(eid uint32, msg string) error {
|
||||||
|
return l.report("warn", eid, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error writes an error event msg with event id eid to the console l.
|
||||||
|
func (l *ConsoleLog) Error(eid uint32, msg string) error {
|
||||||
|
return l.report("error", eid, msg)
|
||||||
|
}
|
45
vendor/golang.org/x/sys/windows/svc/debug/service.go
generated
vendored
Normal file
45
vendor/golang.org/x/sys/windows/svc/debug/service.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
// Package debug provides facilities to execute svc.Handler on console.
|
||||||
|
//
|
||||||
|
package debug
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Run executes service name by calling appropriate handler function.
|
||||||
|
// The process is running on console, unlike real service. Use Ctrl+C to
|
||||||
|
// send "Stop" command to your service.
|
||||||
|
func Run(name string, handler svc.Handler) error {
|
||||||
|
cmds := make(chan svc.ChangeRequest)
|
||||||
|
changes := make(chan svc.Status)
|
||||||
|
|
||||||
|
sig := make(chan os.Signal)
|
||||||
|
signal.Notify(sig)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
status := svc.Status{State: svc.Stopped}
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-sig:
|
||||||
|
cmds <- svc.ChangeRequest{Cmd: svc.Stop, CurrentStatus: status}
|
||||||
|
case status = <-changes:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, errno := handler.Execute([]string{name}, cmds, changes)
|
||||||
|
if errno != 0 {
|
||||||
|
return syscall.Errno(errno)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
80
vendor/golang.org/x/sys/windows/svc/eventlog/install.go
generated
vendored
Normal file
80
vendor/golang.org/x/sys/windows/svc/eventlog/install.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package eventlog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
"golang.org/x/sys/windows/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Log levels.
|
||||||
|
Info = windows.EVENTLOG_INFORMATION_TYPE
|
||||||
|
Warning = windows.EVENTLOG_WARNING_TYPE
|
||||||
|
Error = windows.EVENTLOG_ERROR_TYPE
|
||||||
|
)
|
||||||
|
|
||||||
|
const addKeyName = `SYSTEM\CurrentControlSet\Services\EventLog\Application`
|
||||||
|
|
||||||
|
// Install modifies PC registry to allow logging with an event source src.
|
||||||
|
// It adds all required keys and values to the event log registry key.
|
||||||
|
// Install uses msgFile as the event message file. If useExpandKey is true,
|
||||||
|
// the event message file is installed as REG_EXPAND_SZ value,
|
||||||
|
// otherwise as REG_SZ. Use bitwise of log.Error, log.Warning and
|
||||||
|
// log.Info to specify events supported by the new event source.
|
||||||
|
func Install(src, msgFile string, useExpandKey bool, eventsSupported uint32) error {
|
||||||
|
appkey, err := registry.OpenKey(registry.LOCAL_MACHINE, addKeyName, registry.CREATE_SUB_KEY)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer appkey.Close()
|
||||||
|
|
||||||
|
sk, alreadyExist, err := registry.CreateKey(appkey, src, registry.SET_VALUE)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer sk.Close()
|
||||||
|
if alreadyExist {
|
||||||
|
return errors.New(addKeyName + `\` + src + " registry key already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sk.SetDWordValue("CustomSource", 1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if useExpandKey {
|
||||||
|
err = sk.SetExpandStringValue("EventMessageFile", msgFile)
|
||||||
|
} else {
|
||||||
|
err = sk.SetStringValue("EventMessageFile", msgFile)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = sk.SetDWordValue("TypesSupported", eventsSupported)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstallAsEventCreate is the same as Install, but uses
|
||||||
|
// %SystemRoot%\System32\EventCreate.exe as the event message file.
|
||||||
|
func InstallAsEventCreate(src string, eventsSupported uint32) error {
|
||||||
|
return Install(src, "%SystemRoot%\\System32\\EventCreate.exe", true, eventsSupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove deletes all registry elements installed by the correspondent Install.
|
||||||
|
func Remove(src string) error {
|
||||||
|
appkey, err := registry.OpenKey(registry.LOCAL_MACHINE, addKeyName, registry.SET_VALUE)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer appkey.Close()
|
||||||
|
return registry.DeleteKey(appkey, src)
|
||||||
|
}
|
70
vendor/golang.org/x/sys/windows/svc/eventlog/log.go
generated
vendored
Normal file
70
vendor/golang.org/x/sys/windows/svc/eventlog/log.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
// Package eventlog implements access to Windows event log.
|
||||||
|
//
|
||||||
|
package eventlog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Log provides access to the system log.
|
||||||
|
type Log struct {
|
||||||
|
Handle windows.Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open retrieves a handle to the specified event log.
|
||||||
|
func Open(source string) (*Log, error) {
|
||||||
|
return OpenRemote("", source)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenRemote does the same as Open, but on different computer host.
|
||||||
|
func OpenRemote(host, source string) (*Log, error) {
|
||||||
|
if source == "" {
|
||||||
|
return nil, errors.New("Specify event log source")
|
||||||
|
}
|
||||||
|
var s *uint16
|
||||||
|
if host != "" {
|
||||||
|
s = syscall.StringToUTF16Ptr(host)
|
||||||
|
}
|
||||||
|
h, err := windows.RegisterEventSource(s, syscall.StringToUTF16Ptr(source))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Log{Handle: h}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes event log l.
|
||||||
|
func (l *Log) Close() error {
|
||||||
|
return windows.DeregisterEventSource(l.Handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Log) report(etype uint16, eid uint32, msg string) error {
|
||||||
|
ss := []*uint16{syscall.StringToUTF16Ptr(msg)}
|
||||||
|
return windows.ReportEvent(l.Handle, etype, 0, eid, 0, 1, 0, &ss[0], nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info writes an information event msg with event id eid to the end of event log l.
|
||||||
|
// When EventCreate.exe is used, eid must be between 1 and 1000.
|
||||||
|
func (l *Log) Info(eid uint32, msg string) error {
|
||||||
|
return l.report(windows.EVENTLOG_INFORMATION_TYPE, eid, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warning writes an warning event msg with event id eid to the end of event log l.
|
||||||
|
// When EventCreate.exe is used, eid must be between 1 and 1000.
|
||||||
|
func (l *Log) Warning(eid uint32, msg string) error {
|
||||||
|
return l.report(windows.EVENTLOG_WARNING_TYPE, eid, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error writes an error event msg with event id eid to the end of event log l.
|
||||||
|
// When EventCreate.exe is used, eid must be between 1 and 1000.
|
||||||
|
func (l *Log) Error(eid uint32, msg string) error {
|
||||||
|
return l.report(windows.EVENTLOG_ERROR_TYPE, eid, msg)
|
||||||
|
}
|
145
vendor/golang.org/x/sys/windows/svc/mgr/config.go
generated
vendored
Normal file
145
vendor/golang.org/x/sys/windows/svc/mgr/config.go
generated
vendored
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package mgr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Service start types.
|
||||||
|
StartManual = windows.SERVICE_DEMAND_START // the service must be started manually
|
||||||
|
StartAutomatic = windows.SERVICE_AUTO_START // the service will start by itself whenever the computer reboots
|
||||||
|
StartDisabled = windows.SERVICE_DISABLED // the service cannot be started
|
||||||
|
|
||||||
|
// The severity of the error, and action taken,
|
||||||
|
// if this service fails to start.
|
||||||
|
ErrorCritical = windows.SERVICE_ERROR_CRITICAL
|
||||||
|
ErrorIgnore = windows.SERVICE_ERROR_IGNORE
|
||||||
|
ErrorNormal = windows.SERVICE_ERROR_NORMAL
|
||||||
|
ErrorSevere = windows.SERVICE_ERROR_SEVERE
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(brainman): Password is not returned by windows.QueryServiceConfig, not sure how to get it.
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
ServiceType uint32
|
||||||
|
StartType uint32
|
||||||
|
ErrorControl uint32
|
||||||
|
BinaryPathName string // fully qualified path to the service binary file, can also include arguments for an auto-start service
|
||||||
|
LoadOrderGroup string
|
||||||
|
TagId uint32
|
||||||
|
Dependencies []string
|
||||||
|
ServiceStartName string // name of the account under which the service should run
|
||||||
|
DisplayName string
|
||||||
|
Password string
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
func toString(p *uint16) string {
|
||||||
|
if p == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return syscall.UTF16ToString((*[4096]uint16)(unsafe.Pointer(p))[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func toStringSlice(ps *uint16) []string {
|
||||||
|
if ps == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
r := make([]string, 0)
|
||||||
|
for from, i, p := 0, 0, (*[1 << 24]uint16)(unsafe.Pointer(ps)); true; i++ {
|
||||||
|
if p[i] == 0 {
|
||||||
|
// empty string marks the end
|
||||||
|
if i <= from {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
r = append(r, string(utf16.Decode(p[from:i])))
|
||||||
|
from = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config retrieves service s configuration paramteres.
|
||||||
|
func (s *Service) Config() (Config, error) {
|
||||||
|
var p *windows.QUERY_SERVICE_CONFIG
|
||||||
|
n := uint32(1024)
|
||||||
|
for {
|
||||||
|
b := make([]byte, n)
|
||||||
|
p = (*windows.QUERY_SERVICE_CONFIG)(unsafe.Pointer(&b[0]))
|
||||||
|
err := windows.QueryServiceConfig(s.Handle, p, n, &n)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err.(syscall.Errno) != syscall.ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
return Config{}, err
|
||||||
|
}
|
||||||
|
if n <= uint32(len(b)) {
|
||||||
|
return Config{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := s.queryServiceConfig2(windows.SERVICE_CONFIG_DESCRIPTION)
|
||||||
|
if err != nil {
|
||||||
|
return Config{}, err
|
||||||
|
}
|
||||||
|
p2 := (*windows.SERVICE_DESCRIPTION)(unsafe.Pointer(&b[0]))
|
||||||
|
|
||||||
|
return Config{
|
||||||
|
ServiceType: p.ServiceType,
|
||||||
|
StartType: p.StartType,
|
||||||
|
ErrorControl: p.ErrorControl,
|
||||||
|
BinaryPathName: toString(p.BinaryPathName),
|
||||||
|
LoadOrderGroup: toString(p.LoadOrderGroup),
|
||||||
|
TagId: p.TagId,
|
||||||
|
Dependencies: toStringSlice(p.Dependencies),
|
||||||
|
ServiceStartName: toString(p.ServiceStartName),
|
||||||
|
DisplayName: toString(p.DisplayName),
|
||||||
|
Description: toString(p2.Description),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateDescription(handle windows.Handle, desc string) error {
|
||||||
|
d := windows.SERVICE_DESCRIPTION{Description: toPtr(desc)}
|
||||||
|
return windows.ChangeServiceConfig2(handle,
|
||||||
|
windows.SERVICE_CONFIG_DESCRIPTION, (*byte)(unsafe.Pointer(&d)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateConfig updates service s configuration parameters.
|
||||||
|
func (s *Service) UpdateConfig(c Config) error {
|
||||||
|
err := windows.ChangeServiceConfig(s.Handle, c.ServiceType, c.StartType,
|
||||||
|
c.ErrorControl, toPtr(c.BinaryPathName), toPtr(c.LoadOrderGroup),
|
||||||
|
nil, toStringBlock(c.Dependencies), toPtr(c.ServiceStartName),
|
||||||
|
toPtr(c.Password), toPtr(c.DisplayName))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return updateDescription(s.Handle, c.Description)
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryServiceConfig2 calls Windows QueryServiceConfig2 with infoLevel parameter and returns retrieved service configuration information.
|
||||||
|
func (s *Service) queryServiceConfig2(infoLevel uint32) ([]byte, error) {
|
||||||
|
n := uint32(1024)
|
||||||
|
for {
|
||||||
|
b := make([]byte, n)
|
||||||
|
err := windows.QueryServiceConfig2(s.Handle, infoLevel, &b[0], n, &n)
|
||||||
|
if err == nil {
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
if err.(syscall.Errno) != syscall.ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if n <= uint32(len(b)) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
162
vendor/golang.org/x/sys/windows/svc/mgr/mgr.go
generated
vendored
Normal file
162
vendor/golang.org/x/sys/windows/svc/mgr/mgr.go
generated
vendored
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
// Package mgr can be used to manage Windows service programs.
|
||||||
|
// It can be used to install and remove them. It can also start,
|
||||||
|
// stop and pause them. The package can query / change current
|
||||||
|
// service state and config parameters.
|
||||||
|
//
|
||||||
|
package mgr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mgr is used to manage Windows service.
|
||||||
|
type Mgr struct {
|
||||||
|
Handle windows.Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect establishes a connection to the service control manager.
|
||||||
|
func Connect() (*Mgr, error) {
|
||||||
|
return ConnectRemote("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectRemote establishes a connection to the
|
||||||
|
// service control manager on computer named host.
|
||||||
|
func ConnectRemote(host string) (*Mgr, error) {
|
||||||
|
var s *uint16
|
||||||
|
if host != "" {
|
||||||
|
s = syscall.StringToUTF16Ptr(host)
|
||||||
|
}
|
||||||
|
h, err := windows.OpenSCManager(s, nil, windows.SC_MANAGER_ALL_ACCESS)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Mgr{Handle: h}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnect closes connection to the service control manager m.
|
||||||
|
func (m *Mgr) Disconnect() error {
|
||||||
|
return windows.CloseServiceHandle(m.Handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func toPtr(s string) *uint16 {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return syscall.StringToUTF16Ptr(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// toStringBlock terminates strings in ss with 0, and then
|
||||||
|
// concatenates them together. It also adds extra 0 at the end.
|
||||||
|
func toStringBlock(ss []string) *uint16 {
|
||||||
|
if len(ss) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
t := ""
|
||||||
|
for _, s := range ss {
|
||||||
|
if s != "" {
|
||||||
|
t += s + "\x00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
t += "\x00"
|
||||||
|
return &utf16.Encode([]rune(t))[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateService installs new service name on the system.
|
||||||
|
// The service will be executed by running exepath binary.
|
||||||
|
// Use config c to specify service parameters.
|
||||||
|
// Any args will be passed as command-line arguments when
|
||||||
|
// the service is started; these arguments are distinct from
|
||||||
|
// the arguments passed to Service.Start or via the "Start
|
||||||
|
// parameters" field in the service's Properties dialog box.
|
||||||
|
func (m *Mgr) CreateService(name, exepath string, c Config, args ...string) (*Service, error) {
|
||||||
|
if c.StartType == 0 {
|
||||||
|
c.StartType = StartManual
|
||||||
|
}
|
||||||
|
if c.ErrorControl == 0 {
|
||||||
|
c.ErrorControl = ErrorNormal
|
||||||
|
}
|
||||||
|
if c.ServiceType == 0 {
|
||||||
|
c.ServiceType = windows.SERVICE_WIN32_OWN_PROCESS
|
||||||
|
}
|
||||||
|
s := syscall.EscapeArg(exepath)
|
||||||
|
for _, v := range args {
|
||||||
|
s += " " + syscall.EscapeArg(v)
|
||||||
|
}
|
||||||
|
h, err := windows.CreateService(m.Handle, toPtr(name), toPtr(c.DisplayName),
|
||||||
|
windows.SERVICE_ALL_ACCESS, c.ServiceType,
|
||||||
|
c.StartType, c.ErrorControl, toPtr(s), toPtr(c.LoadOrderGroup),
|
||||||
|
nil, toStringBlock(c.Dependencies), toPtr(c.ServiceStartName), toPtr(c.Password))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if c.Description != "" {
|
||||||
|
err = updateDescription(h, c.Description)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &Service{Name: name, Handle: h}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenService retrieves access to service name, so it can
|
||||||
|
// be interrogated and controlled.
|
||||||
|
func (m *Mgr) OpenService(name string) (*Service, error) {
|
||||||
|
h, err := windows.OpenService(m.Handle, syscall.StringToUTF16Ptr(name), windows.SERVICE_ALL_ACCESS)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Service{Name: name, Handle: h}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListServices enumerates services in the specified
|
||||||
|
// service control manager database m.
|
||||||
|
// If the caller does not have the SERVICE_QUERY_STATUS
|
||||||
|
// access right to a service, the service is silently
|
||||||
|
// omitted from the list of services returned.
|
||||||
|
func (m *Mgr) ListServices() ([]string, error) {
|
||||||
|
var err error
|
||||||
|
var bytesNeeded, servicesReturned uint32
|
||||||
|
var buf []byte
|
||||||
|
for {
|
||||||
|
var p *byte
|
||||||
|
if len(buf) > 0 {
|
||||||
|
p = &buf[0]
|
||||||
|
}
|
||||||
|
err = windows.EnumServicesStatusEx(m.Handle, windows.SC_ENUM_PROCESS_INFO,
|
||||||
|
windows.SERVICE_WIN32, windows.SERVICE_STATE_ALL,
|
||||||
|
p, uint32(len(buf)), &bytesNeeded, &servicesReturned, nil, nil)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != syscall.ERROR_MORE_DATA {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if bytesNeeded <= uint32(len(buf)) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buf = make([]byte, bytesNeeded)
|
||||||
|
}
|
||||||
|
if servicesReturned == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
services := (*[1 << 20]windows.ENUM_SERVICE_STATUS_PROCESS)(unsafe.Pointer(&buf[0]))[:servicesReturned]
|
||||||
|
var names []string
|
||||||
|
for _, s := range services {
|
||||||
|
name := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(s.ServiceName))[:])
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
return names, nil
|
||||||
|
}
|
96
vendor/golang.org/x/sys/windows/svc/mgr/recovery.go
generated
vendored
Normal file
96
vendor/golang.org/x/sys/windows/svc/mgr/recovery.go
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package mgr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Possible recovery actions that the service control manager can perform.
|
||||||
|
NoAction = windows.SC_ACTION_NONE // no action
|
||||||
|
ComputerReboot = windows.SC_ACTION_REBOOT // reboot the computer
|
||||||
|
ServiceRestart = windows.SC_ACTION_RESTART // restart the service
|
||||||
|
RunCommand = windows.SC_ACTION_RUN_COMMAND // run a command
|
||||||
|
)
|
||||||
|
|
||||||
|
// RecoveryAction represents an action that the service control manager can perform when service fails.
|
||||||
|
// A service is considered failed when it terminates without reporting a status of SERVICE_STOPPED to the service controller.
|
||||||
|
type RecoveryAction struct {
|
||||||
|
Type int // one of NoAction, ComputerReboot, ServiceRestart or RunCommand
|
||||||
|
Delay time.Duration // the time to wait before performing the specified action
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRecoveryActions sets actions that service controller performs when service fails and
|
||||||
|
// the time after which to reset the service failure count to zero if there are no failures, in seconds.
|
||||||
|
// Specify INFINITE to indicate that service failure count should never be reset.
|
||||||
|
func (s *Service) SetRecoveryActions(recoveryActions []RecoveryAction, resetPeriod uint32) error {
|
||||||
|
if recoveryActions == nil {
|
||||||
|
return errors.New("recoveryActions cannot be nil")
|
||||||
|
}
|
||||||
|
actions := []windows.SC_ACTION{}
|
||||||
|
for _, a := range recoveryActions {
|
||||||
|
action := windows.SC_ACTION{
|
||||||
|
Type: uint32(a.Type),
|
||||||
|
Delay: uint32(a.Delay.Nanoseconds() / 1000000),
|
||||||
|
}
|
||||||
|
actions = append(actions, action)
|
||||||
|
}
|
||||||
|
rActions := windows.SERVICE_FAILURE_ACTIONS{
|
||||||
|
ActionsCount: uint32(len(actions)),
|
||||||
|
Actions: &actions[0],
|
||||||
|
ResetPeriod: resetPeriod,
|
||||||
|
}
|
||||||
|
return windows.ChangeServiceConfig2(s.Handle, windows.SERVICE_CONFIG_FAILURE_ACTIONS, (*byte)(unsafe.Pointer(&rActions)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecoveryActions returns actions that service controller performs when service fails.
|
||||||
|
// The service control manager counts the number of times service s has failed since the system booted.
|
||||||
|
// The count is reset to 0 if the service has not failed for ResetPeriod seconds.
|
||||||
|
// When the service fails for the Nth time, the service controller performs the action specified in element [N-1] of returned slice.
|
||||||
|
// If N is greater than slice length, the service controller repeats the last action in the slice.
|
||||||
|
func (s *Service) RecoveryActions() ([]RecoveryAction, error) {
|
||||||
|
b, err := s.queryServiceConfig2(windows.SERVICE_CONFIG_FAILURE_ACTIONS)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p := (*windows.SERVICE_FAILURE_ACTIONS)(unsafe.Pointer(&b[0]))
|
||||||
|
if p.Actions == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var recoveryActions []RecoveryAction
|
||||||
|
actions := (*[1024]windows.SC_ACTION)(unsafe.Pointer(p.Actions))[:p.ActionsCount]
|
||||||
|
for _, action := range actions {
|
||||||
|
recoveryActions = append(recoveryActions, RecoveryAction{Type: int(action.Type), Delay: time.Duration(action.Delay) * time.Millisecond})
|
||||||
|
}
|
||||||
|
return recoveryActions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetRecoveryActions deletes both reset period and array of failure actions.
|
||||||
|
func (s *Service) ResetRecoveryActions() error {
|
||||||
|
actions := make([]windows.SC_ACTION, 1)
|
||||||
|
rActions := windows.SERVICE_FAILURE_ACTIONS{
|
||||||
|
Actions: &actions[0],
|
||||||
|
}
|
||||||
|
return windows.ChangeServiceConfig2(s.Handle, windows.SERVICE_CONFIG_FAILURE_ACTIONS, (*byte)(unsafe.Pointer(&rActions)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetPeriod is the time after which to reset the service failure
|
||||||
|
// count to zero if there are no failures, in seconds.
|
||||||
|
func (s *Service) ResetPeriod() (uint32, error) {
|
||||||
|
b, err := s.queryServiceConfig2(windows.SERVICE_CONFIG_FAILURE_ACTIONS)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
p := (*windows.SERVICE_FAILURE_ACTIONS)(unsafe.Pointer(&b[0]))
|
||||||
|
return p.ResetPeriod, nil
|
||||||
|
}
|
72
vendor/golang.org/x/sys/windows/svc/mgr/service.go
generated
vendored
Normal file
72
vendor/golang.org/x/sys/windows/svc/mgr/service.go
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package mgr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
"golang.org/x/sys/windows/svc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(brainman): Use EnumDependentServices to enumerate dependent services.
|
||||||
|
|
||||||
|
// Service is used to access Windows service.
|
||||||
|
type Service struct {
|
||||||
|
Name string
|
||||||
|
Handle windows.Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete marks service s for deletion from the service control manager database.
|
||||||
|
func (s *Service) Delete() error {
|
||||||
|
return windows.DeleteService(s.Handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close relinquish access to the service s.
|
||||||
|
func (s *Service) Close() error {
|
||||||
|
return windows.CloseServiceHandle(s.Handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start starts service s.
|
||||||
|
// args will be passed to svc.Handler.Execute.
|
||||||
|
func (s *Service) Start(args ...string) error {
|
||||||
|
var p **uint16
|
||||||
|
if len(args) > 0 {
|
||||||
|
vs := make([]*uint16, len(args))
|
||||||
|
for i := range vs {
|
||||||
|
vs[i] = syscall.StringToUTF16Ptr(args[i])
|
||||||
|
}
|
||||||
|
p = &vs[0]
|
||||||
|
}
|
||||||
|
return windows.StartService(s.Handle, uint32(len(args)), p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Control sends state change request c to the servce s.
|
||||||
|
func (s *Service) Control(c svc.Cmd) (svc.Status, error) {
|
||||||
|
var t windows.SERVICE_STATUS
|
||||||
|
err := windows.ControlService(s.Handle, uint32(c), &t)
|
||||||
|
if err != nil {
|
||||||
|
return svc.Status{}, err
|
||||||
|
}
|
||||||
|
return svc.Status{
|
||||||
|
State: svc.State(t.CurrentState),
|
||||||
|
Accepts: svc.Accepted(t.ControlsAccepted),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query returns current status of service s.
|
||||||
|
func (s *Service) Query() (svc.Status, error) {
|
||||||
|
var t windows.SERVICE_STATUS
|
||||||
|
err := windows.QueryServiceStatus(s.Handle, &t)
|
||||||
|
if err != nil {
|
||||||
|
return svc.Status{}, err
|
||||||
|
}
|
||||||
|
return svc.Status{
|
||||||
|
State: svc.State(t.CurrentState),
|
||||||
|
Accepts: svc.Accepted(t.ControlsAccepted),
|
||||||
|
}, nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user