diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index f6d3d00fdc..ad22528859 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -2939,6 +2939,10 @@ "ImportPath": "golang.org/x/sys/windows", "Rev": "95c6576299259db960f6c5b9b69ea52422860fce" }, + { + "ImportPath": "golang.org/x/sys/windows/svc", + "Rev": "95c6576299259db960f6c5b9b69ea52422860fce" + }, { "ImportPath": "golang.org/x/text/cases", "Rev": "b19bf474d317b857955b12035d2c5acb57ce8b01" diff --git a/Godeps/LICENSES b/Godeps/LICENSES index 3136937a6d..a41c79856f 100644 --- a/Godeps/LICENSES +++ b/Godeps/LICENSES @@ -89349,6 +89349,41 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================================================ +================================================================================ += vendor/golang.org/x/sys/windows/svc licensed under: = + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + += vendor/golang.org/x/sys/LICENSE 5d4950ecb7b26d2c5e4e7b4e0dd74707 +================================================================================ + + ================================================================================ = vendor/golang.org/x/text/cases licensed under: = diff --git a/cmd/kube-proxy/app/BUILD b/cmd/kube-proxy/app/BUILD index 7082c8f51b..3ae5ab8f04 100644 --- a/cmd/kube-proxy/app/BUILD +++ b/cmd/kube-proxy/app/BUILD @@ -13,36 +13,47 @@ go_library( "server.go", ] + select({ "@io_bazel_rules_go//go/platform:android": [ + "init_others.go", "server_others.go", ], "@io_bazel_rules_go//go/platform:darwin": [ + "init_others.go", "server_others.go", ], "@io_bazel_rules_go//go/platform:dragonfly": [ + "init_others.go", "server_others.go", ], "@io_bazel_rules_go//go/platform:freebsd": [ + "init_others.go", "server_others.go", ], "@io_bazel_rules_go//go/platform:linux": [ + "init_others.go", "server_others.go", ], "@io_bazel_rules_go//go/platform:nacl": [ + "init_others.go", "server_others.go", ], "@io_bazel_rules_go//go/platform:netbsd": [ + "init_others.go", "server_others.go", ], "@io_bazel_rules_go//go/platform:openbsd": [ + "init_others.go", "server_others.go", ], "@io_bazel_rules_go//go/platform:plan9": [ + "init_others.go", "server_others.go", ], "@io_bazel_rules_go//go/platform:solaris": [ + "init_others.go", "server_others.go", ], "@io_bazel_rules_go//go/platform:windows": [ + "init_windows.go", "server_windows.go", ], "//conditions:default": [], @@ -177,6 +188,7 @@ go_library( "//pkg/proxy/winkernel:go_default_library", "//pkg/proxy/winuserspace:go_default_library", "//pkg/util/netsh:go_default_library", + "//pkg/windows/service:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library", ], diff --git a/cmd/kube-proxy/app/init_others.go b/cmd/kube-proxy/app/init_others.go new file mode 100644 index 0000000000..6c0e6b8c7d --- /dev/null +++ b/cmd/kube-proxy/app/init_others.go @@ -0,0 +1,30 @@ +// +build !windows + +/* +Copyright 2018 The Kubernetes 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 app + +import ( + "github.com/spf13/pflag" +) + +func initForOS(service bool) error { + return nil +} + +func (o *Options) addOSFlags(fs *pflag.FlagSet) { +} diff --git a/cmd/kube-proxy/app/init_windows.go b/cmd/kube-proxy/app/init_windows.go new file mode 100644 index 0000000000..85cafcda53 --- /dev/null +++ b/cmd/kube-proxy/app/init_windows.go @@ -0,0 +1,40 @@ +// +build windows + +/* +Copyright 2018 The Kubernetes 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 app + +import ( + "k8s.io/kubernetes/pkg/windows/service" + + "github.com/spf13/pflag" +) + +const ( + serviceName = "kube-proxy" +) + +func initForOS(windowsService bool) error { + if windowsService { + return service.InitService(serviceName) + } + return nil +} + +func (o *Options) addOSFlags(fs *pflag.FlagSet) { + fs.BoolVar(&o.WindowsService, "windows-service", o.WindowsService, "Enable Windows Service Control Manager API integration") +} diff --git a/cmd/kube-proxy/app/server.go b/cmd/kube-proxy/app/server.go index ff30980da2..7b42e66168 100644 --- a/cmd/kube-proxy/app/server.go +++ b/cmd/kube-proxy/app/server.go @@ -100,6 +100,9 @@ type Options struct { CleanupAndExit bool // CleanupIPVS, when true, makes the proxy server clean up ipvs rules before running. CleanupIPVS bool + // WindowsService should be set to true if kube-proxy is running as a service on Windows. + // Its corresponding flag only gets registered in Windows builds + WindowsService bool // config is the proxy server's configuration object. config *kubeproxyconfig.KubeProxyConfiguration @@ -119,6 +122,7 @@ type Options struct { // AddFlags adds flags to fs and binds them to options. func (o *Options) AddFlags(fs *pflag.FlagSet) { + o.addOSFlags(fs) fs.StringVar(&o.ConfigFile, "config", o.ConfigFile, "The path to the configuration file.") fs.StringVar(&o.WriteConfigTo, "write-config-to", o.WriteConfigTo, "If set, write the default configuration values to this file and exit.") fs.BoolVar(&o.CleanupAndExit, "cleanup-iptables", o.CleanupAndExit, "If true cleanup iptables and ipvs rules and exit.") @@ -344,6 +348,10 @@ with the apiserver API to configure the proxy.`, verflag.PrintAndExitIfRequested() utilflag.PrintFlags(cmd.Flags()) + if err := initForOS(opts.WindowsService); err != nil { + glog.Fatalf("failed OS init: %v", err) + } + cmdutil.CheckErr(opts.Complete()) cmdutil.CheckErr(opts.Validate(args)) cmdutil.CheckErr(opts.Run()) diff --git a/cmd/kubelet/app/BUILD b/cmd/kubelet/app/BUILD index 968b7a4512..cbfb90ff13 100644 --- a/cmd/kubelet/app/BUILD +++ b/cmd/kubelet/app/BUILD @@ -20,36 +20,47 @@ go_library( "server.go", ] + select({ "@io_bazel_rules_go//go/platform:android": [ + "init_others.go", "server_unsupported.go", ], "@io_bazel_rules_go//go/platform:darwin": [ + "init_others.go", "server_unsupported.go", ], "@io_bazel_rules_go//go/platform:dragonfly": [ + "init_others.go", "server_unsupported.go", ], "@io_bazel_rules_go//go/platform:freebsd": [ + "init_others.go", "server_unsupported.go", ], "@io_bazel_rules_go//go/platform:linux": [ + "init_others.go", "server_linux.go", ], "@io_bazel_rules_go//go/platform:nacl": [ + "init_others.go", "server_unsupported.go", ], "@io_bazel_rules_go//go/platform:netbsd": [ + "init_others.go", "server_unsupported.go", ], "@io_bazel_rules_go//go/platform:openbsd": [ + "init_others.go", "server_unsupported.go", ], "@io_bazel_rules_go//go/platform:plan9": [ + "init_others.go", "server_unsupported.go", ], "@io_bazel_rules_go//go/platform:solaris": [ + "init_others.go", "server_unsupported.go", ], "@io_bazel_rules_go//go/platform:windows": [ + "init_windows.go", "server_unsupported.go", ], "//conditions:default": [], @@ -162,6 +173,9 @@ go_library( "@io_bazel_rules_go//go/platform:linux": [ "//vendor/golang.org/x/exp/inotify:go_default_library", ], + "@io_bazel_rules_go//go/platform:windows": [ + "//pkg/windows/service:go_default_library", + ], "//conditions:default": [], }), ) diff --git a/cmd/kubelet/app/init_others.go b/cmd/kubelet/app/init_others.go new file mode 100644 index 0000000000..4b19ac91b2 --- /dev/null +++ b/cmd/kubelet/app/init_others.go @@ -0,0 +1,23 @@ +// +build !windows + +/* +Copyright 2018 The Kubernetes 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 app + +func initForOS(service bool) error { + return nil +} diff --git a/cmd/kubelet/app/init_windows.go b/cmd/kubelet/app/init_windows.go new file mode 100644 index 0000000000..a2da4cf049 --- /dev/null +++ b/cmd/kubelet/app/init_windows.go @@ -0,0 +1,34 @@ +// +build windows + +/* +Copyright 2018 The Kubernetes 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 app + +import ( + "k8s.io/kubernetes/pkg/windows/service" +) + +const ( + serviceName = "kubelet" +) + +func initForOS(windowsService bool) error { + if windowsService { + return service.InitService(serviceName) + } + return nil +} diff --git a/cmd/kubelet/app/options/BUILD b/cmd/kubelet/app/options/BUILD index 0194dc7894..db6e6a9a86 100644 --- a/cmd/kubelet/app/options/BUILD +++ b/cmd/kubelet/app/options/BUILD @@ -15,36 +15,47 @@ go_library( ] + select({ "@io_bazel_rules_go//go/platform:android": [ "globalflags_other.go", + "osflags_others.go", ], "@io_bazel_rules_go//go/platform:darwin": [ "globalflags_other.go", + "osflags_others.go", ], "@io_bazel_rules_go//go/platform:dragonfly": [ "globalflags_other.go", + "osflags_others.go", ], "@io_bazel_rules_go//go/platform:freebsd": [ "globalflags_other.go", + "osflags_others.go", ], "@io_bazel_rules_go//go/platform:linux": [ "globalflags_linux.go", + "osflags_others.go", ], "@io_bazel_rules_go//go/platform:nacl": [ "globalflags_other.go", + "osflags_others.go", ], "@io_bazel_rules_go//go/platform:netbsd": [ "globalflags_other.go", + "osflags_others.go", ], "@io_bazel_rules_go//go/platform:openbsd": [ "globalflags_other.go", + "osflags_others.go", ], "@io_bazel_rules_go//go/platform:plan9": [ "globalflags_other.go", + "osflags_others.go", ], "@io_bazel_rules_go//go/platform:solaris": [ "globalflags_other.go", + "osflags_others.go", ], "@io_bazel_rules_go//go/platform:windows": [ "globalflags_other.go", + "osflags_windows.go", ], "//conditions:default": [], }), diff --git a/cmd/kubelet/app/options/options.go b/cmd/kubelet/app/options/options.go index c881423451..d359ac0705 100644 --- a/cmd/kubelet/app/options/options.go +++ b/cmd/kubelet/app/options/options.go @@ -125,6 +125,10 @@ type KubeletFlags struct { // cAdvisorPort is the port of the localhost cAdvisor endpoint (set to 0 to disable) CAdvisorPort int32 + // WindowsService should be set to true if kubelet is running as a service on Windows. + // Its corresponding flag only gets registered in Windows builds. + WindowsService bool + // EXPERIMENTAL FLAGS // Whitelist of unsafe sysctls or sysctl patterns (ending in *). // +optional @@ -329,6 +333,7 @@ func (s *KubeletServer) AddFlags(fs *pflag.FlagSet) { // AddFlags adds flags for a specific KubeletFlags to the specified FlagSet func (f *KubeletFlags) AddFlags(fs *pflag.FlagSet) { f.ContainerRuntimeOptions.AddFlags(fs) + f.addOSFlags(fs) fs.StringVar(&f.KubeletConfigFile, "config", f.KubeletConfigFile, "The Kubelet will load its initial configuration from this file. The path may be absolute or relative; relative paths start at the Kubelet's current working directory. Omit this flag to use the built-in default configuration values. Command-line flags override configuration from this file.") fs.StringVar(&f.KubeConfig, "kubeconfig", f.KubeConfig, "Path to a kubeconfig file, specifying how to connect to the API server. Providing --kubeconfig enables API server mode, omitting --kubeconfig enables standalone mode.") diff --git a/cmd/kubelet/app/options/osflags_others.go b/cmd/kubelet/app/options/osflags_others.go new file mode 100644 index 0000000000..ab4c0ac1c6 --- /dev/null +++ b/cmd/kubelet/app/options/osflags_others.go @@ -0,0 +1,26 @@ +// +build !windows + +/* +Copyright 2018 The Kubernetes 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 options + +import ( + "github.com/spf13/pflag" +) + +func (f *KubeletFlags) addOSFlags(fs *pflag.FlagSet) { +} diff --git a/cmd/kubelet/app/options/osflags_windows.go b/cmd/kubelet/app/options/osflags_windows.go new file mode 100644 index 0000000000..8923f5d6f1 --- /dev/null +++ b/cmd/kubelet/app/options/osflags_windows.go @@ -0,0 +1,27 @@ +// +build windows + +/* +Copyright 2018 The Kubernetes 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 options + +import ( + "github.com/spf13/pflag" +) + +func (f *KubeletFlags) addOSFlags(fs *pflag.FlagSet) { + fs.BoolVar(&f.WindowsService, "windows-service", f.WindowsService, "Enable Windows Service Control Manager API integration") +} diff --git a/cmd/kubelet/app/server.go b/cmd/kubelet/app/server.go index b355640d6e..327d372f15 100644 --- a/cmd/kubelet/app/server.go +++ b/cmd/kubelet/app/server.go @@ -373,6 +373,9 @@ func UnsecuredDependencies(s *options.KubeletServer) (*kubelet.Dependencies, err func Run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies) error { // To help debugging, immediately log version glog.Infof("Version: %+v", version.Get()) + if err := initForOS(s.KubeletFlags.WindowsService); err != nil { + return fmt.Errorf("failed OS init: %v", err) + } if err := run(s, kubeDeps); err != nil { return fmt.Errorf("failed to run Kubelet: %v", err) } diff --git a/pkg/BUILD b/pkg/BUILD index 9b5868573b..037dbecae9 100644 --- a/pkg/BUILD +++ b/pkg/BUILD @@ -102,6 +102,7 @@ filegroup( "//pkg/version:all-srcs", "//pkg/volume:all-srcs", "//pkg/watch/json:all-srcs", + "//pkg/windows/service:all-srcs", ], tags = ["automanaged"], ) diff --git a/pkg/windows/service/BUILD b/pkg/windows/service/BUILD new file mode 100644 index 0000000000..ea07fc9e4d --- /dev/null +++ b/pkg/windows/service/BUILD @@ -0,0 +1,38 @@ +package(default_visibility = ["//visibility:public"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", +) + +go_library( + name = "go_default_library", + srcs = select({ + "@io_bazel_rules_go//go/platform:windows": [ + "service.go", + ], + "//conditions:default": [], + }), + importpath = "k8s.io/kubernetes/pkg/windows/service", + deps = select({ + "@io_bazel_rules_go//go/platform:windows": [ + "//vendor/github.com/golang/glog:go_default_library", + "//vendor/golang.org/x/sys/windows:go_default_library", + "//vendor/golang.org/x/sys/windows/svc:go_default_library", + ], + "//conditions:default": [], + }), +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], +) diff --git a/pkg/windows/service/service.go b/pkg/windows/service/service.go new file mode 100644 index 0000000000..acc48246f1 --- /dev/null +++ b/pkg/windows/service/service.go @@ -0,0 +1,91 @@ +// +build windows + +/* +Copyright 2018 The Kubernetes 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 service + +import ( + "os" + + "github.com/golang/glog" + + "golang.org/x/sys/windows" + "golang.org/x/sys/windows/svc" +) + +var ( + service *handler +) + +type handler struct { + tosvc chan bool + fromsvc chan error +} + +// InitService is the entry point for running the daemon as a Windows +// service. It returns an indication of whether it is running as a service; +// and an error. +func InitService(serviceName string) error { + h := &handler{ + tosvc: make(chan bool), + fromsvc: make(chan error), + } + + service = h + var err error + go func() { + err = svc.Run(serviceName, h) + h.fromsvc <- err + }() + + // Wait for the first signal from the service handler. + err = <-h.fromsvc + if err != nil { + return err + } + glog.Infof("Running %s as a Windows service!", serviceName) + 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 initService() + h.fromsvc <- nil + + s <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown | svc.Accepted(windows.SERVICE_ACCEPT_PARAMCHANGE)} + glog.Infof("Service running") +Loop: + for { + select { + case <-h.tosvc: + break Loop + case c := <-r: + switch c.Cmd { + case svc.Cmd(windows.SERVICE_CONTROL_PARAMCHANGE): + s <- c.CurrentStatus + case svc.Interrogate: + s <- c.CurrentStatus + case svc.Stop, svc.Shutdown: + s <- svc.Status{State: svc.Stopped} + // TODO: Stop the kubelet gracefully instead of killing the process + os.Exit(0) + } + } + } + + return false, 0 +} diff --git a/vendor/golang.org/x/sys/windows/BUILD b/vendor/golang.org/x/sys/windows/BUILD index 01bb8b3b61..07d936c10f 100644 --- a/vendor/golang.org/x/sys/windows/BUILD +++ b/vendor/golang.org/x/sys/windows/BUILD @@ -46,7 +46,10 @@ filegroup( filegroup( name = "all-srcs", - srcs = [":package-srcs"], + srcs = [ + ":package-srcs", + "//vendor/golang.org/x/sys/windows/svc:all-srcs", + ], tags = ["automanaged"], visibility = ["//visibility:public"], ) diff --git a/vendor/golang.org/x/sys/windows/svc/BUILD b/vendor/golang.org/x/sys/windows/svc/BUILD new file mode 100644 index 0000000000..00d2b5052a --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/BUILD @@ -0,0 +1,45 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = select({ + "@io_bazel_rules_go//go/platform:windows": [ + "event.go", + "go12.go", + "go13.go", + "security.go", + "service.go", + ], + "//conditions:default": [], + }) + select({ + "@io_bazel_rules_go//go/platform:windows_386": [ + "sys_386.s", + ], + "@io_bazel_rules_go//go/platform:windows_amd64": [ + "sys_amd64.s", + ], + "//conditions:default": [], + }), + importpath = "golang.org/x/sys/windows/svc", + visibility = ["//visibility:public"], + deps = select({ + "@io_bazel_rules_go//go/platform:windows": [ + "//vendor/golang.org/x/sys/windows:go_default_library", + ], + "//conditions:default": [], + }), +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/vendor/golang.org/x/sys/windows/svc/event.go b/vendor/golang.org/x/sys/windows/svc/event.go new file mode 100644 index 0000000000..0508e22881 --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/event.go @@ -0,0 +1,48 @@ +// 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 svc + +import ( + "errors" + + "golang.org/x/sys/windows" +) + +// event represents auto-reset, initially non-signaled Windows event. +// It is used to communicate between go and asm parts of this package. +type event struct { + h windows.Handle +} + +func newEvent() (*event, error) { + h, err := windows.CreateEvent(nil, 0, 0, nil) + if err != nil { + return nil, err + } + return &event{h: h}, nil +} + +func (e *event) Close() error { + return windows.CloseHandle(e.h) +} + +func (e *event) Set() error { + return windows.SetEvent(e.h) +} + +func (e *event) Wait() error { + s, err := windows.WaitForSingleObject(e.h, windows.INFINITE) + switch s { + case windows.WAIT_OBJECT_0: + break + case windows.WAIT_FAILED: + return err + default: + return errors.New("unexpected result from WaitForSingleObject") + } + return nil +} diff --git a/vendor/golang.org/x/sys/windows/svc/go12.c b/vendor/golang.org/x/sys/windows/svc/go12.c new file mode 100644 index 0000000000..6f1be1fa3b --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/go12.c @@ -0,0 +1,24 @@ +// 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 +// +build !go1.3 + +// copied from pkg/runtime +typedef unsigned int uint32; +typedef unsigned long long int uint64; +#ifdef _64BIT +typedef uint64 uintptr; +#else +typedef uint32 uintptr; +#endif + +// from sys_386.s or sys_amd64.s +void ·servicemain(void); + +void +·getServiceMain(uintptr *r) +{ + *r = (uintptr)·servicemain; +} diff --git a/vendor/golang.org/x/sys/windows/svc/go12.go b/vendor/golang.org/x/sys/windows/svc/go12.go new file mode 100644 index 0000000000..cd8b913c99 --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/go12.go @@ -0,0 +1,11 @@ +// Copyright 2014 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 +// +build !go1.3 + +package svc + +// from go12.c +func getServiceMain(r *uintptr) diff --git a/vendor/golang.org/x/sys/windows/svc/go13.go b/vendor/golang.org/x/sys/windows/svc/go13.go new file mode 100644 index 0000000000..9d7f3cec54 --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/go13.go @@ -0,0 +1,31 @@ +// Copyright 2014 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 +// +build go1.3 + +package svc + +import "unsafe" + +const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const + +// Should be a built-in for unsafe.Pointer? +func add(p unsafe.Pointer, x uintptr) unsafe.Pointer { + return unsafe.Pointer(uintptr(p) + x) +} + +// funcPC returns the entry PC of the function f. +// It assumes that f is a func value. Otherwise the behavior is undefined. +func funcPC(f interface{}) uintptr { + return **(**uintptr)(add(unsafe.Pointer(&f), ptrSize)) +} + +// from sys_386.s and sys_amd64.s +func servicectlhandler(ctl uint32) uintptr +func servicemain(argc uint32, argv **uint16) + +func getServiceMain(r *uintptr) { + *r = funcPC(servicemain) +} diff --git a/vendor/golang.org/x/sys/windows/svc/security.go b/vendor/golang.org/x/sys/windows/svc/security.go new file mode 100644 index 0000000000..6fbc9236ed --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/security.go @@ -0,0 +1,62 @@ +// 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 svc + +import ( + "unsafe" + + "golang.org/x/sys/windows" +) + +func allocSid(subAuth0 uint32) (*windows.SID, error) { + var sid *windows.SID + err := windows.AllocateAndInitializeSid(&windows.SECURITY_NT_AUTHORITY, + 1, subAuth0, 0, 0, 0, 0, 0, 0, 0, &sid) + if err != nil { + return nil, err + } + return sid, nil +} + +// IsAnInteractiveSession determines if calling process is running interactively. +// It queries the process token for membership in the Interactive group. +// http://stackoverflow.com/questions/2668851/how-do-i-detect-that-my-application-is-running-as-service-or-in-an-interactive-s +func IsAnInteractiveSession() (bool, error) { + interSid, err := allocSid(windows.SECURITY_INTERACTIVE_RID) + if err != nil { + return false, err + } + defer windows.FreeSid(interSid) + + serviceSid, err := allocSid(windows.SECURITY_SERVICE_RID) + if err != nil { + return false, err + } + defer windows.FreeSid(serviceSid) + + t, err := windows.OpenCurrentProcessToken() + if err != nil { + return false, err + } + defer t.Close() + + gs, err := t.GetTokenGroups() + if err != nil { + return false, err + } + p := unsafe.Pointer(&gs.Groups[0]) + groups := (*[2 << 20]windows.SIDAndAttributes)(p)[:gs.GroupCount] + for _, g := range groups { + if windows.EqualSid(g.Sid, interSid) { + return true, nil + } + if windows.EqualSid(g.Sid, serviceSid) { + return false, nil + } + } + return false, nil +} diff --git a/vendor/golang.org/x/sys/windows/svc/service.go b/vendor/golang.org/x/sys/windows/svc/service.go new file mode 100644 index 0000000000..903cba3f12 --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/service.go @@ -0,0 +1,363 @@ +// 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 svc provides everything required to build Windows service. +// +package svc + +import ( + "errors" + "runtime" + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +// State describes service execution state (Stopped, Running and so on). +type State uint32 + +const ( + Stopped = State(windows.SERVICE_STOPPED) + StartPending = State(windows.SERVICE_START_PENDING) + StopPending = State(windows.SERVICE_STOP_PENDING) + Running = State(windows.SERVICE_RUNNING) + ContinuePending = State(windows.SERVICE_CONTINUE_PENDING) + PausePending = State(windows.SERVICE_PAUSE_PENDING) + Paused = State(windows.SERVICE_PAUSED) +) + +// Cmd represents service state change request. It is sent to a service +// by the service manager, and should be actioned upon by the service. +type Cmd uint32 + +const ( + Stop = Cmd(windows.SERVICE_CONTROL_STOP) + Pause = Cmd(windows.SERVICE_CONTROL_PAUSE) + Continue = Cmd(windows.SERVICE_CONTROL_CONTINUE) + Interrogate = Cmd(windows.SERVICE_CONTROL_INTERROGATE) + Shutdown = Cmd(windows.SERVICE_CONTROL_SHUTDOWN) + ParamChange = Cmd(windows.SERVICE_CONTROL_PARAMCHANGE) + NetBindAdd = Cmd(windows.SERVICE_CONTROL_NETBINDADD) + NetBindRemove = Cmd(windows.SERVICE_CONTROL_NETBINDREMOVE) + NetBindEnable = Cmd(windows.SERVICE_CONTROL_NETBINDENABLE) + NetBindDisable = Cmd(windows.SERVICE_CONTROL_NETBINDDISABLE) + DeviceEvent = Cmd(windows.SERVICE_CONTROL_DEVICEEVENT) + HardwareProfileChange = Cmd(windows.SERVICE_CONTROL_HARDWAREPROFILECHANGE) + PowerEvent = Cmd(windows.SERVICE_CONTROL_POWEREVENT) + SessionChange = Cmd(windows.SERVICE_CONTROL_SESSIONCHANGE) +) + +// Accepted is used to describe commands accepted by the service. +// Note that Interrogate is always accepted. +type Accepted uint32 + +const ( + AcceptStop = Accepted(windows.SERVICE_ACCEPT_STOP) + AcceptShutdown = Accepted(windows.SERVICE_ACCEPT_SHUTDOWN) + AcceptPauseAndContinue = Accepted(windows.SERVICE_ACCEPT_PAUSE_CONTINUE) + AcceptParamChange = Accepted(windows.SERVICE_ACCEPT_PARAMCHANGE) + AcceptNetBindChange = Accepted(windows.SERVICE_ACCEPT_NETBINDCHANGE) + AcceptHardwareProfileChange = Accepted(windows.SERVICE_ACCEPT_HARDWAREPROFILECHANGE) + AcceptPowerEvent = Accepted(windows.SERVICE_ACCEPT_POWEREVENT) + AcceptSessionChange = Accepted(windows.SERVICE_ACCEPT_SESSIONCHANGE) +) + +// Status combines State and Accepted commands to fully describe running service. +type Status struct { + State State + Accepts Accepted + CheckPoint uint32 // used to report progress during a lengthy operation + WaitHint uint32 // estimated time required for a pending operation, in milliseconds +} + +// ChangeRequest is sent to the service Handler to request service status change. +type ChangeRequest struct { + Cmd Cmd + EventType uint32 + EventData uintptr + CurrentStatus Status +} + +// Handler is the interface that must be implemented to build Windows service. +type Handler interface { + + // Execute will be called by the package code at the start of + // the service, and the service will exit once Execute completes. + // Inside Execute you must read service change requests from r and + // act accordingly. You must keep service control manager up to date + // about state of your service by writing into s as required. + // args contains service name followed by argument strings passed + // to the service. + // You can provide service exit code in exitCode return parameter, + // with 0 being "no error". You can also indicate if exit code, + // if any, is service specific or not by using svcSpecificEC + // parameter. + Execute(args []string, r <-chan ChangeRequest, s chan<- Status) (svcSpecificEC bool, exitCode uint32) +} + +var ( + // These are used by asm code. + goWaitsH uintptr + cWaitsH uintptr + ssHandle uintptr + sName *uint16 + sArgc uintptr + sArgv **uint16 + ctlHandlerExProc uintptr + cSetEvent uintptr + cWaitForSingleObject uintptr + cRegisterServiceCtrlHandlerExW uintptr +) + +func init() { + k := syscall.MustLoadDLL("kernel32.dll") + cSetEvent = k.MustFindProc("SetEvent").Addr() + cWaitForSingleObject = k.MustFindProc("WaitForSingleObject").Addr() + a := syscall.MustLoadDLL("advapi32.dll") + cRegisterServiceCtrlHandlerExW = a.MustFindProc("RegisterServiceCtrlHandlerExW").Addr() +} + +// The HandlerEx prototype also has a context pointer but since we don't use +// it at start-up time we don't have to pass it over either. +type ctlEvent struct { + cmd Cmd + eventType uint32 + eventData uintptr + errno uint32 +} + +// service provides access to windows service api. +type service struct { + name string + h windows.Handle + cWaits *event + goWaits *event + c chan ctlEvent + handler Handler +} + +func newService(name string, handler Handler) (*service, error) { + var s service + var err error + s.name = name + s.c = make(chan ctlEvent) + s.handler = handler + s.cWaits, err = newEvent() + if err != nil { + return nil, err + } + s.goWaits, err = newEvent() + if err != nil { + s.cWaits.Close() + return nil, err + } + return &s, nil +} + +func (s *service) close() error { + s.cWaits.Close() + s.goWaits.Close() + return nil +} + +type exitCode struct { + isSvcSpecific bool + errno uint32 +} + +func (s *service) updateStatus(status *Status, ec *exitCode) error { + if s.h == 0 { + return errors.New("updateStatus with no service status handle") + } + var t windows.SERVICE_STATUS + t.ServiceType = windows.SERVICE_WIN32_OWN_PROCESS + t.CurrentState = uint32(status.State) + if status.Accepts&AcceptStop != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_STOP + } + if status.Accepts&AcceptShutdown != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_SHUTDOWN + } + if status.Accepts&AcceptPauseAndContinue != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_PAUSE_CONTINUE + } + if status.Accepts&AcceptParamChange != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_PARAMCHANGE + } + if status.Accepts&AcceptNetBindChange != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_NETBINDCHANGE + } + if status.Accepts&AcceptHardwareProfileChange != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_HARDWAREPROFILECHANGE + } + if status.Accepts&AcceptPowerEvent != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_POWEREVENT + } + if status.Accepts&AcceptSessionChange != 0 { + t.ControlsAccepted |= windows.SERVICE_ACCEPT_SESSIONCHANGE + } + if ec.errno == 0 { + t.Win32ExitCode = windows.NO_ERROR + t.ServiceSpecificExitCode = windows.NO_ERROR + } else if ec.isSvcSpecific { + t.Win32ExitCode = uint32(windows.ERROR_SERVICE_SPECIFIC_ERROR) + t.ServiceSpecificExitCode = ec.errno + } else { + t.Win32ExitCode = ec.errno + t.ServiceSpecificExitCode = windows.NO_ERROR + } + t.CheckPoint = status.CheckPoint + t.WaitHint = status.WaitHint + return windows.SetServiceStatus(s.h, &t) +} + +const ( + sysErrSetServiceStatusFailed = uint32(syscall.APPLICATION_ERROR) + iota + sysErrNewThreadInCallback +) + +func (s *service) run() { + s.goWaits.Wait() + s.h = windows.Handle(ssHandle) + argv := (*[100]*int16)(unsafe.Pointer(sArgv))[:sArgc] + args := make([]string, len(argv)) + for i, a := range argv { + args[i] = syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(a))[:]) + } + + cmdsToHandler := make(chan ChangeRequest) + changesFromHandler := make(chan Status) + exitFromHandler := make(chan exitCode) + + go func() { + ss, errno := s.handler.Execute(args, cmdsToHandler, changesFromHandler) + exitFromHandler <- exitCode{ss, errno} + }() + + status := Status{State: Stopped} + ec := exitCode{isSvcSpecific: true, errno: 0} + var outch chan ChangeRequest + inch := s.c + var cmd Cmd + var evtype uint32 + var evdata uintptr +loop: + for { + select { + case r := <-inch: + if r.errno != 0 { + ec.errno = r.errno + break loop + } + inch = nil + outch = cmdsToHandler + cmd = r.cmd + evtype = r.eventType + evdata = r.eventData + case outch <- ChangeRequest{cmd, evtype, evdata, status}: + inch = s.c + outch = nil + case c := <-changesFromHandler: + err := s.updateStatus(&c, &ec) + if err != nil { + // best suitable error number + ec.errno = sysErrSetServiceStatusFailed + if err2, ok := err.(syscall.Errno); ok { + ec.errno = uint32(err2) + } + break loop + } + status = c + case ec = <-exitFromHandler: + break loop + } + } + + s.updateStatus(&Status{State: Stopped}, &ec) + s.cWaits.Set() +} + +func newCallback(fn interface{}) (cb uintptr, err error) { + defer func() { + r := recover() + if r == nil { + return + } + cb = 0 + switch v := r.(type) { + case string: + err = errors.New(v) + case error: + err = v + default: + err = errors.New("unexpected panic in syscall.NewCallback") + } + }() + return syscall.NewCallback(fn), nil +} + +// BUG(brainman): There is no mechanism to run multiple services +// inside one single executable. Perhaps, it can be overcome by +// using RegisterServiceCtrlHandlerEx Windows api. + +// Run executes service name by calling appropriate handler function. +func Run(name string, handler Handler) error { + runtime.LockOSThread() + + tid := windows.GetCurrentThreadId() + + s, err := newService(name, handler) + if err != nil { + return err + } + + ctlHandler := func(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr { + e := ctlEvent{cmd: Cmd(ctl), eventType: evtype, eventData: evdata} + // We assume that this callback function is running on + // the same thread as Run. Nowhere in MS documentation + // I could find statement to guarantee that. So putting + // check here to verify, otherwise things will go bad + // quickly, if ignored. + i := windows.GetCurrentThreadId() + if i != tid { + e.errno = sysErrNewThreadInCallback + } + s.c <- e + // Always return NO_ERROR (0) for now. + return 0 + } + + var svcmain uintptr + getServiceMain(&svcmain) + t := []windows.SERVICE_TABLE_ENTRY{ + {syscall.StringToUTF16Ptr(s.name), svcmain}, + {nil, 0}, + } + + goWaitsH = uintptr(s.goWaits.h) + cWaitsH = uintptr(s.cWaits.h) + sName = t[0].ServiceName + ctlHandlerExProc, err = newCallback(ctlHandler) + if err != nil { + return err + } + + go s.run() + + err = windows.StartServiceCtrlDispatcher(&t[0]) + if err != nil { + return err + } + return nil +} + +// StatusHandle returns service status handle. It is safe to call this function +// from inside the Handler.Execute because then it is guaranteed to be set. +// This code will have to change once multiple services are possible per process. +func StatusHandle() windows.Handle { + return windows.Handle(ssHandle) +} diff --git a/vendor/golang.org/x/sys/windows/svc/sys_386.s b/vendor/golang.org/x/sys/windows/svc/sys_386.s new file mode 100644 index 0000000000..2c82a9d91d --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/sys_386.s @@ -0,0 +1,68 @@ +// 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 + +// func servicemain(argc uint32, argv **uint16) +TEXT ·servicemain(SB),7,$0 + MOVL argc+0(FP), AX + MOVL AX, ·sArgc(SB) + MOVL argv+4(FP), AX + MOVL AX, ·sArgv(SB) + + PUSHL BP + PUSHL BX + PUSHL SI + PUSHL DI + + SUBL $12, SP + + MOVL ·sName(SB), AX + MOVL AX, (SP) + MOVL $·servicectlhandler(SB), AX + MOVL AX, 4(SP) + MOVL $0, 8(SP) + MOVL ·cRegisterServiceCtrlHandlerExW(SB), AX + MOVL SP, BP + CALL AX + MOVL BP, SP + CMPL AX, $0 + JE exit + MOVL AX, ·ssHandle(SB) + + MOVL ·goWaitsH(SB), AX + MOVL AX, (SP) + MOVL ·cSetEvent(SB), AX + MOVL SP, BP + CALL AX + MOVL BP, SP + + MOVL ·cWaitsH(SB), AX + MOVL AX, (SP) + MOVL $-1, AX + MOVL AX, 4(SP) + MOVL ·cWaitForSingleObject(SB), AX + MOVL SP, BP + CALL AX + MOVL BP, SP + +exit: + ADDL $12, SP + + POPL DI + POPL SI + POPL BX + POPL BP + + MOVL 0(SP), CX + ADDL $12, SP + JMP CX + +// I do not know why, but this seems to be the only way to call +// ctlHandlerProc on Windows 7. + +// func servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr { +TEXT ·servicectlhandler(SB),7,$0 + MOVL ·ctlHandlerExProc(SB), CX + JMP CX diff --git a/vendor/golang.org/x/sys/windows/svc/sys_amd64.s b/vendor/golang.org/x/sys/windows/svc/sys_amd64.s new file mode 100644 index 0000000000..06b425900d --- /dev/null +++ b/vendor/golang.org/x/sys/windows/svc/sys_amd64.s @@ -0,0 +1,42 @@ +// 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 + +// func servicemain(argc uint32, argv **uint16) +TEXT ·servicemain(SB),7,$0 + MOVL CX, ·sArgc(SB) + MOVL DX, ·sArgv(SB) + + SUBQ $32, SP // stack for the first 4 syscall params + + MOVQ ·sName(SB), CX + MOVQ $·servicectlhandler(SB), DX + // BUG(pastarmovj): Figure out a way to pass in context in R8. + MOVQ ·cRegisterServiceCtrlHandlerExW(SB), AX + CALL AX + CMPQ AX, $0 + JE exit + MOVQ AX, ·ssHandle(SB) + + MOVQ ·goWaitsH(SB), CX + MOVQ ·cSetEvent(SB), AX + CALL AX + + MOVQ ·cWaitsH(SB), CX + MOVQ $4294967295, DX + MOVQ ·cWaitForSingleObject(SB), AX + CALL AX + +exit: + ADDQ $32, SP + RET + +// I do not know why, but this seems to be the only way to call +// ctlHandlerProc on Windows 7. + +// func ·servicectlhandler(ctl uint32, evtype uint32, evdata uintptr, context uintptr) uintptr { +TEXT ·servicectlhandler(SB),7,$0 + MOVQ ·ctlHandlerExProc(SB), AX + JMP AX