1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #1472 from anusha-ragunathan/plugins

Make libnetwork understand pluginv2.
This commit is contained in:
Madhu Venugopal 2016-09-27 19:11:55 -07:00 committed by GitHub
commit 4396a733bc
49 changed files with 278 additions and 2271 deletions

View file

@ -1,6 +1,7 @@
{
"ImportPath": "github.com/docker/libnetwork",
"GoVersion": "go1.5",
"GodepVersion": "v74",
"Packages": [
"./..."
],
@ -20,10 +21,12 @@
},
{
"ImportPath": "github.com/Microsoft/go-winio",
"Comment": "v0.3.5-2-gce2922f",
"Rev": "ce2922f643c8fd76b46cadc7f404a06282678b34"
},
{
"ImportPath": "github.com/Microsoft/hcsshim",
"Comment": "v0.4.3",
"Rev": "6611816fb4c1693b429ada0f358102119a0b1466"
},
{
@ -81,113 +84,118 @@
},
{
"ImportPath": "github.com/docker/docker/api/types/filters",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/api/types/network",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/api/types/versions",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/opts",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/pkg/discovery",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/pkg/discovery/kv",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/pkg/homedir",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/pkg/ioutils",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/pkg/locker",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/pkg/longpath",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/pkg/mount",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/pkg/parsers/kernel",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/pkg/plugins",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/pkg/plugins/transport",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/pkg/random",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/pkg/reexec",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/pkg/signal",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/pkg/stringid",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/pkg/symlink",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/pkg/system",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/pkg/term",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/pkg/term/windows",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1316-g426a0af",
"Rev": "426a0af0759798d8e3332b38236ee40df6d14798"
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/docker/plugin/getter",
"Comment": "docs-v1.12.0-rc4-2016-07-15-1792-g1de5043",
"Rev": "1de5043f4e7f4283909eac0f2268e701395d7ad9"
},
{
"ImportPath": "github.com/docker/go-connections/sockets",
@ -210,32 +218,32 @@
},
{
"ImportPath": "github.com/docker/libkv",
"Comment": "v0.1.0-35-g7283ef2",
"Comment": "v0.2.0-1-g7283ef2",
"Rev": "7283ef27ed32fe267388510a91709b307bb9942c"
},
{
"ImportPath": "github.com/docker/libkv/store",
"Comment": "v0.1.0-35-g7283ef2",
"Comment": "v0.2.0-1-g7283ef2",
"Rev": "7283ef27ed32fe267388510a91709b307bb9942c"
},
{
"ImportPath": "github.com/docker/libkv/store/boltdb",
"Comment": "v0.1.0-35-g7283ef2",
"Comment": "v0.2.0-1-g7283ef2",
"Rev": "7283ef27ed32fe267388510a91709b307bb9942c"
},
{
"ImportPath": "github.com/docker/libkv/store/consul",
"Comment": "v0.1.0-35-g7283ef2",
"Comment": "v0.2.0-1-g7283ef2",
"Rev": "7283ef27ed32fe267388510a91709b307bb9942c"
},
{
"ImportPath": "github.com/docker/libkv/store/etcd",
"Comment": "v0.1.0-35-g7283ef2",
"Comment": "v0.2.0-1-g7283ef2",
"Rev": "7283ef27ed32fe267388510a91709b307bb9942c"
},
{
"ImportPath": "github.com/docker/libkv/store/zookeeper",
"Comment": "v0.1.0-35-g7283ef2",
"Comment": "v0.2.0-1-g7283ef2",
"Rev": "7283ef27ed32fe267388510a91709b307bb9942c"
},
{

View file

@ -0,0 +1 @@
*.exe

View file

@ -11,5 +11,12 @@ package.
Please see the LICENSE file for licensing information.
This project has adopted the [Microsoft Open Source Code of
Conduct](https://opensource.microsoft.com/codeofconduct/). For more information
see the [Code of Conduct
FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact
[opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional
questions or comments.
Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe
for another named pipe implementation.

View file

@ -80,3 +80,83 @@ func (w *atomicFileWriter) Close() (retErr error) {
}
return nil
}
// AtomicWriteSet is used to atomically write a set
// of files and ensure they are visible at the same time.
// Must be committed to a new directory.
type AtomicWriteSet struct {
root string
}
// NewAtomicWriteSet creates a new atomic write set to
// atomically create a set of files. The given directory
// is used as the base directory for storing files before
// commit. If no temporary directory is given the system
// default is used.
func NewAtomicWriteSet(tmpDir string) (*AtomicWriteSet, error) {
td, err := ioutil.TempDir(tmpDir, "write-set-")
if err != nil {
return nil, err
}
return &AtomicWriteSet{
root: td,
}, nil
}
// WriteFile writes a file to the set, guaranteeing the file
// has been synced.
func (ws *AtomicWriteSet) WriteFile(filename string, data []byte, perm os.FileMode) error {
f, err := ws.FileWriter(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
if err != nil {
return err
}
n, err := f.Write(data)
if err == nil && n < len(data) {
err = io.ErrShortWrite
}
if err1 := f.Close(); err == nil {
err = err1
}
return err
}
type syncFileCloser struct {
*os.File
}
func (w syncFileCloser) Close() error {
err := w.File.Sync()
if err1 := w.File.Close(); err == nil {
err = err1
}
return err
}
// FileWriter opens a file writer inside the set. The file
// should be synced and closed before calling commit.
func (ws *AtomicWriteSet) FileWriter(name string, flag int, perm os.FileMode) (io.WriteCloser, error) {
f, err := os.OpenFile(filepath.Join(ws.root, name), flag, perm)
if err != nil {
return nil, err
}
return syncFileCloser{f}, nil
}
// Cancel cancels the set and removes all temporary data
// created in the set.
func (ws *AtomicWriteSet) Cancel() error {
return os.RemoveAll(ws.root)
}
// Commit moves all created files to the target directory. The
// target directory must not exist and the parent of the target
// directory must exist.
func (ws *AtomicWriteSet) Commit(target string) error {
return os.Rename(ws.root, target)
}
// String returns the location the set is writing to.
func (ws *AtomicWriteSet) String() string {
return ws.root
}

View file

@ -83,8 +83,8 @@ func (p *Plugin) Client() *Client {
return p.client
}
// IsLegacy returns true for legacy plugins and false otherwise.
func (p *Plugin) IsLegacy() bool {
// IsV1 returns true for V1 plugins and false otherwise.
func (p *Plugin) IsV1() bool {
return true
}

View file

@ -6,7 +6,7 @@ import (
"syscall"
)
// Signals used in api/client (no windows equivalent, use
// Signals used in cli/command (no windows equivalent, use
// invalid signals so they don't get handled)
const (

View file

@ -6,7 +6,7 @@ import (
"syscall"
)
// Signals used in api/client (no windows equivalent, use
// Signals used in cli/command (no windows equivalent, use
// invalid signals so they don't get handled)
const (
SIGCHLD = syscall.Signal(0xff)

View file

@ -95,8 +95,8 @@ func evalSymlinksInScope(path, root string) (string, error) {
// root gets prepended and we Clean again (to remove any trailing slash
// if the first Clean gave us just "/")
cleanP := filepath.Clean(string(filepath.Separator) + b.String() + p)
if cleanP == string(filepath.Separator) {
// never Lstat "/" itself
if isDriveOrRoot(cleanP) {
// never Lstat "/" itself, or drive letters on Windows
b.Reset()
continue
}
@ -113,7 +113,8 @@ func evalSymlinksInScope(path, root string) (string, error) {
return "", err
}
if fi.Mode()&os.ModeSymlink == 0 {
b.WriteString(p + string(filepath.Separator))
b.WriteString(p)
b.WriteRune(filepath.Separator)
continue
}

View file

@ -9,3 +9,7 @@ import (
func evalSymlinks(path string) (string, error) {
return filepath.EvalSymlinks(path)
}
func isDriveOrRoot(p string) bool {
return p == string(filepath.Separator)
}

View file

@ -153,3 +153,17 @@ func walkSymlinks(path string) (string, error) {
}
return filepath.Clean(b.String()), nil
}
func isDriveOrRoot(p string) bool {
if p == string(filepath.Separator) {
return true
}
length := len(p)
if length >= 2 {
if p[length-1] == ':' && (('a' <= p[length-2] && p[length-2] <= 'z') || ('A' <= p[length-2] && p[length-2] <= 'Z')) {
return true
}
}
return false
}

View file

@ -73,6 +73,7 @@ func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
if os.Getenv("ConEmuANSI") == "ON" {
// The ConEmu terminal emulates ANSI on output streams well.
emulateStdin = true
emulateStdout = false
emulateStderr = false
}

View file

@ -0,0 +1,26 @@
package getter
import "github.com/docker/docker/pkg/plugins"
const (
// LOOKUP doesn't update RefCount
LOOKUP = 0
// CREATE increments RefCount
CREATE = 1
// REMOVE decrements RefCount
REMOVE = -1
)
// CompatPlugin is a abstraction to handle both v2(new) and v1(legacy) plugins.
type CompatPlugin interface {
Client() *plugins.Client
Name() string
IsV1() bool
}
// PluginGetter is the interface implemented by Store
type PluginGetter interface {
Get(name, capability string, mode int) (CompatPlugin, error)
GetAllByCap(capability string) ([]CompatPlugin, error)
Handle(capability string, callback func(string, *plugins.Client))
}

View file

@ -1,178 +0,0 @@
// 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)
)
// 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
}
// 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) {
ki, err := k.Stat()
if err != nil {
return nil, err
}
names := make([]string, 0, ki.SubKeyCount)
buf := make([]uint16, ki.MaxSubKeyLen+1) // 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
}

View file

@ -1,33 +0,0 @@
// 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"
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall.go
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 expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) = kernel32.ExpandEnvironmentStringsW

View file

@ -1,384 +0,0 @@
// 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
}

View file

@ -1,82 +0,0 @@
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
package registry
import "unsafe"
import "syscall"
var _ unsafe.Pointer
var (
modadvapi32 = syscall.NewLazyDLL("advapi32.dll")
modkernel32 = syscall.NewLazyDLL("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")
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 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 = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}

View file

@ -1,56 +0,0 @@
// 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)
}

View file

@ -1,45 +0,0 @@
// 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{svc.Stop, status}
case status = <-changes:
}
}
}()
_, errno := handler.Execute([]string{name}, cmds, changes)
if errno != 0 {
return syscall.Errno(errno)
}
return nil
}

View file

@ -1,48 +0,0 @@
// 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
}

View file

@ -1,80 +0,0 @@
// 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)
}

View file

@ -1,70 +0,0 @@
// 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)
}

View file

@ -1,22 +0,0 @@
// 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 main
import (
"syscall"
)
// BUG(brainman): MessageBeep Windows api is broken on Windows 7,
// so this example does not beep when runs as service on Windows 7.
var (
beepFunc = syscall.MustLoadDLL("user32.dll").MustFindProc("MessageBeep")
)
func beep() {
beepFunc.Call(0xffffffff)
}

View file

@ -1,92 +0,0 @@
// 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 main
import (
"fmt"
"os"
"path/filepath"
"golang.org/x/sys/windows/svc/eventlog"
"golang.org/x/sys/windows/svc/mgr"
)
func exePath() (string, error) {
prog := os.Args[0]
p, err := filepath.Abs(prog)
if err != nil {
return "", err
}
fi, err := os.Stat(p)
if err == nil {
if !fi.Mode().IsDir() {
return p, nil
}
err = fmt.Errorf("%s is directory", p)
}
if filepath.Ext(p) == "" {
p += ".exe"
fi, err := os.Stat(p)
if err == nil {
if !fi.Mode().IsDir() {
return p, nil
}
err = fmt.Errorf("%s is directory", p)
}
}
return "", err
}
func installService(name, desc string) error {
exepath, err := exePath()
if err != nil {
return err
}
m, err := mgr.Connect()
if err != nil {
return err
}
defer m.Disconnect()
s, err := m.OpenService(name)
if err == nil {
s.Close()
return fmt.Errorf("service %s already exists", name)
}
s, err = m.CreateService(name, exepath, mgr.Config{DisplayName: desc}, "is", "auto-started")
if err != nil {
return err
}
defer s.Close()
err = eventlog.InstallAsEventCreate(name, eventlog.Error|eventlog.Warning|eventlog.Info)
if err != nil {
s.Delete()
return fmt.Errorf("SetupEventLogSource() failed: %s", err)
}
return nil
}
func removeService(name string) error {
m, err := mgr.Connect()
if err != nil {
return err
}
defer m.Disconnect()
s, err := m.OpenService(name)
if err != nil {
return fmt.Errorf("service %s is not installed", name)
}
defer s.Close()
err = s.Delete()
if err != nil {
return err
}
err = eventlog.Remove(name)
if err != nil {
return fmt.Errorf("RemoveEventLogSource() failed: %s", err)
}
return nil
}

View file

@ -1,76 +0,0 @@
// 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
// Example service program that beeps.
//
// The program demonstrates how to create Windows service and
// install / remove it on a computer. It also shows how to
// stop / start / pause / continue any service, and how to
// write to event log. It also shows how to use debug
// facilities available in debug package.
//
package main
import (
"fmt"
"log"
"os"
"strings"
"golang.org/x/sys/windows/svc"
)
func usage(errmsg string) {
fmt.Fprintf(os.Stderr,
"%s\n\n"+
"usage: %s <command>\n"+
" where <command> is one of\n"+
" install, remove, debug, start, stop, pause or continue.\n",
errmsg, os.Args[0])
os.Exit(2)
}
func main() {
const svcName = "myservice"
isIntSess, err := svc.IsAnInteractiveSession()
if err != nil {
log.Fatalf("failed to determine if we are running in an interactive session: %v", err)
}
if !isIntSess {
runService(svcName, false)
return
}
if len(os.Args) < 2 {
usage("no command specified")
}
cmd := strings.ToLower(os.Args[1])
switch cmd {
case "debug":
runService(svcName, true)
return
case "install":
err = installService(svcName, "my service")
case "remove":
err = removeService(svcName)
case "start":
err = startService(svcName)
case "stop":
err = controlService(svcName, svc.Stop, svc.Stopped)
case "pause":
err = controlService(svcName, svc.Pause, svc.Paused)
case "continue":
err = controlService(svcName, svc.Continue, svc.Running)
default:
usage(fmt.Sprintf("invalid command %s", cmd))
}
if err != nil {
log.Fatalf("failed to %s %s: %v", cmd, svcName, err)
}
return
}

View file

@ -1,62 +0,0 @@
// 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 main
import (
"fmt"
"time"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/mgr"
)
func startService(name string) error {
m, err := mgr.Connect()
if err != nil {
return err
}
defer m.Disconnect()
s, err := m.OpenService(name)
if err != nil {
return fmt.Errorf("could not access service: %v", err)
}
defer s.Close()
err = s.Start("is", "manual-started")
if err != nil {
return fmt.Errorf("could not start service: %v", err)
}
return nil
}
func controlService(name string, c svc.Cmd, to svc.State) error {
m, err := mgr.Connect()
if err != nil {
return err
}
defer m.Disconnect()
s, err := m.OpenService(name)
if err != nil {
return fmt.Errorf("could not access service: %v", err)
}
defer s.Close()
status, err := s.Control(c)
if err != nil {
return fmt.Errorf("could not send control=%d: %v", c, err)
}
timeout := time.Now().Add(10 * time.Second)
for status.State != to {
if timeout.Before(time.Now()) {
return fmt.Errorf("timeout waiting for service to go to state=%d", to)
}
time.Sleep(300 * time.Millisecond)
status, err = s.Query()
if err != nil {
return fmt.Errorf("could not retrieve service status: %v", err)
}
}
return nil
}

View file

@ -1,82 +0,0 @@
// 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 main
import (
"fmt"
"time"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/debug"
"golang.org/x/sys/windows/svc/eventlog"
)
var elog debug.Log
type myservice struct{}
func (m *myservice) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue
changes <- svc.Status{State: svc.StartPending}
fasttick := time.Tick(500 * time.Millisecond)
slowtick := time.Tick(2 * time.Second)
tick := fasttick
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
loop:
for {
select {
case <-tick:
beep()
elog.Info(1, "beep")
case c := <-r:
switch c.Cmd {
case svc.Interrogate:
changes <- c.CurrentStatus
// Testing deadlock from https://code.google.com/p/winsvc/issues/detail?id=4
time.Sleep(100 * time.Millisecond)
changes <- c.CurrentStatus
case svc.Stop, svc.Shutdown:
break loop
case svc.Pause:
changes <- svc.Status{State: svc.Paused, Accepts: cmdsAccepted}
tick = slowtick
case svc.Continue:
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
tick = fasttick
default:
elog.Error(1, fmt.Sprintf("unexpected control request #%d", c))
}
}
}
changes <- svc.Status{State: svc.StopPending}
return
}
func runService(name string, isDebug bool) {
var err error
if isDebug {
elog = debug.New(name)
} else {
elog, err = eventlog.Open(name)
if err != nil {
return
}
}
defer elog.Close()
elog.Info(1, fmt.Sprintf("starting %s service", name))
run := svc.Run
if isDebug {
run = debug.Run
}
err = run(name, &myservice{})
if err != nil {
elog.Error(1, fmt.Sprintf("%s service failed: %v", name, err))
return
}
elog.Info(1, fmt.Sprintf("%s service stopped", name))
}

View file

@ -1,24 +0,0 @@
// 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;
}

View file

@ -1,11 +0,0 @@
// 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)

View file

@ -1,31 +0,0 @@
// 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)
}

View file

@ -1,139 +0,0 @@
// 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
}
}
var p2 *windows.SERVICE_DESCRIPTION
n = uint32(1024)
for {
b := make([]byte, n)
p2 = (*windows.SERVICE_DESCRIPTION)(unsafe.Pointer(&b[0]))
err := windows.QueryServiceConfig2(s.Handle,
windows.SERVICE_CONFIG_DESCRIPTION, &b[0], 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
}
}
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{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)
}

View file

@ -1,119 +0,0 @@
// 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"
"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.
// If service StartType is set to StartAutomatic,
// args will be passed to svc.Handle.Execute.
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
}

View file

@ -1,74 +0,0 @@
// 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.
// TODO(brainman): Use EnumServicesStatus to enumerate services in the specified service control manager database.
// 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
}

View file

@ -1,62 +0,0 @@
// 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
}

View file

@ -1,316 +0,0 @@
// 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)
)
// 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)
)
// 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
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
ctlHandlerProc uintptr
cSetEvent uintptr
cWaitForSingleObject uintptr
cRegisterServiceCtrlHandlerW uintptr
)
func init() {
k := syscall.MustLoadDLL("kernel32.dll")
cSetEvent = k.MustFindProc("SetEvent").Addr()
cWaitForSingleObject = k.MustFindProc("WaitForSingleObject").Addr()
a := syscall.MustLoadDLL("advapi32.dll")
cRegisterServiceCtrlHandlerW = a.MustFindProc("RegisterServiceCtrlHandlerW").Addr()
}
type ctlEvent struct {
cmd Cmd
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 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
loop:
for {
select {
case r := <-inch:
if r.errno != 0 {
ec.errno = r.errno
break loop
}
inch = nil
outch = cmdsToHandler
cmd = r.cmd
case outch <- ChangeRequest{cmd, 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) uintptr {
e := ctlEvent{cmd: Cmd(ctl)}
// 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
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
ctlHandlerProc, err = newCallback(ctlHandler)
if err != nil {
return err
}
go s.run()
err = windows.StartServiceCtrlDispatcher(&t[0])
if err != nil {
return err
}
return nil
}

View file

@ -1,67 +0,0 @@
// 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 ·cRegisterServiceCtrlHandlerW(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) uintptr
TEXT ·servicectlhandler(SB),7,$0
MOVL ·ctlHandlerProc(SB), CX
JMP CX

View file

@ -1,41 +0,0 @@
// 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
MOVQ ·cRegisterServiceCtrlHandlerW(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) uintptr
TEXT ·servicectlhandler(SB),7,$0
MOVQ ·ctlHandlerProc(SB), AX
JMP AX

View file

@ -19,7 +19,7 @@
// These calls return err == nil to indicate success; otherwise
// err represents an operating system error describing the failure and
// holds a value of type syscall.Errno.
package windows // import "golang.org/x/sys/windows"
package windows
import (
"syscall"

View file

@ -7,6 +7,7 @@ import (
"os/signal"
"github.com/docker/docker/pkg/reexec"
"github.com/docker/docker/plugin/getter"
"github.com/docker/libnetwork/driverapi"
"github.com/docker/libnetwork/drivers/overlay"
"github.com/docker/libnetwork/netlabel"
@ -24,6 +25,10 @@ type endpoint struct {
name string
}
func (r *router) GetPluginGetter() getter.PluginGetter {
return nil
}
func (r *router) RegisterDriver(name string, driver driverapi.Driver, c driverapi.Capability) error {
r.d = driver
return nil

View file

@ -6,6 +6,7 @@ import (
"github.com/BurntSushi/toml"
log "github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/discovery"
"github.com/docker/docker/plugin/getter"
"github.com/docker/go-connections/tlsconfig"
"github.com/docker/libkv/store"
"github.com/docker/libnetwork/cluster"
@ -20,6 +21,7 @@ type Config struct {
Cluster ClusterCfg
Scopes map[string]*datastore.ScopeCfg
ActiveSandboxes map[string]interface{}
PluginGetter getter.PluginGetter
}
// DaemonCfg represents libnetwork core configuration
@ -205,6 +207,13 @@ func OptionExecRoot(execRoot string) Option {
}
}
// OptionPluginGetter returns a plugingetter for remote drivers.
func OptionPluginGetter(pg getter.PluginGetter) Option {
return func(c *Config) {
c.PluginGetter = pg
}
}
// ProcessOptions processes options and stores it in config
func (c *Config) ProcessOptions(options ...Option) {
for _, opt := range options {

View file

@ -55,6 +55,7 @@ import (
"github.com/docker/docker/pkg/locker"
"github.com/docker/docker/pkg/plugins"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/plugin/getter"
"github.com/docker/libnetwork/cluster"
"github.com/docker/libnetwork/config"
"github.com/docker/libnetwork/datastore"
@ -178,7 +179,7 @@ func New(cfgOptions ...config.Option) (NetworkController, error) {
return nil, err
}
drvRegistry, err := drvregistry.New(c.getStore(datastore.LocalScope), c.getStore(datastore.GlobalScope), c.RegisterDriver, nil)
drvRegistry, err := drvregistry.New(c.getStore(datastore.LocalScope), c.getStore(datastore.GlobalScope), c.RegisterDriver, nil, c.cfg.PluginGetter)
if err != nil {
return nil, err
}
@ -601,6 +602,10 @@ func (c *controller) isDistributedControl() bool {
return !c.isManager() && !c.isAgent()
}
func (c *controller) GetPluginGetter() getter.PluginGetter {
return c.drvRegistry.GetPluginGetter()
}
func (c *controller) RegisterDriver(networkType string, driver driverapi.Driver, capability driverapi.Capability) error {
c.Lock()
hd := c.discovery
@ -1074,7 +1079,7 @@ func (c *controller) loadDriver(networkType string) error {
}
func (c *controller) loadIPAMDriver(name string) error {
if _, err := plugins.Get(name, ipamapi.PluginEndpointType); err != nil {
if _, err := c.GetPluginGetter().Get(name, ipamapi.PluginEndpointType, getter.LOOKUP); err != nil {
if err == plugins.ErrNotFound {
return types.NotFoundErrorf(err.Error())
}

View file

@ -3,6 +3,7 @@ package driverapi
import (
"net"
"github.com/docker/docker/plugin/getter"
"github.com/docker/libnetwork/discoverapi"
)
@ -139,6 +140,8 @@ type JoinInfo interface {
// DriverCallback provides a Callback interface for Drivers into LibNetwork
type DriverCallback interface {
// GetPluginGetter returns the pluginv2 getter.
GetPluginGetter() getter.PluginGetter
// RegisterDriver provides a way for Remote drivers to dynamically register new NetworkType and associate with a driver instance
RegisterDriver(name string, driver Driver, capability Capability) error
}

View file

@ -3,6 +3,7 @@ package ipvlan
import (
"testing"
"github.com/docker/docker/plugin/getter"
"github.com/docker/libnetwork/driverapi"
_ "github.com/docker/libnetwork/testutils"
)
@ -14,6 +15,10 @@ type driverTester struct {
d *driver
}
func (dt *driverTester) GetPluginGetter() getter.PluginGetter {
return nil
}
func (dt *driverTester) RegisterDriver(name string, drv driverapi.Driver,
cap driverapi.Capability) error {
if name != testNetworkType {

View file

@ -3,6 +3,7 @@ package macvlan
import (
"testing"
"github.com/docker/docker/plugin/getter"
"github.com/docker/libnetwork/driverapi"
_ "github.com/docker/libnetwork/testutils"
)
@ -14,6 +15,10 @@ type driverTester struct {
d *driver
}
func (dt *driverTester) GetPluginGetter() getter.PluginGetter {
return nil
}
func (dt *driverTester) RegisterDriver(name string, drv driverapi.Driver,
cap driverapi.Capability) error {
if name != testNetworkType {

View file

@ -5,6 +5,7 @@ import (
"testing"
"time"
"github.com/docker/docker/plugin/getter"
"github.com/docker/libkv/store/consul"
"github.com/docker/libnetwork/datastore"
"github.com/docker/libnetwork/discoverapi"
@ -67,6 +68,10 @@ func cleanupDriver(t *testing.T, dt *driverTester) {
}
}
func (dt *driverTester) GetPluginGetter() getter.PluginGetter {
return nil
}
func (dt *driverTester) RegisterDriver(name string, drv driverapi.Driver,
cap driverapi.Capability) error {
if name != testNetworkType {

View file

@ -29,7 +29,12 @@ func newDriver(name string, client *plugins.Client) driverapi.Driver {
// Init makes sure a remote driver is registered when a network driver
// plugin is activated.
func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
plugins.Handle(driverapi.NetworkPluginEndpointType, func(name string, client *plugins.Client) {
// Unit test code is unaware of a true PluginStore. So we fall back to v1 plugins.
handleFunc := plugins.Handle
if pg := dc.GetPluginGetter(); pg != nil {
handleFunc = pg.Handle
}
handleFunc(driverapi.NetworkPluginEndpointType, func(name string, client *plugins.Client) {
// negotiate driver capability with client
d := newDriver(name, client)
c, err := d.(*driver).getCapabilities()

View file

@ -5,6 +5,7 @@ import (
"strings"
"sync"
"github.com/docker/docker/plugin/getter"
"github.com/docker/libnetwork/driverapi"
"github.com/docker/libnetwork/ipamapi"
"github.com/docker/libnetwork/types"
@ -32,6 +33,7 @@ type DrvRegistry struct {
ipamDrivers ipamTable
dfn DriverNotifyFunc
ifn IPAMNotifyFunc
pluginGetter getter.PluginGetter
}
// Functors definition
@ -52,12 +54,13 @@ type IPAMNotifyFunc func(name string, driver ipamapi.Ipam, cap *ipamapi.Capabili
type DriverNotifyFunc func(name string, driver driverapi.Driver, capability driverapi.Capability) error
// New retruns a new driver registry handle.
func New(lDs, gDs interface{}, dfn DriverNotifyFunc, ifn IPAMNotifyFunc) (*DrvRegistry, error) {
func New(lDs, gDs interface{}, dfn DriverNotifyFunc, ifn IPAMNotifyFunc, pg getter.PluginGetter) (*DrvRegistry, error) {
r := &DrvRegistry{
drivers: make(driverTable),
ipamDrivers: make(ipamTable),
dfn: dfn,
ifn: ifn,
pluginGetter: pg,
}
return r, nil
@ -149,6 +152,11 @@ func (r *DrvRegistry) IPAMDefaultAddressSpaces(name string) (string, string, err
return i.defaultLocalAddressSpace, i.defaultGlobalAddressSpace, nil
}
// GetPluginGetter returns the plugingetter
func (r *DrvRegistry) GetPluginGetter() getter.PluginGetter {
return r.pluginGetter
}
// RegisterDriver registers the network driver when it gets discovered.
func (r *DrvRegistry) RegisterDriver(ntype string, driver driverapi.Driver, capability driverapi.Capability) error {
if strings.TrimSpace(ntype) == "" {

View file

@ -88,7 +88,7 @@ func (m *mockDriver) EventNotify(etype driverapi.EventType, nid, tableName, key
}
func getNew(t *testing.T) *DrvRegistry {
reg, err := New(nil, nil, nil, nil)
reg, err := New(nil, nil, nil, nil, nil)
if err != nil {
t.Fatal(err)
}

View file

@ -4,6 +4,7 @@ package ipamapi
import (
"net"
"github.com/docker/docker/plugin/getter"
"github.com/docker/libnetwork/discoverapi"
"github.com/docker/libnetwork/types"
)
@ -25,6 +26,8 @@ const (
// Callback provides a Callback interface for registering an IPAM instance into LibNetwork
type Callback interface {
// GetPluginGetter returns the pluginv2 getter.
GetPluginGetter() getter.PluginGetter
// RegisterIpamDriver provides a way for Remote drivers to dynamically register with libnetwork
RegisterIpamDriver(name string, driver Ipam) error
// RegisterIpamDriverWithCapabilities provides a way for Remote drivers to dynamically register with libnetwork and specify capabilities

View file

@ -30,7 +30,13 @@ func newAllocator(name string, client *plugins.Client) ipamapi.Ipam {
// Init registers a remote ipam when its plugin is activated
func Init(cb ipamapi.Callback, l, g interface{}) error {
plugins.Handle(ipamapi.PluginEndpointType, func(name string, client *plugins.Client) {
// Unit test code is unaware of a true PluginStore. So we fall back to v1 plugins.
handleFunc := plugins.Handle
if pg := cb.GetPluginGetter(); pg != nil {
handleFunc = pg.Handle
}
handleFunc(ipamapi.PluginEndpointType, func(name string, client *plugins.Client) {
a := newAllocator(name, client)
if cps, err := a.(*allocator).getCapabilities(); err == nil {
if err := cb.RegisterIpamDriverWithCapabilities(name, a, cps); err != nil {